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