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