1 #include <u.h> 2 #include <libc.h> 3 #include <disk.h> 4 #include "dat.h" 5 #include "fns.h" 6 7 enum 8 { 9 Pagesz = 255, 10 }; 11 12 static Dev mmcdev; 13 14 typedef struct Mmcaux Mmcaux; 15 struct Mmcaux { 16 uchar page05[Pagesz]; 17 int page05ok; 18 19 int pagecmdsz; 20 ulong mmcnwa; 21 22 int nropen; 23 int nwopen; 24 long ntotby; 25 long ntotbk; 26 }; 27 28 static ulong 29 bige(void *p) 30 { 31 uchar *a; 32 33 a = p; 34 return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); 35 } 36 37 static ushort 38 biges(void *p) 39 { 40 uchar *a; 41 42 a = p; 43 return (a[0]<<8) | a[1]; 44 } 45 46 static void 47 hexdump(void *v, int n) 48 { 49 int i; 50 uchar *p; 51 52 p = v; 53 for(i=0; i<n; i++){ 54 print("%.2ux ", p[i]); 55 if((i%8) == 7) 56 print("\n"); 57 } 58 if(i%8) 59 print("\n"); 60 } 61 62 static int 63 mmcgetpage10(Drive *drive, int page, void *v) 64 { 65 uchar cmd[10], resp[512]; 66 int n, r; 67 68 memset(cmd, 0, sizeof(cmd)); 69 cmd[0] = 0x5A; 70 cmd[2] = page; 71 cmd[8] = 255; 72 n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 73 if(n < 8) 74 return -1; 75 76 r = (resp[6]<<8) | resp[7]; 77 n -= 8+r; 78 79 if(n < 0) 80 return -1; 81 if(n > Pagesz) 82 n = Pagesz; 83 84 memmove(v, &resp[8+r], n); 85 return n; 86 } 87 88 static int 89 mmcgetpage6(Drive *drive, int page, void *v) 90 { 91 uchar cmd[6], resp[512]; 92 int n; 93 94 memset(cmd, 0, sizeof(cmd)); 95 cmd[0] = 0x1A; 96 cmd[2] = page; 97 cmd[4] = 255; 98 n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 99 if(n < 4) 100 return -1; 101 102 n -= 4+resp[3]; 103 if(n < 0) 104 return -1; 105 if(n > Pagesz) 106 n = Pagesz; 107 108 memmove(v, &resp[4+resp[3]], n); 109 return n; 110 } 111 112 static int 113 mmcsetpage10(Drive *drive, int page, void *v) 114 { 115 uchar cmd[10], *p, *pagedata; 116 int len, n; 117 118 pagedata = v; 119 assert(pagedata[0] == page); 120 len = 8+2+pagedata[1]; 121 p = emalloc(len); 122 memmove(p+8, pagedata, pagedata[1]); 123 memset(cmd, 0, sizeof(cmd)); 124 cmd[0] = 0x55; 125 cmd[1] = 0x10; 126 cmd[8] = len; 127 128 // print("cmd\n"); 129 // hexdump(cmd, 10); 130 // print("page\n"); 131 // hexdump(p, len); 132 133 n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite); 134 free(p); 135 if(n < len) 136 return -1; 137 return 0; 138 } 139 140 static int 141 mmcsetpage6(Drive *drive, int page, void *v) 142 { 143 uchar cmd[6], *p, *pagedata; 144 int len, n; 145 146 pagedata = v; 147 assert(pagedata[0] == page); 148 len = 4+2+pagedata[1]; 149 p = emalloc(len); 150 memmove(p+4, pagedata, pagedata[1]); 151 memset(cmd, 0, sizeof(cmd)); 152 cmd[0] = 0x15; 153 cmd[1] = 0x10; 154 cmd[4] = len; 155 156 n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite); 157 free(p); 158 if(n < len) 159 return -1; 160 return 0; 161 } 162 163 static int 164 mmcgetpage(Drive *drive, int page, void *v) 165 { 166 Mmcaux *aux; 167 168 aux = drive->aux; 169 switch(aux->pagecmdsz) { 170 case 10: 171 return mmcgetpage10(drive, page, v); 172 case 6: 173 return mmcgetpage6(drive, page, v); 174 default: 175 assert(0); 176 } 177 return -1; 178 } 179 180 static int 181 mmcsetpage(Drive *drive, int page, void *v) 182 { 183 Mmcaux *aux; 184 185 aux = drive->aux; 186 switch(aux->pagecmdsz) { 187 case 10: 188 return mmcsetpage10(drive, page, v); 189 case 6: 190 return mmcsetpage6(drive, page, v); 191 default: 192 assert(0); 193 } 194 return -1; 195 } 196 197 int 198 mmcstatus(Drive *drive) 199 { 200 uchar cmd[12]; 201 202 memset(cmd, 0, sizeof(cmd)); 203 cmd[0] = 0xBD; 204 return scsi(drive, cmd, sizeof(cmd), nil, 0, Sread); 205 } 206 207 void 208 mmcgetspeed(Drive *drive) 209 { 210 int n, maxread, curread, maxwrite, curwrite; 211 uchar buf[Pagesz]; 212 213 n = mmcgetpage(drive, 0x2A, buf); 214 maxread = (buf[8]<<8)|buf[9]; 215 curread = (buf[14]<<8)|buf[15]; 216 maxwrite = (buf[18]<<8)|buf[19]; 217 curwrite = (buf[20]<<8)|buf[21]; 218 219 if(n < 22 || (maxread && maxread < 170) || (curread && curread < 170)) 220 return; /* bogus data */ 221 222 drive->readspeed = curread; 223 drive->writespeed = curwrite; 224 drive->maxreadspeed = maxread; 225 drive->maxwritespeed = maxwrite; 226 } 227 228 Drive* 229 mmcprobe(Scsi *scsi) 230 { 231 Mmcaux *aux; 232 Drive *drive; 233 uchar buf[Pagesz]; 234 int cap; 235 236 /* BUG: confirm mmc better? */ 237 238 drive = emalloc(sizeof(Drive)); 239 drive->Scsi = *scsi; 240 drive->Dev = mmcdev; 241 aux = emalloc(sizeof(Mmcaux)); 242 drive->aux = aux; 243 244 /* attempt to read CD capabilities page */ 245 if(mmcgetpage10(drive, 0x2A, buf) >= 0) 246 aux->pagecmdsz = 10; 247 else if(mmcgetpage6(drive, 0x2A, buf) >= 0) 248 aux->pagecmdsz = 6; 249 else { 250 werrstr("not an mmc device"); 251 free(drive); 252 return nil; 253 } 254 255 cap = 0; 256 if(buf[3] & 3) /* 2=cdrw, 1=cdr */ 257 cap |= Cwrite; 258 if(buf[5] & 1) 259 cap |= Ccdda; 260 261 // print("read %d max %d\n", biges(buf+14), biges(buf+8)); 262 // print("write %d max %d\n", biges(buf+20), biges(buf+18)); 263 264 /* cache page 05 (write parameter page) */ 265 if((cap & Cwrite) && mmcgetpage(drive, 0x05, aux->page05) >= 0) 266 aux->page05ok = 1; 267 else 268 cap &= ~Cwrite; 269 270 drive->cap = cap; 271 272 mmcgetspeed(drive); 273 return drive; 274 } 275 276 static int 277 mmctrackinfo(Drive *drive, int t, int i) 278 { 279 uchar cmd[10], resp[255]; 280 int n, type, bs; 281 uchar tmode; 282 ulong beg, size; 283 Mmcaux *aux; 284 285 aux = drive->aux; 286 memset(cmd, 0, sizeof(cmd)); 287 cmd[0] = 0x52; /* get track info */ 288 cmd[1] = 1; 289 cmd[2] = t>>24; 290 cmd[3] = t>>16; 291 cmd[4] = t>>8; 292 cmd[5] = t; 293 cmd[7] = sizeof(resp)>>8; 294 cmd[8] = sizeof(resp); 295 n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 296 if(n < 28) { 297 if(vflag) 298 print("trackinfo %d fails n=%d %r\n", t, n); 299 return -1; 300 } 301 302 beg = bige(&resp[8]); 303 size = bige(&resp[24]); 304 305 tmode = resp[5] & 0x0D; 306 // dmode = resp[6] & 0x0F; 307 308 if(vflag) 309 print("track %d type 0x%x\n", t, tmode); 310 type = TypeNone; 311 bs = BScdda; 312 switch(tmode){ 313 case 0: 314 type = TypeAudio; 315 bs = BScdda; 316 break; 317 case 1: /* 2 audio channels, with pre-emphasis 50/15 μs */ 318 if(vflag) 319 print("audio channels with preemphasis on track %d (u%.3d)\n", t, i); 320 type = TypeNone; 321 break; 322 case 4: /* data track, recorded uninterrupted */ 323 type = TypeData; 324 bs = BScdrom; 325 break; 326 case 5: /* data track, recorded interrupted */ 327 default: 328 if(vflag) 329 print("unknown track type %d\n", tmode); 330 } 331 332 drive->track[i].mtime = drive->changetime; 333 drive->track[i].beg = beg; 334 drive->track[i].end = beg+size; 335 drive->track[i].type = type; 336 drive->track[i].bs = bs; 337 drive->track[i].size = (vlong)(size-2)*bs; /* -2: skip lead out */ 338 339 if(resp[6] & (1<<6)) { 340 drive->track[i].type = TypeBlank; 341 drive->writeok = 1; 342 } 343 344 if(t == 0xFF) 345 aux->mmcnwa = bige(&resp[12]); 346 347 return 0; 348 } 349 350 static int 351 mmcreadtoc(Drive *drive, int type, int track, void *data, int nbytes) 352 { 353 uchar cmd[10]; 354 355 memset(cmd, 0, sizeof(cmd)); 356 cmd[0] = 0x43; 357 cmd[1] = type; 358 cmd[6] = track; 359 cmd[7] = nbytes>>8; 360 cmd[8] = nbytes; 361 362 return scsi(drive, cmd, sizeof(cmd), data, nbytes, Sread); 363 } 364 365 static int 366 mmcreaddiscinfo(Drive *drive, void *data, int nbytes) 367 { 368 uchar cmd[10]; 369 int n; 370 371 memset(cmd, 0, sizeof(cmd)); 372 cmd[0] = 0x51; 373 cmd[7] = nbytes>>8; 374 cmd[8] = nbytes; 375 n = scsi(drive, cmd, sizeof(cmd), data, nbytes, Sread); 376 if(n < 24) { 377 if(n >= 0) 378 werrstr("rdiscinfo returns %d", n); 379 return -1; 380 } 381 382 return n; 383 } 384 385 static Msf 386 rdmsf(uchar *p) 387 { 388 Msf msf; 389 390 msf.m = p[0]; 391 msf.s = p[1]; 392 msf.f = p[2]; 393 return msf; 394 } 395 396 static int 397 mmcgettoc(Drive *drive) 398 { 399 uchar resp[1024]; 400 int i, n, first, last; 401 ulong tot; 402 Track *t; 403 404 /* 405 * if someone has swapped the cd, 406 * mmcreadtoc will get ``medium changed'' and the 407 * scsi routines will set nchange and changetime in the 408 * scsi device. 409 */ 410 mmcreadtoc(drive, 0, 0, resp, sizeof(resp)); 411 if(drive->Scsi.changetime == 0) { /* no media present */ 412 drive->ntrack = 0; 413 return 0; 414 } 415 416 if(drive->nchange == drive->Scsi.nchange && drive->changetime != 0) 417 return 0; 418 419 drive->ntrack = 0; 420 drive->nameok = 0; 421 drive->nchange = drive->Scsi.nchange; 422 drive->changetime = drive->Scsi.changetime; 423 drive->writeok = 0; 424 425 for(i=0; i<nelem(drive->track); i++){ 426 memset(&drive->track[i].mbeg, 0, sizeof(Msf)); 427 memset(&drive->track[i].mend, 0, sizeof(Msf)); 428 } 429 430 /* 431 * find number of tracks 432 */ 433 if((n=mmcreadtoc(drive, 0x02, 0, resp, sizeof(resp))) < 4) { 434 /* 435 * on a blank disc in a cd-rw, use readdiscinfo 436 * to find the track info. 437 */ 438 if(mmcreaddiscinfo(drive, resp, sizeof(resp)) < 0) 439 return -1; 440 if(resp[4] != 1) 441 print("multi-session disc %d\n", resp[4]); 442 first = resp[3]; 443 last = resp[6]; 444 if(vflag) 445 print("blank disc %d %d\n", first, last); 446 drive->writeok = 1; 447 } else { 448 first = resp[2]; 449 last = resp[3]; 450 451 if(n >= 4+8*(last-first+2)) { 452 for(i=0; i<=last-first+1; i++) /* <=: track[last-first+1] = end */ 453 drive->track[i].mbeg = rdmsf(resp+4+i*8+5); 454 for(i=0; i<last-first+1; i++) 455 drive->track[i].mend = drive->track[i+1].mbeg; 456 } 457 } 458 459 if(vflag) 460 print("first %d last %d\n", first, last); 461 462 if(first == 0 && last == 0) 463 first = 1; 464 465 if(first <= 0 || first >= Maxtrack) { 466 werrstr("first table %d not in range", first); 467 return -1; 468 } 469 if(last <= 0 || last >= Maxtrack) { 470 werrstr("last table %d not in range", last); 471 return -1; 472 } 473 474 if(drive->cap & Cwrite) { /* CDR drives are easy */ 475 for(i = first; i <= last; i++) 476 mmctrackinfo(drive, i, i-first); 477 } else { 478 /* 479 * otherwise we need to infer endings from the 480 * beginnings of other tracks. 481 */ 482 for(i = first; i <= last; i++) { 483 memset(resp, 0, sizeof(resp)); 484 if(mmcreadtoc(drive, 0x00, i, resp, sizeof(resp)) < 0) 485 break; 486 t = &drive->track[i-first]; 487 t->mtime = drive->changetime; 488 t->type = TypeData; 489 t->bs = BScdrom; 490 t->beg = bige(resp+8); 491 if(!(resp[5] & 4)) { 492 t->type = TypeAudio; 493 t->bs = BScdda; 494 } 495 } 496 497 if((long)drive->track[0].beg < 0) /* i've seen negative track 0's */ 498 drive->track[0].beg = 0; 499 500 tot = 0; 501 memset(resp, 0, sizeof(resp)); 502 if(mmcreadtoc(drive, 0x00, 0xAA, resp, sizeof(resp)) < 0) 503 print("bad\n"); 504 if(resp[6]) 505 tot = bige(resp+8); 506 507 for(i=last; i>=first; i--) { 508 t = &drive->track[i-first]; 509 t->end = tot; 510 tot = t->beg; 511 if(t->end <= t->beg) { 512 t->beg = 0; 513 t->end = 0; 514 } 515 t->size = (t->end - t->beg - 2) * (vlong)t->bs; /* -2: skip lead out */ 516 } 517 } 518 519 drive->firsttrack = first; 520 drive->ntrack = last+1-first; 521 return 0; 522 } 523 524 static int 525 mmcsetbs(Drive *drive, int bs) 526 { 527 uchar *p; 528 Mmcaux *aux; 529 530 aux = drive->aux; 531 532 assert(aux->page05ok); 533 534 p = aux->page05; 535 p[2] = 0x01; /* track-at-once */ 536 // if(xflag) 537 // p[2] |= 0x10; /* test-write */ 538 539 switch(bs){ 540 case BScdrom: 541 p[3] = (p[3] & ~0x07)|0x04; /* data track, uninterrupted */ 542 p[4] = 0x08; /* mode 1 CD-ROM */ 543 p[8] = 0; /* session format CD-DA or CD-ROM */ 544 break; 545 546 case BScdda: 547 p[3] = (p[3] & ~0x07)|0x00; /* 2 audio channels without pre-emphasis */ 548 p[4] = 0x00; /* raw data */ 549 p[8] = 0; /* session format CD-DA or CD-ROM */ 550 break; 551 552 case BScdxa: 553 p[3] = (p[3] & ~0x07)|0x04; /* data track, uninterrupted */ 554 p[4] = 0x09; /* mode 2 */ 555 p[8] = 0x20; /* session format CD-ROM XA */ 556 break; 557 558 default: 559 assert(0); 560 } 561 562 if(mmcsetpage(drive, 0x05, p) < 0) 563 return -1; 564 return 0; 565 } 566 567 static long 568 mmcread(Buf *buf, void *v, long nblock, long off) 569 { 570 Drive *drive; 571 int bs; 572 uchar cmd[12]; 573 long n, nn; 574 Otrack *o; 575 576 o = buf->otrack; 577 drive = o->drive; 578 bs = o->track->bs; 579 off += o->track->beg; 580 581 if(nblock >= (1<<10)) { 582 werrstr("mmcread too big"); 583 if(vflag) 584 fprint(2, "mmcread too big\n"); 585 return -1; 586 } 587 588 /* truncate nblock modulo size of track */ 589 if(off > o->track->end - 2) { 590 werrstr("read past end of track"); 591 if(vflag) 592 fprint(2, "end of track (%ld->%ld off %ld)", o->track->beg, o->track->end-2, off); 593 return -1; 594 } 595 if(off == o->track->end - 2) 596 return 0; 597 598 if(off+nblock > o->track->end - 2) 599 nblock = o->track->end - 2 - off; 600 601 memset(cmd, 0, sizeof(cmd)); 602 cmd[0] = 0xBE; 603 cmd[2] = off>>24; 604 cmd[3] = off>>16; 605 cmd[4] = off>>8; 606 cmd[5] = off>>0; 607 cmd[6] = nblock>>16; 608 cmd[7] = nblock>>8; 609 cmd[8] = nblock>>0; 610 cmd[9] = 0x10; 611 612 switch(bs){ 613 case BScdda: 614 cmd[1] = 0x04; 615 break; 616 617 case BScdrom: 618 cmd[1] = 0x08; 619 break; 620 621 case BScdxa: 622 cmd[1] = 0x0C; 623 break; 624 625 default: 626 werrstr("unknown bs %d", bs); 627 return -1; 628 } 629 630 n = nblock*bs; 631 nn = scsi(drive, cmd, sizeof(cmd), v, n, Sread); 632 if(nn != n) { 633 if(nn != -1) 634 werrstr("short read %ld/%ld", nn, n); 635 if(vflag) 636 print("read off %lud nblock %ld bs %d failed\n", off, nblock, bs); 637 return -1; 638 } 639 640 return nblock; 641 } 642 643 static Otrack* 644 mmcopenrd(Drive *drive, int trackno) 645 { 646 Otrack *o; 647 Mmcaux *aux; 648 649 if(trackno < 0 || trackno >= drive->ntrack) { 650 werrstr("track number out of range"); 651 return nil; 652 } 653 654 aux = drive->aux; 655 if(aux->nwopen) { 656 werrstr("disk in use for writing"); 657 return nil; 658 } 659 660 o = emalloc(sizeof(Otrack)); 661 o->drive = drive; 662 o->track = &drive->track[trackno]; 663 o->nchange = drive->nchange; 664 o->omode = OREAD; 665 o->buf = bopen(mmcread, OREAD, o->track->bs, Nblock); 666 o->buf->otrack = o; 667 668 aux->nropen++; 669 670 return o; 671 } 672 673 static long 674 mmcxwrite(Otrack *o, void *v, long nblk) 675 { 676 uchar cmd[10]; 677 Mmcaux *aux; 678 679 assert(o->omode == OWRITE); 680 681 aux = o->drive->aux; 682 aux->ntotby += nblk*o->track->bs; 683 aux->ntotbk += nblk; 684 memset(cmd, 0, sizeof(cmd)); 685 cmd[0] = 0x2a; /* write */ 686 cmd[2] = aux->mmcnwa>>24; 687 cmd[3] = aux->mmcnwa>>16; 688 cmd[4] = aux->mmcnwa>>8; 689 cmd[5] = aux->mmcnwa; 690 cmd[7] = nblk>>8; 691 cmd[8] = nblk>>0; 692 if(vflag) 693 print("%lld: write %ld at 0x%lux\n", nsec(), nblk, aux->mmcnwa); 694 aux->mmcnwa += nblk; 695 return scsi(o->drive, cmd, sizeof(cmd), v, nblk*o->track->bs, Swrite); 696 } 697 698 static long 699 mmcwrite(Buf *buf, void *v, long nblk, long) 700 { 701 return mmcxwrite(buf->otrack, v, nblk); 702 } 703 704 static Otrack* 705 mmccreate(Drive *drive, int type) 706 { 707 int bs; 708 Mmcaux *aux; 709 Track *t; 710 Otrack *o; 711 712 aux = drive->aux; 713 714 if(aux->nropen || aux->nwopen) { 715 werrstr("drive in use"); 716 return nil; 717 } 718 719 switch(type){ 720 case TypeAudio: 721 bs = BScdda; 722 break; 723 case TypeData: 724 bs = BScdrom; 725 break; 726 default: 727 werrstr("bad type %d", type); 728 return nil; 729 } 730 731 if(mmctrackinfo(drive, 0xFF, Maxtrack)) { /* the invisible track */ 732 werrstr("CD not writable"); 733 return nil; 734 } 735 if(mmcsetbs(drive, bs) < 0) { 736 werrstr("cannot set bs mode"); 737 return nil; 738 } 739 if(mmctrackinfo(drive, 0xFF, Maxtrack)) { /* the invisible track */ 740 werrstr("CD not writable 2"); 741 return nil; 742 } 743 744 aux->ntotby = 0; 745 aux->ntotbk = 0; 746 747 t = &drive->track[drive->ntrack++]; 748 t->size = 0; 749 t->bs = bs; 750 t->beg = aux->mmcnwa; 751 t->end = 0; 752 t->type = type; 753 drive->nameok = 0; 754 755 o = emalloc(sizeof(Otrack)); 756 o->drive = drive; 757 o->nchange = drive->nchange; 758 o->omode = OWRITE; 759 o->track = t; 760 o->buf = bopen(mmcwrite, OWRITE, bs, Nblock); 761 o->buf->otrack = o; 762 763 aux->nwopen++; 764 765 if(vflag) 766 print("mmcinit: nwa = 0x%luX\n", aux->mmcnwa); 767 768 return o; 769 } 770 771 void 772 mmcsynccache(Drive *drive) 773 { 774 uchar cmd[10]; 775 Mmcaux *aux; 776 777 memset(cmd, 0, sizeof(cmd)); 778 cmd[0] = 0x35; /* flush */ 779 scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 780 if(vflag) { 781 aux = drive->aux; 782 print("mmcsynccache: bytes = %ld blocks = %ld, mmcnwa 0x%luX\n", 783 aux->ntotby, aux->ntotbk, aux->mmcnwa); 784 } 785 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 786 } 787 788 static void 789 mmcclose(Otrack *o) 790 { 791 Mmcaux *aux; 792 static uchar zero[2*BSmax]; 793 794 aux = o->drive->aux; 795 if(o->omode == OREAD) 796 aux->nropen--; 797 else if(o->omode == OWRITE) { 798 aux->nwopen--; 799 mmcxwrite(o, zero, 2); /* write lead out */ 800 mmcsynccache(o->drive); 801 o->drive->nchange = -1; /* force reread toc */ 802 } 803 free(o); 804 } 805 806 static int 807 mmcxclose(Drive *drive, int ts, int trackno) 808 { 809 uchar cmd[10]; 810 811 /* 812 * ts: 1 == track, 2 == session 813 */ 814 memset(cmd, 0, sizeof(cmd)); 815 cmd[0] = 0x5B; 816 cmd[2] = ts; 817 if(ts == 1) 818 cmd[5] = trackno; 819 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 820 } 821 822 static int 823 mmcfixate(Drive *drive) 824 { 825 uchar *p; 826 Mmcaux *aux; 827 828 if((drive->cap & Cwrite) == 0) { 829 werrstr("not a writer"); 830 return -1; 831 } 832 833 drive->nchange = -1; /* force reread toc */ 834 835 aux = drive->aux; 836 p = aux->page05; 837 p[3] = (p[3] & ~0xC0); 838 if(mmcsetpage(drive, 0x05, p) < 0) 839 return -1; 840 841 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 842 return mmcxclose(drive, 0x02, 0); 843 } 844 845 static int 846 mmcsession(Drive *drive) 847 { 848 uchar *p; 849 Mmcaux *aux; 850 851 drive->nchange = -1; /* force reread toc */ 852 853 aux = drive->aux; 854 p = aux->page05; 855 p[3] = (p[3] & ~0xC0); 856 if(mmcsetpage(drive, 0x05, p) < 0) 857 return -1; 858 859 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 860 return mmcxclose(drive, 0x02, 0); 861 } 862 863 static int 864 mmcblank(Drive *drive, int quick) 865 { 866 uchar cmd[12]; 867 868 drive->nchange = -1; /* force reread toc */ 869 870 memset(cmd, 0, sizeof(cmd)); 871 cmd[0] = 0xA1; /* blank */ 872 /* cmd[1] = 0 means blank the whole disc; = 1 just the header */ 873 cmd[1] = quick ? 0x01 : 0x00; 874 875 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 876 } 877 878 static int 879 start(Drive *drive, int code) 880 { 881 uchar cmd[6]; 882 883 memset(cmd, 0, sizeof(cmd)); 884 cmd[0] = 0x1B; 885 cmd[4] = code; 886 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 887 } 888 889 static char* 890 e(int status) 891 { 892 if(status < 0) 893 return geterrstr(); 894 return nil; 895 } 896 897 static char* 898 mmcctl(Drive *drive, int argc, char **argv) 899 { 900 if(argc < 1) 901 return nil; 902 903 if(strcmp(argv[0], "blank") == 0) 904 return e(mmcblank(drive, 0)); 905 if(strcmp(argv[0], "quickblank") == 0) 906 return e(mmcblank(drive, 1)); 907 if(strcmp(argv[0], "eject") == 0) 908 return e(start(drive, 2)); 909 if(strcmp(argv[0], "ingest") == 0) 910 return e(start(drive, 3)); 911 return "bad arg"; 912 } 913 914 static char* 915 mmcsetspeed(Drive *drive, int r, int w) 916 { 917 char *rv; 918 uchar cmd[12]; 919 920 memset(cmd, 0, sizeof(cmd)); 921 cmd[0] = 0xBB; 922 cmd[2] = r>>8; 923 cmd[3] = r; 924 cmd[4] = w>>8; 925 cmd[5] = w; 926 rv = e(scsi(drive, cmd, sizeof(cmd), nil, 0, Snone)); 927 mmcgetspeed(drive); 928 return rv; 929 } 930 931 static Dev mmcdev = { 932 mmcopenrd, 933 mmccreate, 934 bufread, 935 bufwrite, 936 mmcclose, 937 mmcgettoc, 938 mmcfixate, 939 mmcctl, 940 mmcsetspeed, 941 }; 942