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