vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | 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