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