sdscsi.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       sdscsi.c (7937B)
       ---
            1 #include "u.h"
            2 #include "lib.h"
            3 #include "mem.h"
            4 #include "dat.h"
            5 #include "fns.h"
            6 #include "io.h"
            7 #include "ureg.h"
            8 #include "error.h"
            9 
           10 #include "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] = sizeof(unit->inquiry)-1;
           49         r->clen = 6;
           50         r->data = inquiry;
           51         r->dlen = sizeof(unit->inquiry)-1;
           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         status = 0;
           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                         if(r->sense[12] == 0x3A)
          158                                 break;
          159                         if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
          160                                 break;
          161 
          162                         while(waserror())
          163                                 ;
          164                         tsleep(&up->sleep, return0, 0, 500);
          165                         poperror();
          166                         scsitest(r);
          167                         return 2;
          168                 default:
          169                         break;
          170                 }
          171                 break;
          172         case SDok:
          173                 return 0;
          174         }
          175         return -1;
          176 }
          177 
          178 int
          179 scsionline(SDunit* unit)
          180 {
          181         SDreq *r;
          182         uchar *p;
          183         int ok, retries;
          184 
          185         if((r = malloc(sizeof(SDreq))) == nil)
          186                 return 0;
          187         if((p = sdmalloc(8)) == nil){
          188                 free(r);
          189                 return 0;
          190         }
          191 
          192         ok = 0;
          193 
          194         r->unit = unit;
          195         r->lun = 0;                                /* ??? */
          196         for(retries = 0; retries < 10; retries++){
          197                 /*
          198                  * Read-capacity is mandatory for DA, WORM, CD-ROM and
          199                  * MO. It may return 'not ready' if type DA is not
          200                  * spun up, type MO or type CD-ROM are not loaded or just
          201                  * plain slow getting their act together after a reset.
          202                  */
          203                 r->write = 0;
          204                 memset(r->cmd, 0, sizeof(r->cmd));
          205                 r->cmd[0] = 0x25;
          206                 r->cmd[1] = r->lun<<5;
          207                 r->clen = 10;
          208                 r->data = p;
          209                 r->dlen = 8;
          210                 r->flags = 0;
          211 
          212                 r->status = ~0;
          213                 switch(scsirio(r)){
          214                 default:
          215                         break;
          216                 case 0:
          217                         unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
          218                         unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
          219 
          220                         /*
          221                          * Some ATAPI CD readers lie about the block size.
          222                          * Since we don't read audio via this interface
          223                          * it's okay to always fudge this.
          224                          */
          225                         if(unit->secsize == 2352)
          226                                 unit->secsize = 2048;
          227                         /*
          228                          * Devices with removable media may return 0 sectors
          229                          * when they have empty media (e.g. sata dvd writers);
          230                          * if so, keep the count zero.
          231                          *
          232                          * Read-capacity returns the LBA of the last sector,
          233                          * therefore the number of sectors must be incremented.
          234                          */
          235                         if(unit->sectors != 0)
          236                                 unit->sectors++;
          237                         ok = 1;
          238                         break;
          239                 case 1:
          240                         ok = 1;
          241                         break;
          242                 case 2:
          243                         continue;
          244                 }
          245                 break;
          246         }
          247         free(p);
          248         free(r);
          249 
          250         if(ok)
          251                 return ok+retries;
          252         else
          253                 return 0;
          254 }
          255 
          256 int
          257 scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
          258 {
          259         SDreq *r;
          260         int status;
          261 
          262         if((r = malloc(sizeof(SDreq))) == nil)
          263                 return SDmalloc;
          264         r->unit = unit;
          265         r->lun = cmd[1]>>5;                /* ??? */
          266         r->write = write;
          267         memmove(r->cmd, cmd, clen);
          268         r->clen = clen;
          269         r->data = data;
          270         if(dlen)
          271                 r->dlen = *dlen;
          272         r->flags = 0;
          273 
          274         r->status = ~0;
          275 
          276         /*
          277          * Call the device-specific I/O routine.
          278          * There should be no calls to 'error()' below this
          279          * which percolate back up.
          280          */
          281         switch(status = unit->dev->ifc->rio(r)){
          282         case SDok:
          283                 if(dlen)
          284                         *dlen = r->rlen;
          285                 /*FALLTHROUGH*/
          286         case SDcheck:
          287                 /*FALLTHROUGH*/
          288         default:
          289                 /*
          290                  * It's more complicated than this. There are conditions
          291                  * which are 'ok' but for which the returned status code
          292                  * is not 'SDok'.
          293                  * Also, not all conditions require a reqsense, might
          294                  * need to do a reqsense here and make it available to the
          295                  * caller somehow.
          296                  *
          297                  * MaƱana.
          298                  */
          299                 break;
          300         }
          301         sdfree(r);
          302 
          303         return status;
          304 }
          305 
          306 static void
          307 scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno)
          308 {
          309         uchar *c;
          310 
          311         c = r->cmd;
          312         if(write == 0)
          313                 c[0] = 0x28;
          314         else
          315                 c[0] = 0x2A;
          316         c[1] = lun<<5;
          317         c[2] = bno>>24;
          318         c[3] = bno>>16;
          319         c[4] = bno>>8;
          320         c[5] = bno;
          321         c[6] = 0;
          322         c[7] = nb>>8;
          323         c[8] = nb;
          324         c[9] = 0;
          325 
          326         r->clen = 10;
          327 }
          328 
          329 static void
          330 scsifmt16(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] = 0x88;
          337         else
          338                 c[0] = 0x8A;
          339         c[1] = lun<<5;                /* so wrong */
          340         c[2] = bno>>56;
          341         c[3] = bno>>48;
          342         c[4] = bno>>40;
          343         c[5] = bno>>32;
          344         c[6] = bno>>24;
          345         c[7] = bno>>16;
          346         c[8] = bno>>8;
          347         c[9] = bno;
          348         c[10] = nb>>24;
          349         c[11] = nb>>16;
          350         c[12] = nb>>8;
          351         c[13] = nb;
          352         c[14] = 0;
          353         c[15] = 0;
          354 
          355         r->clen = 16;
          356 }
          357 
          358 long
          359 scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
          360 {
          361         SDreq *r;
          362         long rlen;
          363 
          364         if((r = malloc(sizeof(SDreq))) == nil)
          365                 error(Enomem);
          366         r->unit = unit;
          367         r->lun = lun;
          368 again:
          369         r->write = write;
          370         if(bno >= (1ULL<<32))
          371                 scsifmt16(r, write, lun, nb, bno);
          372         else
          373                 scsifmt10(r, write, lun, nb, bno);
          374         r->data = data;
          375         r->dlen = nb*unit->secsize;
          376         r->flags = 0;
          377 
          378         r->status = ~0;
          379         switch(scsirio(r)){
          380         default:
          381                 rlen = -1;
          382                 break;
          383         case 0:
          384                 rlen = r->rlen;
          385                 break;
          386         case 2:
          387                 rlen = -1;
          388                 if(!(r->flags & SDvalidsense))
          389                         break;
          390                 switch(r->sense[2] & 0x0F){
          391                 default:
          392                         break;
          393                 case 0x01:                /* recovered error */
          394                         print("%s: recovered error at sector %llud\n",
          395                                 unit->perm.name, bno);
          396                         rlen = r->rlen;
          397                         break;
          398                 case 0x06:                /* check condition */
          399                         /*
          400                          * Check for a removeable media change.
          401                          * If so, mark it by zapping the geometry info
          402                          * to force an online request.
          403                          */
          404                         if(r->sense[12] != 0x28 || r->sense[13] != 0)
          405                                 break;
          406                         if(unit->inquiry[1] & 0x80)
          407                                 unit->sectors = 0;
          408                         break;
          409                 case 0x02:                /* not ready */
          410                         /*
          411                          * If unit is becoming ready,
          412                          * rather than not not ready, try again.
          413                          */
          414                         if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
          415                                 goto again;
          416                         break;
          417                 }
          418                 break;
          419         }
          420         free(r);
          421 
          422         return rlen;
          423 }
          424