xref: /plan9/sys/src/9/port/devsd.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 /*
2  * Storage Device.
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "../port/error.h"
12 
13 #include "../port/sd.h"
14 
15 extern Dev sddevtab;
16 extern SDifc* sdifc[];
17 
18 static char Echange[] = "media or partition has changed";
19 
20 static char devletters[] = "0123456789"
21 	"abcdefghijklmnopqrstuvwxyz"
22 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
23 
24 static SDev *devs[sizeof devletters-1];
25 static QLock devslock;
26 
27 enum {
28 	Rawcmd,
29 	Rawdata,
30 	Rawstatus,
31 };
32 
33 enum {
34 	Qtopdir		= 1,		/* top level directory */
35 	Qtopbase,
36 	Qtopctl		 = Qtopbase,
37 
38 	Qunitdir,			/* directory per unit */
39 	Qunitbase,
40 	Qctl		= Qunitbase,
41 	Qraw,
42 	Qpart,
43 
44 	TypeLOG		= 4,
45 	NType		= (1<<TypeLOG),
46 	TypeMASK	= (NType-1),
47 	TypeSHIFT	= 0,
48 
49 	PartLOG		= 8,
50 	NPart		= (1<<PartLOG),
51 	PartMASK	= (NPart-1),
52 	PartSHIFT	= TypeLOG,
53 
54 	UnitLOG		= 8,
55 	NUnit		= (1<<UnitLOG),
56 	UnitMASK	= (NUnit-1),
57 	UnitSHIFT	= (PartLOG+TypeLOG),
58 
59 	DevLOG		= 8,
60 	NDev		= (1 << DevLOG),
61 	DevMASK		= (NDev-1),
62 	DevSHIFT	 = (UnitLOG+PartLOG+TypeLOG),
63 
64 	Ncmd = 20,
65 };
66 
67 #define TYPE(q)		((((ulong)(q).path)>>TypeSHIFT) & TypeMASK)
68 #define PART(q)		((((ulong)(q).path)>>PartSHIFT) & PartMASK)
69 #define UNIT(q)		((((ulong)(q).path)>>UnitSHIFT) & UnitMASK)
70 #define DEV(q)		((((ulong)(q).path)>>DevSHIFT) & DevMASK)
71 #define QID(d,u, p, t)	(((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
72 					 ((p)<<PartSHIFT)|((t)<<TypeSHIFT))
73 
74 
75 static void
76 sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
77 {
78 	SDpart *pp;
79 	int i, partno;
80 
81 	/*
82 	 * Check name not already used
83 	 * and look for a free slot.
84 	 */
85 	if(unit->part != nil){
86 		partno = -1;
87 		for(i = 0; i < unit->npart; i++){
88 			pp = &unit->part[i];
89 			if(!pp->valid){
90 				if(partno == -1)
91 					partno = i;
92 				break;
93 			}
94 			if(strcmp(name, pp->name) == 0){
95 				if(pp->start == start && pp->end == end)
96 					return;
97 				error(Ebadctl);
98 			}
99 		}
100 	}
101 	else{
102 		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil)
103 			error(Enomem);
104 		unit->npart = SDnpart;
105 		partno = 0;
106 	}
107 
108 	/*
109 	 * If no free slot found then increase the
110 	 * array size (can't get here with unit->part == nil).
111 	 */
112 	if(partno == -1){
113 		if(unit->npart >= NPart)
114 			error(Enomem);
115 		if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil)
116 			error(Enomem);
117 		memmove(pp, unit->part, sizeof(SDpart)*unit->npart);
118 		free(unit->part);
119 		unit->part = pp;
120 		partno = unit->npart;
121 		unit->npart += SDnpart;
122 	}
123 
124 	/*
125 	 * Check size and extent are valid.
126 	 */
127 	if(start > end || end > unit->sectors)
128 		error(Eio);
129 	pp = &unit->part[partno];
130 	pp->start = start;
131 	pp->end = end;
132 	kstrdup(&pp->name, name);
133 	kstrdup(&pp->user, eve);
134 	pp->perm = 0640;
135 	pp->valid = 1;
136 }
137 
138 static void
139 sddelpart(SDunit* unit, char* name)
140 {
141 	int i;
142 	SDpart *pp;
143 
144 	/*
145 	 * Look for the partition to delete.
146 	 * Can't delete if someone still has it open.
147 	 */
148 	pp = unit->part;
149 	for(i = 0; i < unit->npart; i++){
150 		if(strcmp(name, pp->name) == 0)
151 			break;
152 		pp++;
153 	}
154 	if(i >= unit->npart)
155 		error(Ebadctl);
156 	if(strcmp(up->user, pp->user) && !iseve())
157 		error(Eperm);
158 	pp->valid = 0;
159 	pp->vers++;
160 }
161 
162 static void
163 sdincvers(SDunit *unit)
164 {
165 	int i;
166 
167 	unit->vers++;
168 	if(unit->part){
169 		for(i = 0; i < unit->npart; i++){
170 			unit->part[i].valid = 0;
171 			unit->part[i].vers++;
172 		}
173 	}
174 }
175 
176 static int
177 sdinitpart(SDunit* unit)
178 {
179 	int nf;
180 	uvlong start, end;
181 	char *f[4], *p, *q, buf[10];
182 
183 	if(unit->sectors > 0){
184 		unit->sectors = unit->secsize = 0;
185 		sdincvers(unit);
186 	}
187 
188 	if(unit->inquiry[0] & 0xC0)
189 		return 0;
190 	switch(unit->inquiry[0] & 0x1F){
191 	case 0x00:			/* DA */
192 	case 0x04:			/* WORM */
193 	case 0x05:			/* CD-ROM */
194 	case 0x07:			/* MO */
195 		break;
196 	default:
197 		return 0;
198 	}
199 
200 	if(unit->dev->ifc->online)
201 		unit->dev->ifc->online(unit);
202 	if(unit->sectors){
203 		sdincvers(unit);
204 		sdaddpart(unit, "data", 0, unit->sectors);
205 
206 		/*
207 		 * Use partitions passed from boot program,
208 		 * e.g.
209 		 *	sdC0part=dos 63 123123/plan9 123123 456456
210 		 * This happens before /boot sets hostname so the
211 		 * partitions will have the null-string for user.
212 		 * The gen functions patch it up.
213 		 */
214 		snprint(buf, sizeof buf, "%spart", unit->name);
215 		for(p = getconf(buf); p != nil; p = q){
216 			if(q = strchr(p, '/'))
217 				*q++ = '\0';
218 			nf = tokenize(p, f, nelem(f));
219 			if(nf < 3)
220 				continue;
221 
222 			start = strtoull(f[1], 0, 0);
223 			end = strtoull(f[2], 0, 0);
224 			if(!waserror()){
225 				sdaddpart(unit, f[0], start, end);
226 				poperror();
227 			}
228 		}
229 	}
230 
231 	return 1;
232 }
233 
234 static int
235 sdindex(int idno)
236 {
237 	char *p;
238 
239 	p = strchr(devletters, idno);
240 	if(p == nil)
241 		return -1;
242 	return p-devletters;
243 }
244 
245 static SDev*
246 sdgetdev(int idno)
247 {
248 	SDev *sdev;
249 	int i;
250 
251 	if((i = sdindex(idno)) < 0)
252 		return nil;
253 
254 	qlock(&devslock);
255 	if(sdev = devs[i])
256 		incref(&sdev->r);
257 	qunlock(&devslock);
258 	return sdev;
259 }
260 
261 static SDunit*
262 sdgetunit(SDev* sdev, int subno)
263 {
264 	SDunit *unit;
265 	char buf[32];
266 
267 	/*
268 	 * Associate a unit with a given device and sub-unit
269 	 * number on that device.
270 	 * The device will be probed if it has not already been
271 	 * successfully accessed.
272 	 */
273 	qlock(&sdev->unitlock);
274 	if(subno > sdev->nunit){
275 		qunlock(&sdev->unitlock);
276 		return nil;
277 	}
278 
279 	unit = sdev->unit[subno];
280 	if(unit == nil){
281 		/*
282 		 * Probe the unit only once. This decision
283 		 * may be a little severe and reviewed later.
284 		 */
285 		if(sdev->unitflg[subno]){
286 			qunlock(&sdev->unitlock);
287 			return nil;
288 		}
289 		if((unit = malloc(sizeof(SDunit))) == nil){
290 			qunlock(&sdev->unitlock);
291 			return nil;
292 		}
293 		sdev->unitflg[subno] = 1;
294 
295 		snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
296 		kstrdup(&unit->name, buf);
297 		kstrdup(&unit->user, eve);
298 		unit->perm = 0555;
299 		unit->subno = subno;
300 		unit->dev = sdev;
301 
302 		if(sdev->enabled == 0 && sdev->ifc->enable)
303 			sdev->ifc->enable(sdev);
304 		sdev->enabled = 1;
305 
306 		/*
307 		 * No need to lock anything here as this is only
308 		 * called before the unit is made available in the
309 		 * sdunit[] array.
310 		 */
311 		if(unit->dev->ifc->verify(unit) == 0){
312 			qunlock(&sdev->unitlock);
313 			free(unit);
314 			return nil;
315 		}
316 		sdev->unit[subno] = unit;
317 	}
318 	qunlock(&sdev->unitlock);
319 	return unit;
320 }
321 
322 static void
323 sdreset(void)
324 {
325 	int i;
326 	SDev *sdev;
327 
328 	/*
329 	 * Probe all known controller types and register any devices found.
330 	 */
331 	for(i = 0; sdifc[i] != nil; i++){
332 		if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
333 			continue;
334 		sdadddevs(sdev);
335 	}
336 }
337 
338 void
339 sdadddevs(SDev *sdev)
340 {
341 	int i, j, id;
342 	SDev *next;
343 
344 	for(; sdev; sdev=next){
345 		next = sdev->next;
346 
347 		sdev->unit = (SDunit**)malloc(sdev->nunit * sizeof(SDunit*));
348 		sdev->unitflg = (int*)malloc(sdev->nunit * sizeof(int));
349 		if(sdev->unit == nil || sdev->unitflg == nil){
350 			print("sdadddevs: out of memory\n");
351 		giveup:
352 			free(sdev->unit);
353 			free(sdev->unitflg);
354 			if(sdev->ifc->clear)
355 				sdev->ifc->clear(sdev);
356 			free(sdev);
357 			continue;
358 		}
359 		id = sdindex(sdev->idno);
360 		if(id == -1){
361 			print("sdadddevs: bad id number %d (%C)\n", id, id);
362 			goto giveup;
363 		}
364 		qlock(&devslock);
365 		for(i=0; i<nelem(devs); i++){
366 			if(devs[j = (id+i)%nelem(devs)] == nil){
367 				sdev->idno = devletters[j];
368 				devs[j] = sdev;
369 				snprint(sdev->name, sizeof sdev->name, "sd%c", devletters[j]);
370 				break;
371 			}
372 		}
373 		qunlock(&devslock);
374 		if(i == nelem(devs)){
375 			print("sdadddevs: out of device letters\n");
376 			goto giveup;
377 		}
378 	}
379 }
380 
381 // void
382 // sdrmdevs(SDev *sdev)
383 // {
384 // 	char buf[2];
385 //
386 // 	snprint(buf, sizeof buf, "%c", sdev->idno);
387 // 	unconfigure(buf);
388 // }
389 
390 static int
391 sd2gen(Chan* c, int i, Dir* dp)
392 {
393 	Qid q;
394 	uvlong l;
395 	SDpart *pp;
396 	SDperm *perm;
397 	SDunit *unit;
398 	SDev *sdev;
399 	int rv;
400 
401 	sdev = sdgetdev(DEV(c->qid));
402 	assert(sdev);
403 	unit = sdev->unit[UNIT(c->qid)];
404 
405 	rv = -1;
406 	switch(i){
407 	case Qctl:
408 		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl),
409 			unit->vers, QTFILE);
410 		perm = &unit->ctlperm;
411 		if(emptystr(perm->user)){
412 			kstrdup(&perm->user, eve);
413 			perm->perm = 0640;
414 		}
415 		devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
416 		rv = 1;
417 		break;
418 
419 	case Qraw:
420 		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw),
421 			unit->vers, QTFILE);
422 		perm = &unit->rawperm;
423 		if(emptystr(perm->user)){
424 			kstrdup(&perm->user, eve);
425 			perm->perm = DMEXCL|0600;
426 		}
427 		devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
428 		rv = 1;
429 		break;
430 
431 	case Qpart:
432 		pp = &unit->part[PART(c->qid)];
433 		l = (pp->end - pp->start) * unit->secsize;
434 		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart),
435 			unit->vers+pp->vers, QTFILE);
436 		if(emptystr(pp->user))
437 			kstrdup(&pp->user, eve);
438 		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
439 		rv = 1;
440 		break;
441 	}
442 
443 	decref(&sdev->r);
444 	return rv;
445 }
446 
447 static int
448 sd1gen(Chan* c, int i, Dir* dp)
449 {
450 	Qid q;
451 
452 	switch(i){
453 	case Qtopctl:
454 		mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
455 		devdir(c, q, "sdctl", 0, eve, 0640, dp);
456 		return 1;
457 	}
458 	return -1;
459 }
460 
461 static int
462 sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
463 {
464 	Qid q;
465 	uvlong l;
466 	int i, r;
467 	SDpart *pp;
468 	SDunit *unit;
469 	SDev *sdev;
470 
471 	switch(TYPE(c->qid)){
472 	case Qtopdir:
473 		if(s == DEVDOTDOT){
474 			mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
475 			sprint(up->genbuf, "#%C", sddevtab.dc);
476 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
477 			return 1;
478 		}
479 
480 		if(s+Qtopbase < Qunitdir)
481 			return sd1gen(c, s+Qtopbase, dp);
482 		s -= (Qunitdir-Qtopbase);
483 
484 		qlock(&devslock);
485 		for(i=0; i<nelem(devs); i++){
486 			if(devs[i]){
487 				if(s < devs[i]->nunit)
488 					break;
489 				s -= devs[i]->nunit;
490 			}
491 		}
492 
493 		if(i == nelem(devs)){
494 			/* Run off the end of the list */
495 			qunlock(&devslock);
496 			return -1;
497 		}
498 
499 		if((sdev = devs[i]) == nil){
500 			qunlock(&devslock);
501 			return 0;
502 		}
503 
504 		incref(&sdev->r);
505 		qunlock(&devslock);
506 
507 		if((unit = sdev->unit[s]) == nil)
508 			if((unit = sdgetunit(sdev, s)) == nil){
509 				decref(&sdev->r);
510 				return 0;
511 			}
512 
513 		mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR);
514 		if(emptystr(unit->user))
515 			kstrdup(&unit->user, eve);
516 		devdir(c, q, unit->name, 0, unit->user, unit->perm, dp);
517 		decref(&sdev->r);
518 		return 1;
519 
520 	case Qunitdir:
521 		if(s == DEVDOTDOT){
522 			mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR);
523 			sprint(up->genbuf, "#%C", sddevtab.dc);
524 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
525 			return 1;
526 		}
527 
528 		if((sdev = sdgetdev(DEV(c->qid))) == nil){
529 			devdir(c, c->qid, "unavailable", 0, eve, 0, dp);
530 			return 1;
531 		}
532 
533 		unit = sdev->unit[UNIT(c->qid)];
534 		qlock(&unit->ctl);
535 
536 		/*
537 		 * Check for media change.
538 		 * If one has already been detected, sectors will be zero.
539 		 * If there is one waiting to be detected, online
540 		 * will return > 1.
541 		 * Online is a bit of a large hammer but does the job.
542 		 */
543 		if(unit->sectors == 0
544 		|| (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
545 			sdinitpart(unit);
546 
547 		i = s+Qunitbase;
548 		if(i < Qpart){
549 			r = sd2gen(c, i, dp);
550 			qunlock(&unit->ctl);
551 			decref(&sdev->r);
552 			return r;
553 		}
554 		i -= Qpart;
555 		if(unit->part == nil || i >= unit->npart){
556 			qunlock(&unit->ctl);
557 			decref(&sdev->r);
558 			break;
559 		}
560 		pp = &unit->part[i];
561 		if(!pp->valid){
562 			qunlock(&unit->ctl);
563 			decref(&sdev->r);
564 			return 0;
565 		}
566 		l = (pp->end - pp->start) * unit->secsize;
567 		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart),
568 			unit->vers+pp->vers, QTFILE);
569 		if(emptystr(pp->user))
570 			kstrdup(&pp->user, eve);
571 		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
572 		qunlock(&unit->ctl);
573 		decref(&sdev->r);
574 		return 1;
575 	case Qraw:
576 	case Qctl:
577 	case Qpart:
578 		if((sdev = sdgetdev(DEV(c->qid))) == nil){
579 			devdir(c, q, "unavailable", 0, eve, 0, dp);
580 			return 1;
581 		}
582 		unit = sdev->unit[UNIT(c->qid)];
583 		qlock(&unit->ctl);
584 		r = sd2gen(c, TYPE(c->qid), dp);
585 		qunlock(&unit->ctl);
586 		decref(&sdev->r);
587 		return r;
588 	case Qtopctl:
589 		return sd1gen(c, TYPE(c->qid), dp);
590 	default:
591 		break;
592 	}
593 
594 	return -1;
595 }
596 
597 static Chan*
598 sdattach(char* spec)
599 {
600 	Chan *c;
601 	char *p;
602 	SDev *sdev;
603 	int idno, subno;
604 
605 	if(*spec == '\0'){
606 		c = devattach(sddevtab.dc, spec);
607 		mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
608 		return c;
609 	}
610 
611 	if(spec[0] != 's' || spec[1] != 'd')
612 		error(Ebadspec);
613 	idno = spec[2];
614 	subno = strtol(&spec[3], &p, 0);
615 	if(p == &spec[3])
616 		error(Ebadspec);
617 
618 	if((sdev=sdgetdev(idno)) == nil)
619 		error(Enonexist);
620 	if(sdgetunit(sdev, subno) == nil){
621 		decref(&sdev->r);
622 		error(Enonexist);
623 	}
624 
625 	c = devattach(sddevtab.dc, spec);
626 	mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR);
627 	c->dev = (sdev->idno << UnitLOG) + subno;
628 	decref(&sdev->r);
629 	return c;
630 }
631 
632 static Walkqid*
633 sdwalk(Chan* c, Chan* nc, char** name, int nname)
634 {
635 	return devwalk(c, nc, name, nname, nil, 0, sdgen);
636 }
637 
638 static int
639 sdstat(Chan* c, uchar* db, int n)
640 {
641 	return devstat(c, db, n, nil, 0, sdgen);
642 }
643 
644 static Chan*
645 sdopen(Chan* c, int omode)
646 {
647 	SDpart *pp;
648 	SDunit *unit;
649 	SDev *sdev;
650 	uchar tp;
651 
652 	c = devopen(c, omode, 0, 0, sdgen);
653 	if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
654 		return c;
655 
656 	sdev = sdgetdev(DEV(c->qid));
657 	if(sdev == nil)
658 		error(Enonexist);
659 
660 	unit = sdev->unit[UNIT(c->qid)];
661 
662 	switch(TYPE(c->qid)){
663 	case Qctl:
664 		c->qid.vers = unit->vers;
665 		break;
666 	case Qraw:
667 		c->qid.vers = unit->vers;
668 		if(tas(&unit->rawinuse) != 0){
669 			c->flag &= ~COPEN;
670 			decref(&sdev->r);
671 			error(Einuse);
672 		}
673 		unit->state = Rawcmd;
674 		break;
675 	case Qpart:
676 		qlock(&unit->ctl);
677 		if(waserror()){
678 			qunlock(&unit->ctl);
679 			c->flag &= ~COPEN;
680 			decref(&sdev->r);
681 			nexterror();
682 		}
683 		pp = &unit->part[PART(c->qid)];
684 		c->qid.vers = unit->vers+pp->vers;
685 		qunlock(&unit->ctl);
686 		poperror();
687 		break;
688 	}
689 	decref(&sdev->r);
690 	return c;
691 }
692 
693 static void
694 sdclose(Chan* c)
695 {
696 	SDunit *unit;
697 	SDev *sdev;
698 
699 	if(c->qid.type & QTDIR)
700 		return;
701 	if(!(c->flag & COPEN))
702 		return;
703 
704 	switch(TYPE(c->qid)){
705 	default:
706 		break;
707 	case Qraw:
708 		sdev = sdgetdev(DEV(c->qid));
709 		if(sdev){
710 			unit = sdev->unit[UNIT(c->qid)];
711 			unit->rawinuse = 0;
712 			decref(&sdev->r);
713 		}
714 		break;
715 	}
716 }
717 
718 static long
719 sdbio(Chan* c, int write, char* a, long len, uvlong off)
720 {
721 	int nchange;
722 	long l;
723 	uchar *b;
724 	SDpart *pp;
725 	SDunit *unit;
726 	SDev *sdev;
727 	ulong max, nb, offset;
728 	uvlong bno;
729 
730 	sdev = sdgetdev(DEV(c->qid));
731 	if(sdev == nil){
732 		decref(&sdev->r);
733 		error(Enonexist);
734 	}
735 	unit = sdev->unit[UNIT(c->qid)];
736 	if(unit == nil)
737 		error(Enonexist);
738 
739 	nchange = 0;
740 	qlock(&unit->ctl);
741 	while(waserror()){
742 		/* notification of media change; go around again */
743 		if(strcmp(up->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
744 			sdinitpart(unit);
745 			continue;
746 		}
747 
748 		/* other errors; give up */
749 		qunlock(&unit->ctl);
750 		decref(&sdev->r);
751 		nexterror();
752 	}
753 	pp = &unit->part[PART(c->qid)];
754 	if(unit->vers+pp->vers != c->qid.vers)
755 		error(Echange);
756 
757 	/*
758 	 * Check the request is within bounds.
759 	 * Removeable drives are locked throughout the I/O
760 	 * in case the media changes unexpectedly.
761 	 * Non-removeable drives are not locked during the I/O
762 	 * to allow the hardware to optimise if it can; this is
763 	 * a little fast and loose.
764 	 * It's assumed that non-removeable media parameters
765 	 * (sectors, secsize) can't change once the drive has
766 	 * been brought online.
767 	 */
768 	bno = (off/unit->secsize) + pp->start;
769 	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
770 	max = SDmaxio/unit->secsize;
771 	if(nb > max)
772 		nb = max;
773 	if(bno+nb > pp->end)
774 		nb = pp->end - bno;
775 	if(bno >= pp->end || nb == 0){
776 		if(write)
777 			error(Eio);
778 		qunlock(&unit->ctl);
779 		decref(&sdev->r);
780 		poperror();
781 		return 0;
782 	}
783 	if(!(unit->inquiry[1] & 0x80)){
784 		qunlock(&unit->ctl);
785 		poperror();
786 	}
787 
788 	b = sdmalloc(nb*unit->secsize);
789 	if(b == nil)
790 		error(Enomem);
791 	if(waserror()){
792 		sdfree(b);
793 		if(!(unit->inquiry[1] & 0x80))
794 			decref(&sdev->r);		/* gadverdamme! */
795 		nexterror();
796 	}
797 
798 	offset = off%unit->secsize;
799 	if(offset+len > nb*unit->secsize)
800 		len = nb*unit->secsize - offset;
801 	if(write){
802 		if(offset || (len%unit->secsize)){
803 			l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
804 			if(l < 0)
805 				error(Eio);
806 			if(l < (nb*unit->secsize)){
807 				nb = l/unit->secsize;
808 				l = nb*unit->secsize - offset;
809 				if(len > l)
810 					len = l;
811 			}
812 		}
813 		memmove(b+offset, a, len);
814 		l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
815 		if(l < 0)
816 			error(Eio);
817 		if(l < offset)
818 			len = 0;
819 		else if(len > l - offset)
820 			len = l - offset;
821 	}
822 	else{
823 		l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
824 		if(l < 0)
825 			error(Eio);
826 		if(l < offset)
827 			len = 0;
828 		else if(len > l - offset)
829 			len = l - offset;
830 		memmove(a, b+offset, len);
831 	}
832 	sdfree(b);
833 	poperror();
834 
835 	if(unit->inquiry[1] & 0x80){
836 		qunlock(&unit->ctl);
837 		poperror();
838 	}
839 
840 	decref(&sdev->r);
841 	return len;
842 }
843 
844 static long
845 sdrio(SDreq* r, void* a, long n)
846 {
847 	void *data;
848 
849 	if(n >= SDmaxio || n < 0)
850 		error(Etoobig);
851 
852 	data = nil;
853 	if(n){
854 		if((data = sdmalloc(n)) == nil)
855 			error(Enomem);
856 		if(r->write)
857 			memmove(data, a, n);
858 	}
859 	r->data = data;
860 	r->dlen = n;
861 
862 	if(waserror()){
863 		sdfree(data);
864 		r->data = nil;
865 		nexterror();
866 	}
867 
868 	if(r->unit->dev->ifc->rio(r) != SDok)
869 		error(Eio);
870 
871 	if(!r->write && r->rlen > 0)
872 		memmove(a, data, r->rlen);
873 	sdfree(data);
874 	r->data = nil;
875 	poperror();
876 
877 	return r->rlen;
878 }
879 
880 /*
881  * SCSI simulation for non-SCSI devices
882  */
883 int
884 sdsetsense(SDreq *r, int status, int key, int asc, int ascq)
885 {
886 	int len;
887 	SDunit *unit;
888 
889 	unit = r->unit;
890 	unit->sense[2] = key;
891 	unit->sense[12] = asc;
892 	unit->sense[13] = ascq;
893 
894 	r->status = status;
895 	if(status == SDcheck && !(r->flags & SDnosense)){
896 		/* request sense case from sdfakescsi */
897 		len = sizeof unit->sense;
898 		if(len > sizeof r->sense-1)
899 			len = sizeof r->sense-1;
900 		memmove(r->sense, unit->sense, len);
901 		unit->sense[2] = 0;
902 		unit->sense[12] = 0;
903 		unit->sense[13] = 0;
904 		r->flags |= SDvalidsense;
905 		return SDok;
906 	}
907 	return status;
908 }
909 
910 int
911 sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen)
912 {
913 	int len;
914 	uchar *data;
915 
916 	/*
917 	 * Fake a vendor-specific request with page code 0,
918 	 * return the drive info.
919 	 */
920 	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
921 		return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
922 	len = (cmd[7]<<8)|cmd[8];
923 	if(len == 0)
924 		return SDok;
925 	if(len < 8+ilen)
926 		return sdsetsense(r, SDcheck, 0x05, 0x1A, 0);
927 	if(r->data == nil || r->dlen < len)
928 		return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
929 	data = r->data;
930 	memset(data, 0, 8);
931 	data[0] = ilen>>8;
932 	data[1] = ilen;
933 	if(ilen)
934 		memmove(data+8, info, ilen);
935 	r->rlen = 8+ilen;
936 	return sdsetsense(r, SDok, 0, 0, 0);
937 }
938 
939 int
940 sdfakescsi(SDreq *r, void *info, int ilen)
941 {
942 	uchar *cmd, *p;
943 	uvlong len;
944 	SDunit *unit;
945 
946 	cmd = r->cmd;
947 	r->rlen = 0;
948 	unit = r->unit;
949 
950 	/*
951 	 * Rewrite read(6)/write(6) into read(10)/write(10).
952 	 */
953 	switch(cmd[0]){
954 	case 0x08:	/* read */
955 	case 0x0A:	/* write */
956 		cmd[9] = 0;
957 		cmd[8] = cmd[4];
958 		cmd[7] = 0;
959 		cmd[6] = 0;
960 		cmd[5] = cmd[3];
961 		cmd[4] = cmd[2];
962 		cmd[3] = cmd[1] & 0x0F;
963 		cmd[2] = 0;
964 		cmd[1] &= 0xE0;
965 		cmd[0] |= 0x20;
966 		break;
967 	}
968 
969 	/*
970 	 * Map SCSI commands into ATA commands for discs.
971 	 * Fail any command with a LUN except INQUIRY which
972 	 * will return 'logical unit not supported'.
973 	 */
974 	if((cmd[1]>>5) && cmd[0] != 0x12)
975 		return sdsetsense(r, SDcheck, 0x05, 0x25, 0);
976 
977 	switch(cmd[0]){
978 	default:
979 		return sdsetsense(r, SDcheck, 0x05, 0x20, 0);
980 
981 	case 0x00:	/* test unit ready */
982 		return sdsetsense(r, SDok, 0, 0, 0);
983 
984 	case 0x03:	/* request sense */
985 		if(cmd[4] < sizeof unit->sense)
986 			len = cmd[4];
987 		else
988 			len = sizeof unit->sense;
989 		if(r->data && r->dlen >= len){
990 			memmove(r->data, unit->sense, len);
991 			r->rlen = len;
992 		}
993 		return sdsetsense(r, SDok, 0, 0, 0);
994 
995 	case 0x12:	/* inquiry */
996 		if(cmd[4] < sizeof unit->inquiry)
997 			len = cmd[4];
998 		else
999 			len = sizeof unit->inquiry;
1000 		if(r->data && r->dlen >= len){
1001 			memmove(r->data, unit->inquiry, len);
1002 			r->rlen = len;
1003 		}
1004 		return sdsetsense(r, SDok, 0, 0, 0);
1005 
1006 	case 0x1B:	/* start/stop unit */
1007 		/*
1008 		 * nop for now, can use power management later.
1009 		 */
1010 		return sdsetsense(r, SDok, 0, 0, 0);
1011 
1012 	case 0x25:	/* read capacity */
1013 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
1014 			return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
1015 		if(r->data == nil || r->dlen < 8)
1016 			return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
1017 
1018 		/*
1019 		 * Read capacity returns the LBA of the last sector.
1020 		 */
1021 		len = unit->sectors - 1;
1022 		p = r->data;
1023 		*p++ = len>>24;
1024 		*p++ = len>>16;
1025 		*p++ = len>>8;
1026 		*p++ = len;
1027 		len = 512;
1028 		*p++ = len>>24;
1029 		*p++ = len>>16;
1030 		*p++ = len>>8;
1031 		*p++ = len;
1032 		r->rlen = p - (uchar*)r->data;
1033 		return sdsetsense(r, SDok, 0, 0, 0);
1034 
1035 	case 0x9E:	/* long read capacity */
1036 		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
1037 			return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
1038 		if(r->data == nil || r->dlen < 8)
1039 			return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
1040 		/*
1041 		 * Read capcity returns the LBA of the last sector.
1042 		 */
1043 		len = unit->sectors - 1;
1044 		p = r->data;
1045 		*p++ = len>>56;
1046 		*p++ = len>>48;
1047 		*p++ = len>>40;
1048 		*p++ = len>>32;
1049 		*p++ = len>>24;
1050 		*p++ = len>>16;
1051 		*p++ = len>>8;
1052 		*p++ = len;
1053 		len = 512;
1054 		*p++ = len>>24;
1055 		*p++ = len>>16;
1056 		*p++ = len>>8;
1057 		*p++ = len;
1058 		r->rlen = p - (uchar*)r->data;
1059 		return sdsetsense(r, SDok, 0, 0, 0);
1060 
1061 	case 0x5A:	/* mode sense */
1062 		return sdmodesense(r, cmd, info, ilen);
1063 
1064 	case 0x28:	/* read */
1065 	case 0x2A:	/* write */
1066 	case 0x88:	/* read16 */
1067 	case 0x8a:	/* write16 */
1068 		return SDnostatus;
1069 	}
1070 }
1071 
1072 static long
1073 sdread(Chan *c, void *a, long n, vlong off)
1074 {
1075 	char *p, *e, *buf;
1076 	SDpart *pp;
1077 	SDunit *unit;
1078 	SDev *sdev;
1079 	ulong offset;
1080 	int i, l, m, status;
1081 
1082 	offset = off;
1083 	switch(TYPE(c->qid)){
1084 	default:
1085 		error(Eperm);
1086 	case Qtopctl:
1087 		m = 64*1024;	/* room for register dumps */
1088 		p = buf = malloc(m);
1089 		assert(p);
1090 		e = p + m;
1091 		qlock(&devslock);
1092 		for(i = 0; i < nelem(devs); i++){
1093 			sdev = devs[i];
1094 			if(sdev && sdev->ifc->rtopctl)
1095 				p = sdev->ifc->rtopctl(sdev, p, e);
1096 		}
1097 		qunlock(&devslock);
1098 		n = readstr(off, a, n, buf);
1099 		free(buf);
1100 		return n;
1101 
1102 	case Qtopdir:
1103 	case Qunitdir:
1104 		return devdirread(c, a, n, 0, 0, sdgen);
1105 
1106 	case Qctl:
1107 		sdev = sdgetdev(DEV(c->qid));
1108 		if(sdev == nil)
1109 			error(Enonexist);
1110 
1111 		unit = sdev->unit[UNIT(c->qid)];
1112 		m = 16*1024;	/* room for register dumps */
1113 		p = malloc(m);
1114 		l = snprint(p, m, "inquiry %.48s\n",
1115 			(char*)unit->inquiry+8);
1116 		qlock(&unit->ctl);
1117 		/*
1118 		 * If there's a device specific routine it must
1119 		 * provide all information pertaining to night geometry
1120 		 * and the garscadden trains.
1121 		 */
1122 		if(unit->dev->ifc->rctl)
1123 			l += unit->dev->ifc->rctl(unit, p+l, m-l);
1124 		if(unit->sectors == 0)
1125 			sdinitpart(unit);
1126 		if(unit->sectors){
1127 			if(unit->dev->ifc->rctl == nil)
1128 				l += snprint(p+l, m-l,
1129 					"geometry %llud %lud\n",
1130 					unit->sectors, unit->secsize);
1131 			pp = unit->part;
1132 			for(i = 0; i < unit->npart; i++){
1133 				if(pp->valid)
1134 					l += snprint(p+l, m-l,
1135 						"part %s %llud %llud\n",
1136 						pp->name, pp->start, pp->end);
1137 				pp++;
1138 			}
1139 		}
1140 		qunlock(&unit->ctl);
1141 		decref(&sdev->r);
1142 		l = readstr(offset, a, n, p);
1143 		free(p);
1144 		return l;
1145 
1146 	case Qraw:
1147 		sdev = sdgetdev(DEV(c->qid));
1148 		if(sdev == nil)
1149 			error(Enonexist);
1150 
1151 		unit = sdev->unit[UNIT(c->qid)];
1152 		qlock(&unit->raw);
1153 		if(waserror()){
1154 			qunlock(&unit->raw);
1155 			decref(&sdev->r);
1156 			nexterror();
1157 		}
1158 		if(unit->state == Rawdata){
1159 			unit->state = Rawstatus;
1160 			i = sdrio(unit->req, a, n);
1161 		}
1162 		else if(unit->state == Rawstatus){
1163 			status = unit->req->status;
1164 			unit->state = Rawcmd;
1165 			free(unit->req);
1166 			unit->req = nil;
1167 			i = readnum(0, a, n, status, NUMSIZE);
1168 		} else
1169 			i = 0;
1170 		qunlock(&unit->raw);
1171 		decref(&sdev->r);
1172 		poperror();
1173 		return i;
1174 
1175 	case Qpart:
1176 		return sdbio(c, 0, a, n, off);
1177 	}
1178 }
1179 
1180 static void legacytopctl(Cmdbuf*);
1181 
1182 static long
1183 sdwrite(Chan* c, void* a, long n, vlong off)
1184 {
1185 	char *f0;
1186 	int i;
1187 	uvlong end, start;
1188 	Cmdbuf *cb;
1189 	SDifc *ifc;
1190 	SDreq *req;
1191 	SDunit *unit;
1192 	SDev *sdev;
1193 
1194 	switch(TYPE(c->qid)){
1195 	default:
1196 		error(Eperm);
1197 	case Qtopctl:
1198 		cb = parsecmd(a, n);
1199 		if(waserror()){
1200 			free(cb);
1201 			nexterror();
1202 		}
1203 		if(cb->nf == 0)
1204 			error("empty control message");
1205 		f0 = cb->f[0];
1206 		cb->f++;
1207 		cb->nf--;
1208 		if(strcmp(f0, "config") == 0){
1209 			/* wormhole into ugly legacy interface */
1210 			legacytopctl(cb);
1211 			poperror();
1212 			free(cb);
1213 			break;
1214 		}
1215 		/*
1216 		 * "ata arg..." invokes sdifc[i]->wtopctl(nil, cb),
1217 		 * where sdifc[i]->name=="ata" and cb contains the args.
1218 		 */
1219 		ifc = nil;
1220 		sdev = nil;
1221 		for(i=0; sdifc[i]; i++){
1222 			if(strcmp(sdifc[i]->name, f0) == 0){
1223 				ifc = sdifc[i];
1224 				sdev = nil;
1225 				goto subtopctl;
1226 			}
1227 		}
1228 		/*
1229 		 * "sd1 arg..." invokes sdifc[i]->wtopctl(sdev, cb),
1230 		 * where sdifc[i] and sdev match controller letter "1",
1231 		 * and cb contains the args.
1232 		 */
1233 		if(f0[0]=='s' && f0[1]=='d' && f0[2] && f0[3] == 0){
1234 			if((sdev = sdgetdev(f0[2])) != nil){
1235 				ifc = sdev->ifc;
1236 				goto subtopctl;
1237 			}
1238 		}
1239 		error("unknown interface");
1240 
1241 	subtopctl:
1242 		if(waserror()){
1243 			if(sdev)
1244 				decref(&sdev->r);
1245 			nexterror();
1246 		}
1247 		if(ifc->wtopctl)
1248 			ifc->wtopctl(sdev, cb);
1249 		else
1250 			error(Ebadctl);
1251 		poperror();
1252 		poperror();
1253 		decref(&sdev->r);
1254 		free(cb);
1255 		break;
1256 
1257 	case Qctl:
1258 		cb = parsecmd(a, n);
1259 		sdev = sdgetdev(DEV(c->qid));
1260 		if(sdev == nil)
1261 			error(Enonexist);
1262 		unit = sdev->unit[UNIT(c->qid)];
1263 
1264 		qlock(&unit->ctl);
1265 		if(waserror()){
1266 			qunlock(&unit->ctl);
1267 			decref(&sdev->r);
1268 			free(cb);
1269 			nexterror();
1270 		}
1271 		if(unit->vers != c->qid.vers)
1272 			error(Echange);
1273 
1274 		if(cb->nf < 1)
1275 			error(Ebadctl);
1276 		if(strcmp(cb->f[0], "part") == 0){
1277 			if(cb->nf != 4)
1278 				error(Ebadctl);
1279 			if(unit->sectors == 0 && !sdinitpart(unit))
1280 				error(Eio);
1281 			start = strtoull(cb->f[2], 0, 0);
1282 			end = strtoull(cb->f[3], 0, 0);
1283 			sdaddpart(unit, cb->f[1], start, end);
1284 		}
1285 		else if(strcmp(cb->f[0], "delpart") == 0){
1286 			if(cb->nf != 2 || unit->part == nil)
1287 				error(Ebadctl);
1288 			sddelpart(unit, cb->f[1]);
1289 		}
1290 		else if(unit->dev->ifc->wctl)
1291 			unit->dev->ifc->wctl(unit, cb);
1292 		else
1293 			error(Ebadctl);
1294 		qunlock(&unit->ctl);
1295 		decref(&sdev->r);
1296 		poperror();
1297 		free(cb);
1298 		break;
1299 
1300 	case Qraw:
1301 		sdev = sdgetdev(DEV(c->qid));
1302 		if(sdev == nil)
1303 			error(Enonexist);
1304 		unit = sdev->unit[UNIT(c->qid)];
1305 		qlock(&unit->raw);
1306 		if(waserror()){
1307 			qunlock(&unit->raw);
1308 			decref(&sdev->r);
1309 			nexterror();
1310 		}
1311 		switch(unit->state){
1312 		case Rawcmd:
1313 			if(n < 6 || n > sizeof(req->cmd))
1314 				error(Ebadarg);
1315 			if((req = malloc(sizeof(SDreq))) == nil)
1316 				error(Enomem);
1317 			req->unit = unit;
1318 			memmove(req->cmd, a, n);
1319 			req->clen = n;
1320 			req->flags = SDnosense;
1321 			req->status = ~0;
1322 
1323 			unit->req = req;
1324 			unit->state = Rawdata;
1325 			break;
1326 
1327 		case Rawstatus:
1328 			unit->state = Rawcmd;
1329 			free(unit->req);
1330 			unit->req = nil;
1331 			error(Ebadusefd);
1332 
1333 		case Rawdata:
1334 			unit->state = Rawstatus;
1335 			unit->req->write = 1;
1336 			n = sdrio(unit->req, a, n);
1337 		}
1338 		qunlock(&unit->raw);
1339 		decref(&sdev->r);
1340 		poperror();
1341 		break;
1342 	case Qpart:
1343 		return sdbio(c, 1, a, n, off);
1344 	}
1345 
1346 	return n;
1347 }
1348 
1349 static int
1350 sdwstat(Chan* c, uchar* dp, int n)
1351 {
1352 	Dir *d;
1353 	SDpart *pp;
1354 	SDperm *perm;
1355 	SDunit *unit;
1356 	SDev *sdev;
1357 
1358 	if(c->qid.type & QTDIR)
1359 		error(Eperm);
1360 
1361 	sdev = sdgetdev(DEV(c->qid));
1362 	if(sdev == nil)
1363 		error(Enonexist);
1364 	unit = sdev->unit[UNIT(c->qid)];
1365 	qlock(&unit->ctl);
1366 	d = nil;
1367 	if(waserror()){
1368 		free(d);
1369 		qunlock(&unit->ctl);
1370 		decref(&sdev->r);
1371 		nexterror();
1372 	}
1373 
1374 	switch(TYPE(c->qid)){
1375 	default:
1376 		error(Eperm);
1377 	case Qctl:
1378 		perm = &unit->ctlperm;
1379 		break;
1380 	case Qraw:
1381 		perm = &unit->rawperm;
1382 		break;
1383 	case Qpart:
1384 		pp = &unit->part[PART(c->qid)];
1385 		if(unit->vers+pp->vers != c->qid.vers)
1386 			error(Enonexist);
1387 		perm = &pp->SDperm;
1388 		break;
1389 	}
1390 
1391 	if(strcmp(up->user, perm->user) && !iseve())
1392 		error(Eperm);
1393 
1394 	d = smalloc(sizeof(Dir)+n);
1395 	n = convM2D(dp, n, &d[0], (char*)&d[1]);
1396 	if(n == 0)
1397 		error(Eshortstat);
1398 	if(!emptystr(d[0].uid))
1399 		kstrdup(&perm->user, d[0].uid);
1400 	if(d[0].mode != ~0UL)
1401 		perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
1402 
1403 	free(d);
1404 	qunlock(&unit->ctl);
1405 	decref(&sdev->r);
1406 	poperror();
1407 	return n;
1408 }
1409 
1410 static int
1411 configure(char* spec, DevConf* cf)
1412 {
1413 	SDev *s, *sdev;
1414 	char *p;
1415 	int i;
1416 
1417 	if(sdindex(*spec) < 0)
1418 		error("bad sd spec");
1419 
1420 	if((p = strchr(cf->type, '/')) != nil)
1421 		*p++ = '\0';
1422 
1423 	for(i = 0; sdifc[i] != nil; i++)
1424 		if(strcmp(sdifc[i]->name, cf->type) == 0)
1425 			break;
1426 	if(sdifc[i] == nil)
1427 		error("sd type not found");
1428 	if(p)
1429 		*(p-1) = '/';
1430 
1431 	if(sdifc[i]->probe == nil)
1432 		error("sd type cannot probe");
1433 
1434 	sdev = sdifc[i]->probe(cf);
1435 	for(s=sdev; s; s=s->next)
1436 		s->idno = *spec;
1437 	sdadddevs(sdev);
1438 	return 0;
1439 }
1440 
1441 static int
1442 unconfigure(char* spec)
1443 {
1444 	int i;
1445 	SDev *sdev;
1446 	SDunit *unit;
1447 
1448 	if((i = sdindex(*spec)) < 0)
1449 		error(Enonexist);
1450 
1451 	qlock(&devslock);
1452 	if((sdev = devs[i]) == nil){
1453 		qunlock(&devslock);
1454 		error(Enonexist);
1455 	}
1456 	if(sdev->r.ref){
1457 		qunlock(&devslock);
1458 		error(Einuse);
1459 	}
1460 	devs[i] = nil;
1461 	qunlock(&devslock);
1462 
1463 	/* make sure no interrupts arrive anymore before removing resources */
1464 	if(sdev->enabled && sdev->ifc->disable)
1465 		sdev->ifc->disable(sdev);
1466 
1467 	for(i = 0; i != sdev->nunit; i++){
1468 		if(unit = sdev->unit[i]){
1469 			free(unit->name);
1470 			free(unit->user);
1471 			free(unit);
1472 		}
1473 	}
1474 
1475 	if(sdev->ifc->clear)
1476 		sdev->ifc->clear(sdev);
1477 	free(sdev);
1478 	return 0;
1479 }
1480 
1481 static int
1482 sdconfig(int on, char* spec, DevConf* cf)
1483 {
1484 	if(on)
1485 		return configure(spec, cf);
1486 	return unconfigure(spec);
1487 }
1488 
1489 Dev sddevtab = {
1490 	'S',
1491 	"sd",
1492 
1493 	sdreset,
1494 	devinit,
1495 	devshutdown,
1496 	sdattach,
1497 	sdwalk,
1498 	sdstat,
1499 	sdopen,
1500 	devcreate,
1501 	sdclose,
1502 	sdread,
1503 	devbread,
1504 	sdwrite,
1505 	devbwrite,
1506 	devremove,
1507 	sdwstat,
1508 	devpower,
1509 	sdconfig,
1510 };
1511 
1512 /*
1513  * This is wrong for so many reasons.  This code must go.
1514  */
1515 typedef struct Confdata Confdata;
1516 struct Confdata {
1517 	int	on;
1518 	char*	spec;
1519 	DevConf	cf;
1520 };
1521 
1522 static void
1523 parseswitch(Confdata* cd, char* option)
1524 {
1525 	if(!strcmp("on", option))
1526 		cd->on = 1;
1527 	else if(!strcmp("off", option))
1528 		cd->on = 0;
1529 	else
1530 		error(Ebadarg);
1531 }
1532 
1533 static void
1534 parsespec(Confdata* cd, char* option)
1535 {
1536 	if(strlen(option) > 1)
1537 		error(Ebadarg);
1538 	cd->spec = option;
1539 }
1540 
1541 static Devport*
1542 getnewport(DevConf* dc)
1543 {
1544 	Devport *p;
1545 
1546 	p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport));
1547 	if(dc->nports > 0){
1548 		memmove(p, dc->ports, dc->nports * sizeof(Devport));
1549 		free(dc->ports);
1550 	}
1551 	dc->ports = p;
1552 	p = &dc->ports[dc->nports++];
1553 	p->size = -1;
1554 	p->port = (ulong)-1;
1555 	return p;
1556 }
1557 
1558 static void
1559 parseport(Confdata* cd, char* option)
1560 {
1561 	char *e;
1562 	Devport *p;
1563 
1564 	if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1)
1565 		p = getnewport(&cd->cf);
1566 	else
1567 		p = &cd->cf.ports[cd->cf.nports-1];
1568 	p->port = strtol(option, &e, 0);
1569 	if(e == nil || *e != '\0')
1570 		error(Ebadarg);
1571 }
1572 
1573 static void
1574 parsesize(Confdata* cd, char* option)
1575 {
1576 	char *e;
1577 	Devport *p;
1578 
1579 	if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1)
1580 		p = getnewport(&cd->cf);
1581 	else
1582 		p = &cd->cf.ports[cd->cf.nports-1];
1583 	p->size = (int)strtol(option, &e, 0);
1584 	if(e == nil || *e != '\0')
1585 		error(Ebadarg);
1586 }
1587 
1588 static void
1589 parseirq(Confdata* cd, char* option)
1590 {
1591 	char *e;
1592 
1593 	cd->cf.intnum = strtoul(option, &e, 0);
1594 	if(e == nil || *e != '\0')
1595 		error(Ebadarg);
1596 }
1597 
1598 static void
1599 parsetype(Confdata* cd, char* option)
1600 {
1601 	cd->cf.type = option;
1602 }
1603 
1604 static struct {
1605 	char	*name;
1606 	void	(*parse)(Confdata*, char*);
1607 } options[] = {
1608 	"switch",	parseswitch,
1609 	"spec",		parsespec,
1610 	"port",		parseport,
1611 	"size",		parsesize,
1612 	"irq",		parseirq,
1613 	"type",		parsetype,
1614 };
1615 
1616 static void
1617 legacytopctl(Cmdbuf *cb)
1618 {
1619 	char *opt;
1620 	int i, j;
1621 	Confdata cd;
1622 
1623 	memset(&cd, 0, sizeof cd);
1624 	cd.on = -1;
1625 	for(i=0; i<cb->nf; i+=2){
1626 		if(i+2 > cb->nf)
1627 			error(Ebadarg);
1628 		opt = cb->f[i];
1629 		for(j=0; j<nelem(options); j++)
1630 			if(strcmp(opt, options[j].name) == 0){
1631 				options[j].parse(&cd, cb->f[i+1]);
1632 				break;
1633 			}
1634 		if(j == nelem(options))
1635 			error(Ebadarg);
1636 	}
1637 	/* this has been rewritten to accomodate sdaoe */
1638 	if(cd.on < 0 || cd.spec == 0)
1639 		error(Ebadarg);
1640 	if(cd.on && cd.cf.type == nil)
1641 		error(Ebadarg);
1642 	sdconfig(cd.on, cd.spec, &cd.cf);
1643 }
1644