xref: /plan9/sys/src/9/port/sdscsi.c (revision 2752240283b2037f93a98c5bab11217cae887208)
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
scsitest(SDreq * r)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
scsiverify(SDunit * unit)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 	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] & SDinq0periphtype) == SDperdisk){
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
scsirio(SDreq * r)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
scsionline(SDunit * unit)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
scsiexec(SDunit * unit,int write,uchar * cmd,int clen,void * data,int * dlen)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
scsifmt10(SDreq * r,int write,int lun,ulong nb,uvlong bno)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
scsifmt16(SDreq * r,int write,int lun,ulong nb,uvlong bno)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
scsibio(SDunit * unit,int lun,int write,void * data,long nb,uvlong bno)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->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] & SDinq1removable)
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 
425