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