1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "io.h"
9 #include "ureg.h"
10
11 #include "../port/sd.h"
12
13 static int
scsitest(SDreq * r)14 scsitest(SDreq* r)
15 {
16 r->write = 0;
17 memset(r->cmd, 0, sizeof(r->cmd));
18 r->cmd[1] = r->lun<<5;
19 r->clen = 6;
20 r->data = nil;
21 r->dlen = 0;
22 r->flags = 0;
23
24 r->status = ~0;
25
26 return r->unit->dev->ifc->rio(r);
27 }
28
29 int
scsiverify(SDunit * unit)30 scsiverify(SDunit* unit)
31 {
32 SDreq *r;
33 int i, status;
34 uchar *inquiry;
35
36 if((r = malloc(sizeof(SDreq))) == nil)
37 return 0;
38 if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
39 free(r);
40 return 0;
41 }
42 r->unit = unit;
43 r->lun = 0; /* ??? */
44
45 memset(unit->inquiry, 0, sizeof(unit->inquiry));
46 r->write = 0;
47 r->cmd[0] = 0x12;
48 r->cmd[1] = r->lun<<5;
49 r->cmd[4] = sizeof(unit->inquiry)-1;
50 r->clen = 6;
51 r->data = inquiry;
52 r->dlen = sizeof(unit->inquiry)-1;
53 r->flags = 0;
54
55 r->status = ~0;
56 if(unit->dev->ifc->rio(r) != SDok){
57 free(r);
58 return 0;
59 }
60 memmove(unit->inquiry, inquiry, r->dlen);
61 free(inquiry);
62
63 SET(status);
64 for(i = 0; i < 3; i++){
65 while((status = scsitest(r)) == SDbusy)
66 ;
67 if(status == SDok || status != SDcheck)
68 break;
69 if(!(r->flags & SDvalidsense))
70 break;
71 if((r->sense[2] & 0x0F) != 0x02)
72 continue;
73
74 /*
75 * Unit is 'not ready'.
76 * If it is in the process of becoming ready or needs
77 * an initialising command, set status so it will be spun-up
78 * below.
79 * If there's no medium, that's OK too, but don't
80 * try to spin it up.
81 */
82 if(r->sense[12] == 0x04){
83 if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
84 status = SDok;
85 break;
86 }
87 }
88 if(r->sense[12] == 0x3A)
89 break;
90 }
91
92 if(status == SDok){
93 /*
94 * Try to ensure a direct-access device is spinning.
95 * Don't wait for completion, ignore the result.
96 */
97 if((unit->inquiry[0] & SDinq0periphtype) == SDperdisk){
98 memset(r->cmd, 0, sizeof(r->cmd));
99 r->write = 0;
100 r->cmd[0] = 0x1B;
101 r->cmd[1] = (r->lun<<5)|0x01;
102 r->cmd[4] = 1;
103 r->clen = 6;
104 r->data = nil;
105 r->dlen = 0;
106 r->flags = 0;
107
108 r->status = ~0;
109 unit->dev->ifc->rio(r);
110 }
111 }
112 free(r);
113
114 if(status == SDok || status == SDcheck)
115 return 1;
116 return 0;
117 }
118
119 static int
scsirio(SDreq * r)120 scsirio(SDreq* r)
121 {
122 /*
123 * Perform an I/O request, returning
124 * -1 failure
125 * 0 ok
126 * 1 no medium present
127 * 2 retry
128 * The contents of r may be altered so the
129 * caller should re-initialise if necesary.
130 */
131 r->status = ~0;
132 switch(r->unit->dev->ifc->rio(r)){
133 default:
134 break;
135 case SDcheck:
136 if(!(r->flags & SDvalidsense))
137 break;
138 switch(r->sense[2] & 0x0F){
139 case 0x00: /* no sense */
140 case 0x01: /* recovered error */
141 return 2;
142 case 0x06: /* check condition */
143 /*
144 * 0x28 - not ready to ready transition,
145 * medium may have changed.
146 * 0x29 - power on or some type of reset.
147 */
148 if(r->sense[12] == 0x28 && r->sense[13] == 0)
149 return 2;
150 if(r->sense[12] == 0x29)
151 return 2;
152 break;
153 case 0x02: /* not ready */
154 /*
155 * If no medium present, bail out.
156 * If unit is becoming ready, rather than not
157 * not ready, wait a little then poke it again. */
158 if(r->sense[12] == 0x3A)
159 break;
160 if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
161 break;
162
163 while(waserror())
164 ;
165 tsleep(&up->sleep, return0, 0, 500);
166 poperror();
167 scsitest(r);
168 return 2;
169 default:
170 break;
171 }
172 break;
173 case SDok:
174 return 0;
175 }
176 return -1;
177 }
178
179 int
scsionline(SDunit * unit)180 scsionline(SDunit* unit)
181 {
182 SDreq *r;
183 uchar *p;
184 int ok, retries;
185
186 if((r = malloc(sizeof(SDreq))) == nil)
187 return 0;
188 if((p = sdmalloc(8)) == nil){
189 free(r);
190 return 0;
191 }
192
193 ok = 0;
194
195 r->unit = unit;
196 r->lun = 0; /* ??? */
197 for(retries = 0; retries < 10; retries++){
198 /*
199 * Read-capacity is mandatory for DA, WORM, CD-ROM and
200 * MO. It may return 'not ready' if type DA is not
201 * spun up, type MO or type CD-ROM are not loaded or just
202 * plain slow getting their act together after a reset.
203 */
204 r->write = 0;
205 memset(r->cmd, 0, sizeof(r->cmd));
206 r->cmd[0] = ScmdRcapacity;
207 r->cmd[1] = r->lun<<5;
208 r->clen = 10;
209 r->data = p;
210 r->dlen = 8;
211 r->flags = 0;
212
213 r->status = ~0;
214 switch(scsirio(r)){
215 default:
216 break;
217 case 0:
218 unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
219 unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
220
221 /*
222 * Some ATAPI CD readers lie about the block size.
223 * Since we don't read audio via this interface
224 * it's okay to always fudge this.
225 */
226 if(unit->secsize == 2352)
227 unit->secsize = 2048;
228 /*
229 * Devices with removable media may return 0 sectors
230 * when they have empty media (e.g. sata dvd writers);
231 * if so, keep the count zero.
232 *
233 * Read-capacity returns the LBA of the last sector,
234 * therefore the number of sectors must be incremented.
235 */
236 if(unit->sectors != 0)
237 unit->sectors++;
238 ok = 1;
239 break;
240 case 1:
241 ok = 1;
242 break;
243 case 2:
244 continue;
245 }
246 break;
247 }
248 free(p);
249 free(r);
250
251 if(ok)
252 return ok+retries;
253 else
254 return 0;
255 }
256
257 int
scsiexec(SDunit * unit,int write,uchar * cmd,int clen,void * data,int * dlen)258 scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
259 {
260 SDreq *r;
261 int status;
262
263 if((r = malloc(sizeof(SDreq))) == nil)
264 return SDmalloc;
265 r->unit = unit;
266 r->lun = cmd[1]>>5; /* ??? */
267 r->write = write;
268 memmove(r->cmd, cmd, clen);
269 r->clen = clen;
270 r->data = data;
271 if(dlen)
272 r->dlen = *dlen;
273 r->flags = 0;
274
275 r->status = ~0;
276
277 /*
278 * Call the device-specific I/O routine.
279 * There should be no calls to 'error()' below this
280 * which percolate back up.
281 */
282 switch(status = unit->dev->ifc->rio(r)){
283 case SDok:
284 if(dlen)
285 *dlen = r->rlen;
286 /*FALLTHROUGH*/
287 case SDcheck:
288 /*FALLTHROUGH*/
289 default:
290 /*
291 * It's more complicated than this. There are conditions
292 * which are 'ok' but for which the returned status code
293 * is not 'SDok'.
294 * Also, not all conditions require a reqsense, might
295 * need to do a reqsense here and make it available to the
296 * caller somehow.
297 *
298 * Mañana.
299 */
300 break;
301 }
302 sdfree(r);
303
304 return status;
305 }
306
307 /* extract lba and count from scsi command block cmd of clen bytes */
308 void
scsilbacount(uchar * cmd,int clen,uvlong * lbap,ulong * countp)309 scsilbacount(uchar *cmd, int clen, uvlong *lbap, ulong *countp)
310 {
311 if (clen == 16) {
312 *lbap = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32 |
313 cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
314 *countp = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
315 } else if (clen == 10) {
316 *lbap = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
317 *countp = cmd[7]<<8 | cmd[8];
318 } else
319 panic("scsilbacount: command len %d unexpected", clen);
320 }
321
322 static void
scsifmt10(SDreq * r,int write,int lun,ulong nb,uvlong bno)323 scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno)
324 {
325 uchar *c;
326
327 c = r->cmd;
328 if(write == 0)
329 c[0] = ScmdExtread;
330 else
331 c[0] = ScmdExtwrite;
332 c[1] = lun<<5;
333 c[2] = bno>>24;
334 c[3] = bno>>16;
335 c[4] = bno>>8;
336 c[5] = bno;
337 c[6] = 0;
338 c[7] = nb>>8;
339 c[8] = nb;
340 c[9] = 0;
341
342 r->clen = 10;
343 }
344
345 static void
scsifmt16(SDreq * r,int write,int lun,ulong nb,uvlong bno)346 scsifmt16(SDreq *r, int write, int lun, ulong nb, uvlong bno)
347 {
348 uchar *c;
349
350 c = r->cmd;
351 if(write == 0)
352 c[0] = ScmdRead16;
353 else
354 c[0] = ScmdWrite16;
355 c[1] = lun<<5; /* so wrong */
356 c[2] = bno>>56;
357 c[3] = bno>>48;
358 c[4] = bno>>40;
359 c[5] = bno>>32;
360 c[6] = bno>>24;
361 c[7] = bno>>16;
362 c[8] = bno>>8;
363 c[9] = bno;
364 c[10] = nb>>24;
365 c[11] = nb>>16;
366 c[12] = nb>>8;
367 c[13] = nb;
368 c[14] = 0;
369 c[15] = 0;
370
371 r->clen = 16;
372 }
373
374 long
scsibio(SDunit * unit,int lun,int write,void * data,long nb,uvlong bno)375 scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
376 {
377 SDreq *r;
378 long rlen;
379
380 if((r = malloc(sizeof(SDreq))) == nil)
381 error(Enomem);
382 r->unit = unit;
383 r->lun = lun;
384 again:
385 r->write = write;
386 if(bno >= (1ULL<<32))
387 scsifmt16(r, write, lun, nb, bno);
388 else
389 scsifmt10(r, write, lun, nb, bno);
390 r->data = data;
391 r->dlen = nb*unit->secsize;
392 r->flags = 0;
393
394 r->status = ~0;
395 switch(scsirio(r)){
396 default:
397 rlen = -1;
398 break;
399 case 0:
400 rlen = r->rlen;
401 break;
402 case 2:
403 rlen = -1;
404 if(!(r->flags & SDvalidsense))
405 break;
406 switch(r->sense[2] & 0x0F){
407 default:
408 break;
409 case 0x01: /* recovered error */
410 print("%s: recovered error at sector %llud\n",
411 unit->name, bno);
412 rlen = r->rlen;
413 break;
414 case 0x06: /* check condition */
415 /*
416 * Check for a removeable media change.
417 * If so, mark it by zapping the geometry info
418 * to force an online request.
419 */
420 if(r->sense[12] != 0x28 || r->sense[13] != 0)
421 break;
422 if(unit->inquiry[1] & SDinq1removable)
423 unit->sectors = 0;
424 break;
425 case 0x02: /* not ready */
426 /*
427 * If unit is becoming ready,
428 * rather than not not ready, try again.
429 */
430 if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
431 goto again;
432 break;
433 }
434 break;
435 }
436 free(r);
437
438 return rlen;
439 }
440
441