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