xref: /plan9-contrib/sys/src/9/port/sdaoe.c (revision 1936bb650459bace06c38a45b60888b47e5cd459)
1 /*
2  * aoe sd driver, copyright © 2007 coraid
3  */
4 
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12 #include "../port/sd.h"
13 #include "../port/netif.h"
14 #include "../port/aoe.h"
15 
16 extern	char	Echange[];
17 extern	char	Enotup[];
18 
19 #define uprint(...)	snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
20 
21 enum {
22 	Nctlr	= 32,
23 	Maxpath	= 128,
24 
25 	Probeintvl	= 100,		/* ms. between probes */
26 	Probemax	= 20,		/* max probes */
27 };
28 
29 enum {
30 	/* sync with ahci.h */
31 	Dllba 	= 1<<0,
32 	Dsmart	= 1<<1,
33 	Dpower	= 1<<2,
34 	Dnop	= 1<<3,
35 	Datapi	= 1<<4,
36 	Datapi16= 1<<5,
37 };
38 
39 static char *flagname[] = {
40 	"llba",
41 	"smart",
42 	"power",
43 	"nop",
44 	"atapi",
45 	"atapi16",
46 };
47 
48 typedef struct Ctlr Ctlr;
49 struct Ctlr{
50 	QLock;
51 
52 	Ctlr	*next;
53 	SDunit	*unit;
54 
55 	char	path[Maxpath];
56 	Chan	*c;
57 
58 	ulong	vers;
59 	uchar	mediachange;
60 	uchar	flag;
61 	uchar	smart;
62 	uchar	smartrs;
63 	uchar	feat;
64 
65 	uvlong	sectors;
66 	char	serial[20+1];
67 	char	firmware[8+1];
68 	char	model[40+1];
69 	char	ident[0x100];
70 };
71 
72 void	aoeidmove(char *p, ushort *a, unsigned n);
73 
74 static	Lock	ctlrlock;
75 static	Ctlr	*head;
76 static	Ctlr	*tail;
77 
78 SDifc sdaoeifc;
79 
80 static ushort
81 gbit16(void *a)
82 {
83 	uchar *i;
84 
85 	i = a;
86 	return i[1] << 8 | i[0];
87 }
88 
89 static ulong
90 gbit32(void *a)
91 {
92 	ulong j;
93 	uchar *i;
94 
95 	i = a;
96 	j  = i[3] << 24;
97 	j |= i[2] << 16;
98 	j |= i[1] << 8;
99 	j |= i[0];
100 	return j;
101 }
102 
103 static uvlong
104 gbit64(void *a)
105 {
106 	uchar *i;
107 
108 	i = a;
109 	return (uvlong)gbit32(i+4)<<32 | gbit32(i);
110 }
111 
112 static int
113 identify(Ctlr *c, ushort *id)
114 {
115 	int i;
116 	uchar oserial[21];
117 	uvlong osectors, s;
118 
119 	osectors = c->sectors;
120 	memmove(oserial, c->serial, sizeof c->serial);
121 
122 	c->feat &= ~(Dllba|Dpower|Dsmart|Dnop);
123 	i = gbit16(id+83) | gbit16(id+86);
124 	if(i & (1<<10)){
125 		c->feat |= Dllba;
126 		s = gbit64(id+100);
127 	}else
128 		s = gbit32(id+60);
129 
130 	i = gbit16(id+83);
131 	if((i>>14) == 1) {
132 		if(i & (1<<3))
133 			c->feat |= Dpower;
134 		i = gbit16(id+82);
135 		if(i & 1)
136 			c->feat |= Dsmart;
137 		if(i & (1<<14))
138 			c->feat |= Dnop;
139 	}
140 
141 	aoeidmove(c->serial, id+10, 20);
142 	aoeidmove(c->firmware, id+23, 8);
143 	aoeidmove(c->model, id+27, 40);
144 
145 	if((osectors == 0 || osectors != s) &&
146 	    memcmp(oserial, c->serial, sizeof oserial) != 0){
147 		c->sectors = s;
148 		c->mediachange = 1;
149 		c->vers++;
150 	}
151 	return 0;
152 }
153 
154 /* must call with d qlocked */
155 static int
156 aoeidentify(Ctlr *d, SDunit *u)
157 {
158 	Chan *c;
159 
160 	c = nil;
161 	if(waserror()){
162 		if(c)
163 			cclose(c);
164 		iprint("aoeidentify: %s\n", up->errstr);
165 		nexterror();
166 	}
167 
168 	uprint("%s/ident", d->path);
169 	c = namec(up->genbuf, Aopen, OREAD, 0);
170 	devtab[c->type]->read(c, d->ident, sizeof d->ident, 0);
171 
172 	poperror();
173 	cclose(c);
174 
175 	d->feat = 0;
176 	d->smart = 0;
177 	identify(d, (ushort*)d->ident);
178 
179 	memset(u->inquiry, 0, sizeof u->inquiry);
180 	u->inquiry[2] = 2;
181 	u->inquiry[3] = 2;
182 	u->inquiry[4] = sizeof u->inquiry - 4;
183 	memmove(u->inquiry+8, d->model, 40);
184 
185 	return 0;
186 }
187 
188 static Ctlr*
189 ctlrlookup(char *path)
190 {
191 	Ctlr *c;
192 
193 	lock(&ctlrlock);
194 	for(c = head; c; c = c->next)
195 		if(strcmp(c->path, path) == 0)
196 			break;
197 	unlock(&ctlrlock);
198 	return c;
199 }
200 
201 static Ctlr*
202 newctlr(char *path)
203 {
204 	Ctlr *c;
205 
206 	/* race? */
207 	if(ctlrlookup(path))
208 		error(Eexist);
209 
210 	if((c = malloc(sizeof *c)) == nil)
211 		return 0;
212 	kstrcpy(c->path, path, sizeof c->path);
213 	lock(&ctlrlock);
214 	if(head != nil)
215 		tail->next = c;
216 	else
217 		head = c;
218 	tail = c;
219 	unlock(&ctlrlock);
220 	return c;
221 }
222 
223 static void
224 delctlr(Ctlr *c)
225 {
226 	Ctlr *x, *prev;
227 
228 	lock(&ctlrlock);
229 
230 	for(prev = 0, x = head; x; prev = x, x = c->next)
231 		if(strcmp(c->path, x->path) == 0)
232 			break;
233 	if(x == 0){
234 		unlock(&ctlrlock);
235 		error(Enonexist);
236 	}
237 
238 	if(prev)
239 		prev->next = x->next;
240 	else
241 		head = x->next;
242 	if(x->next == nil)
243 		tail = prev;
244 	unlock(&ctlrlock);
245 
246 	if(x->c)
247 		cclose(x->c);
248 	free(x);
249 }
250 
251 /* don't call aoeprobe from within a loop; it loops internally retrying open. */
252 static SDev*
253 aoeprobe(char *path, SDev *s)
254 {
255 	int n, i;
256 	char *p;
257 	Chan *c;
258 	Ctlr *ctlr;
259 
260 	if((p = strrchr(path, '/')) == 0)
261 		error(Ebadarg);
262 	*p = 0;
263 	uprint("%s/ctl", path);
264 	*p = '/';
265 
266 	c = namec(up->genbuf, Aopen, OWRITE, 0);
267 	if(waserror()) {
268 		cclose(c);
269 		nexterror();
270 	}
271 	n = uprint("discover %s", p+1);
272 	devtab[c->type]->write(c, up->genbuf, n, 0);
273 	poperror();
274 	cclose(c);
275 
276 	for(i = 0; i < Probemax; i++){
277 		tsleep(&up->sleep, return0, 0, Probeintvl);
278 		uprint("%s/ident", path);
279 		if(!waserror()) {
280 			c = namec(up->genbuf, Aopen, OREAD, 0);
281 			poperror();
282 			cclose(c);
283 			break;
284 		}
285 	}
286 	if(i >= Probemax)
287 		error(Etimedout);
288 	uprint("%s/ident", path);
289 	ctlr = newctlr(path);
290 	if(ctlr == nil || s == nil && (s = malloc(sizeof *s)) == nil)
291 		return nil;
292 	s->ctlr = ctlr;
293 	s->ifc = &sdaoeifc;
294 	s->nunit = 1;
295 	return s;
296 }
297 
298 static char 	*probef[32];
299 static int 	nprobe;
300 
301 static int
302 pnpprobeid(char *s)
303 {
304 	if(strlen(s) < 2)
305 		return 0;
306 	return s[1] == '!'? s[0]: 'e';
307 }
308 
309 static SDev*
310 aoepnp(void)
311 {
312 	int i, id;
313 	char *p;
314 	SDev *h, *t, *s;
315 
316 	if((p = getconf("aoedev")) == 0)
317 		return 0;
318 	nprobe = tokenize(p, probef, nelem(probef));
319 	h = t = 0;
320 	for(i = 0; i < nprobe; i++){
321 		id = pnpprobeid(probef[i]);
322 		if(id == 0)
323 			continue;
324 		s = malloc(sizeof *s);
325 		if(s == nil)
326 			break;
327 		s->ctlr = 0;
328 		s->idno = id;
329 		s->ifc = &sdaoeifc;
330 		s->nunit = 1;
331 
332 		if(h)
333 			t->next = s;
334 		else
335 			h = s;
336 		t = s;
337 	}
338 	return h;
339 }
340 
341 static Ctlr*
342 pnpprobe(SDev *sd)
343 {
344 	ulong start;
345 	char *p;
346 	static int i;
347 
348 	if(i > nprobe)
349 		return 0;
350 	p = probef[i++];
351 	if(strlen(p) < 2)
352 		return 0;
353 	if(p[1] == '!')
354 		p += 2;
355 
356 	start = TK2MS(MACHP(0)->ticks);
357 	if(waserror()){
358 		print("#æ: pnpprobe failed in %lud ms: %s: %s\n",
359 			TK2MS(MACHP(0)->ticks) - start, probef[i-1],
360 			up->errstr);
361 		return nil;
362 	}
363 	sd = aoeprobe(p, sd);			/* does a round of probing */
364 	poperror();
365 	print("#æ: pnpprobe established %s in %lud ms\n",
366 		probef[i-1], TK2MS(MACHP(0)->ticks) - start);
367 	return sd->ctlr;
368 }
369 
370 
371 static int
372 aoeverify(SDunit *u)
373 {
374 	SDev *s;
375 	Ctlr *c;
376 
377 	s = u->dev;
378 	c = s->ctlr;
379 	if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil)
380 		return 0;
381 	c->mediachange = 1;
382 	return 1;
383 }
384 
385 static int
386 aoeconnect(SDunit *u, Ctlr *c)
387 {
388 	qlock(c);
389 	if(waserror()){
390 		qunlock(c);
391 		return -1;
392 	}
393 
394 	aoeidentify(u->dev->ctlr, u);
395 	if(c->c)
396 		cclose(c->c);
397 	c->c = 0;
398 	uprint("%s/data", c->path);
399 	c->c = namec(up->genbuf, Aopen, ORDWR, 0);
400 	qunlock(c);
401 	poperror();
402 
403 	return 0;
404 }
405 
406 static int
407 aoeonline(SDunit *u)
408 {
409 	Ctlr *c;
410 	int r;
411 
412 	c = u->dev->ctlr;
413 	r = 0;
414 
415 	if((c->feat&Datapi) && c->mediachange){
416 		if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0)
417 			c->mediachange = 0;
418 		return r;
419 	}
420 
421 	if(c->mediachange){
422 		if(aoeconnect(u, c) == -1)
423 			return 0;
424 		r = 2;
425 		c->mediachange = 0;
426 		u->sectors = c->sectors;
427 		u->secsize = Aoesectsz;
428 	} else
429 		r = 1;
430 
431 	return r;
432 }
433 
434 static int
435 aoerio(SDreq *r)
436 {
437 	int i, count;
438 	uvlong lba;
439 	char *name;
440 	uchar *cmd;
441 	long (*rio)(Chan*, void*, long, vlong);
442 	Ctlr *c;
443 	SDunit *unit;
444 
445 	unit = r->unit;
446 	c = unit->dev->ctlr;
447 //	if(c->feat & Datapi)
448 //		return aoeriopkt(r, d);
449 
450 	cmd = r->cmd;
451 	name = unit->name;
452 
453 	if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
454 //		qlock(c);
455 //		i = flushcache();
456 //		qunlock(c);
457 //		if(i == 0)
458 //			return sdsetsense(r, SDok, 0, 0, 0);
459 		return sdsetsense(r, SDcheck, 3, 0xc, 2);
460 	}
461 
462 	if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){
463 		r->status = i;
464 		return i;
465 	}
466 
467 	switch(*cmd){
468 	case 0x88:
469 	case 0x28:
470 		rio = devtab[c->c->type]->read;
471 		break;
472 	case 0x8a:
473 	case 0x2a:
474 		rio = devtab[c->c->type]->write;
475 		break;
476 	default:
477 		print("%s: bad cmd %#.2ux\n", name, cmd[0]);
478 		r->status = SDcheck;
479 		return SDcheck;
480 	}
481 
482 	if(r->data == nil)
483 		return SDok;
484 
485 	if(r->clen == 16){
486 		if(cmd[2] || cmd[3])
487 			return sdsetsense(r, SDcheck, 3, 0xc, 2);
488 		lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
489 		lba |=   cmd[6]<<24 |  cmd[7]<<16 |  cmd[8]<<8 | cmd[9];
490 		count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
491 	}else{
492 		lba  = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
493 		count = cmd[7]<<8 | cmd[8];
494 	}
495 
496 	count *= Aoesectsz;
497 
498 	if(r->dlen < count)
499 		count = r->dlen & ~0x1ff;
500 
501 	if(waserror()){
502 		if(strcmp(up->errstr, Echange) == 0 ||
503 		    strcmp(up->errstr, Enotup) == 0)
504 			unit->sectors = 0;
505 		nexterror();
506 	}
507 	r->rlen = rio(c->c, r->data, count, Aoesectsz * lba);
508 	poperror();
509 	r->status = SDok;
510 	return SDok;
511 }
512 
513 static char *smarttab[] = {
514 	"unset",
515 	"error",
516 	"threshold exceeded",
517 	"normal"
518 };
519 
520 static char *
521 pflag(char *s, char *e, uchar f)
522 {
523 	uchar i, m;
524 
525 	for(i = 0; i < 8; i++){
526 		m = 1 << i;
527 		if(f & m)
528 			s = seprint(s, e, "%s ", flagname[i]);
529 	}
530 	return seprint(s, e, "\n");
531 }
532 
533 static int
534 aoerctl(SDunit *u, char *p, int l)
535 {
536 	Ctlr *c;
537 	char *e, *op;
538 
539 	if((c = u->dev->ctlr) == nil)
540 		return 0;
541 	e = p+l;
542 	op = p;
543 
544 	p = seprint(p, e, "model\t%s\n", c->model);
545 	p = seprint(p, e, "serial\t%s\n", c->serial);
546 	p = seprint(p, e, "firm	%s\n", c->firmware);
547 	if(c->smartrs == 0xff)
548 		p = seprint(p, e, "smart\tenable error\n");
549 	else if(c->smartrs == 0)
550 		p = seprint(p, e, "smart\tdisabled\n");
551 	else
552 		p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]);
553 	p = seprint(p, e, "flag	");
554 	p = pflag(p, e, c->feat);
555 	p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz);
556 	return p-op;
557 }
558 
559 static int
560 aoewctl(SDunit *, Cmdbuf *cmd)
561 {
562 	cmderror(cmd, Ebadarg);
563 	return 0;
564 }
565 
566 static SDev*
567 aoeprobew(DevConf *c)
568 {
569 	char *p;
570 
571 	p = strchr(c->type, '/');
572 	if(p == nil || strlen(p) > Maxpath - 11)
573 		error(Ebadarg);
574 	if(p[1] == '#')
575 		p++;			/* hack */
576 	if(ctlrlookup(p))
577 		error(Einuse);
578 	return aoeprobe(p, 0);
579 }
580 
581 static void
582 aoeclear(SDev *s)
583 {
584 	delctlr((Ctlr *)s->ctlr);
585 }
586 
587 static char*
588 aoertopctl(SDev *s, char *p, char *e)
589 {
590 	Ctlr *c;
591 
592 	if(s == nil || (c = s->ctlr) == nil)
593 		return p;
594 
595 	return seprint(p, e, "%s aoe %s\n", s->name, c->path);
596 }
597 
598 static int
599 aoewtopctl(SDev *, Cmdbuf *cmd)
600 {
601 	switch(cmd->nf){
602 	default:
603 		cmderror(cmd, Ebadarg);
604 	}
605 	return 0;
606 }
607 
608 SDifc sdaoeifc = {
609 	"aoe",
610 
611 	aoepnp,
612 	nil,		/* legacy */
613 	nil,		/* enable */
614 	nil,		/* disable */
615 
616 	aoeverify,
617 	aoeonline,
618 	aoerio,
619 	aoerctl,
620 	aoewctl,
621 
622 	scsibio,
623 	aoeprobew,	/* probe */
624 	aoeclear,	/* clear */
625 	aoertopctl,
626 	aoewtopctl,
627 };
628