xref: /plan9-contrib/sys/src/9k/port/sdscsi.c (revision a58eaef09f448f1157eaebb792dfa60a76a2596a)
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