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 } 565 566 static long 567 mmcread(Buf *buf, void *v, long nblock, long off) 568 { 569 Drive *drive; 570 int bs; 571 uchar cmd[12]; 572 long n, nn; 573 Otrack *o; 574 575 o = buf->otrack; 576 drive = o->drive; 577 bs = o->track->bs; 578 off += o->track->beg; 579 580 if(nblock >= (1<<10)) { 581 werrstr("mmcread too big"); 582 if(vflag) 583 fprint(2, "mmcread too big\n"); 584 return -1; 585 } 586 587 /* truncate nblock modulo size of track */ 588 if(off > o->track->end - 2) { 589 werrstr("read past end of track"); 590 if(vflag) 591 fprint(2, "end of track (%ld->%ld off %ld)", o->track->beg, o->track->end-2, off); 592 return -1; 593 } 594 if(off == o->track->end - 2) 595 return 0; 596 597 if(off+nblock > o->track->end - 2) 598 nblock = o->track->end - 2 - off; 599 600 memset(cmd, 0, sizeof(cmd)); 601 cmd[0] = 0xBE; 602 cmd[2] = off>>24; 603 cmd[3] = off>>16; 604 cmd[4] = off>>8; 605 cmd[5] = off>>0; 606 cmd[6] = nblock>>16; 607 cmd[7] = nblock>>8; 608 cmd[8] = nblock>>0; 609 cmd[9] = 0x10; 610 611 switch(bs){ 612 case BScdda: 613 cmd[1] = 0x04; 614 break; 615 616 case BScdrom: 617 cmd[1] = 0x08; 618 break; 619 620 case BScdxa: 621 cmd[1] = 0x0C; 622 break; 623 624 default: 625 werrstr("unknown bs %d", bs); 626 return -1; 627 } 628 629 n = nblock*bs; 630 nn = scsi(drive, cmd, sizeof(cmd), v, n, Sread); 631 if(nn != n) { 632 werrstr("short read %ld/%ld", nn, n); 633 if(vflag) 634 print("read off %lud nblock %ld bs %d failed\n", off, nblock, bs); 635 return -1; 636 } 637 638 return nblock; 639 } 640 641 static Otrack* 642 mmcopenrd(Drive *drive, int trackno) 643 { 644 Otrack *o; 645 Mmcaux *aux; 646 647 if(trackno < 0 || trackno >= drive->ntrack) { 648 werrstr("track number out of range"); 649 return nil; 650 } 651 652 aux = drive->aux; 653 if(aux->nwopen) { 654 werrstr("disk in use for writing"); 655 return nil; 656 } 657 658 o = emalloc(sizeof(Otrack)); 659 o->drive = drive; 660 o->track = &drive->track[trackno]; 661 o->nchange = drive->nchange; 662 o->omode = OREAD; 663 o->buf = bopen(mmcread, OREAD, o->track->bs, Nblock); 664 o->buf->otrack = o; 665 666 aux->nropen++; 667 668 return o; 669 } 670 671 static long 672 mmcxwrite(Otrack *o, void *v, long nblk) 673 { 674 uchar cmd[10]; 675 Mmcaux *aux; 676 677 assert(o->omode == OWRITE); 678 679 aux = o->drive->aux; 680 aux->ntotby += nblk*o->track->bs; 681 aux->ntotbk += nblk; 682 memset(cmd, 0, sizeof(cmd)); 683 cmd[0] = 0x2a; /* write */ 684 cmd[2] = aux->mmcnwa>>24; 685 cmd[3] = aux->mmcnwa>>16; 686 cmd[4] = aux->mmcnwa>>8; 687 cmd[5] = aux->mmcnwa; 688 cmd[7] = nblk>>8; 689 cmd[8] = nblk>>0; 690 if(vflag) 691 print("%lld: write %ld at 0x%lux\n", nsec(), nblk, aux->mmcnwa); 692 aux->mmcnwa += nblk; 693 return scsi(o->drive, cmd, sizeof(cmd), v, nblk*o->track->bs, Swrite); 694 } 695 696 static long 697 mmcwrite(Buf *buf, void *v, long nblk, long) 698 { 699 return mmcxwrite(buf->otrack, v, nblk); 700 } 701 702 static Otrack* 703 mmccreate(Drive *drive, int type) 704 { 705 int bs; 706 Mmcaux *aux; 707 Track *t; 708 Otrack *o; 709 710 aux = drive->aux; 711 712 if(aux->nropen || aux->nwopen) { 713 werrstr("drive in use"); 714 return nil; 715 } 716 717 switch(type){ 718 case TypeAudio: 719 bs = BScdda; 720 break; 721 case TypeData: 722 bs = BScdrom; 723 break; 724 default: 725 werrstr("bad type %d", type); 726 return nil; 727 } 728 729 if(mmctrackinfo(drive, 0xFF, Maxtrack)) { /* the invisible track */ 730 werrstr("CD not writable"); 731 return nil; 732 } 733 if(mmcsetbs(drive, bs) < 0) { 734 werrstr("cannot set bs mode"); 735 return nil; 736 } 737 if(mmctrackinfo(drive, 0xFF, Maxtrack)) { /* the invisible track */ 738 werrstr("CD not writable 2"); 739 return nil; 740 } 741 742 aux->ntotby = 0; 743 aux->ntotbk = 0; 744 745 t = &drive->track[drive->ntrack++]; 746 t->size = 0; 747 t->bs = bs; 748 t->beg = aux->mmcnwa; 749 t->end = 0; 750 t->type = type; 751 drive->nameok = 0; 752 753 o = emalloc(sizeof(Otrack)); 754 o->drive = drive; 755 o->nchange = drive->nchange; 756 o->omode = OWRITE; 757 o->track = t; 758 o->buf = bopen(mmcwrite, OWRITE, bs, Nblock); 759 o->buf->otrack = o; 760 761 aux->nwopen++; 762 763 if(vflag) 764 print("mmcinit: nwa = 0x%luX\n", aux->mmcnwa); 765 766 return o; 767 } 768 769 void 770 mmcsynccache(Drive *drive) 771 { 772 uchar cmd[10]; 773 Mmcaux *aux; 774 775 memset(cmd, 0, sizeof(cmd)); 776 cmd[0] = 0x35; /* flush */ 777 scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 778 if(vflag) { 779 aux = drive->aux; 780 print("mmcsynccache: bytes = %ld blocks = %ld, mmcnwa 0x%luX\n", 781 aux->ntotby, aux->ntotbk, aux->mmcnwa); 782 } 783 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 784 } 785 786 static void 787 mmcclose(Otrack *o) 788 { 789 Mmcaux *aux; 790 static uchar zero[2*BSmax]; 791 792 aux = o->drive->aux; 793 if(o->omode == OREAD) 794 aux->nropen--; 795 else if(o->omode == OWRITE) { 796 aux->nwopen--; 797 mmcxwrite(o, zero, 2); /* write lead out */ 798 mmcsynccache(o->drive); 799 o->drive->nchange = -1; /* force reread toc */ 800 } 801 free(o); 802 } 803 804 static int 805 mmcxclose(Drive *drive, int ts, int trackno) 806 { 807 uchar cmd[10]; 808 809 /* 810 * ts: 1 == track, 2 == session 811 */ 812 memset(cmd, 0, sizeof(cmd)); 813 cmd[0] = 0x5B; 814 cmd[2] = ts; 815 if(ts == 1) 816 cmd[5] = trackno; 817 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 818 } 819 820 static int 821 mmcfixate(Drive *drive) 822 { 823 uchar *p; 824 Mmcaux *aux; 825 826 if((drive->cap & Cwrite) == 0) { 827 werrstr("not a writer"); 828 return -1; 829 } 830 831 drive->nchange = -1; /* force reread toc */ 832 833 aux = drive->aux; 834 p = aux->page05; 835 p[3] = (p[3] & ~0xC0); 836 if(mmcsetpage(drive, 0x05, p) < 0) 837 return -1; 838 839 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 840 return mmcxclose(drive, 0x02, 0); 841 } 842 843 static int 844 mmcsession(Drive *drive) 845 { 846 uchar *p; 847 Mmcaux *aux; 848 849 drive->nchange = -1; /* force reread toc */ 850 851 aux = drive->aux; 852 p = aux->page05; 853 p[3] = (p[3] & ~0xC0); 854 if(mmcsetpage(drive, 0x05, p) < 0) 855 return -1; 856 857 /* rsc: seems not to work on some drives; mmcclose(1, 0xFF); */ 858 return mmcxclose(drive, 0x02, 0); 859 } 860 861 static int 862 mmcblank(Drive *drive, int quick) 863 { 864 uchar cmd[12]; 865 866 drive->nchange = -1; /* force reread toc */ 867 868 memset(cmd, 0, sizeof(cmd)); 869 cmd[0] = 0xA1; /* blank */ 870 /* cmd[1] = 0 means blank the whole disc; = 1 just the header */ 871 cmd[1] = quick ? 0x01 : 0x00; 872 873 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 874 } 875 876 static int 877 start(Drive *drive, int code) 878 { 879 uchar cmd[6]; 880 881 memset(cmd, 0, sizeof(cmd)); 882 cmd[0] = 0x1B; 883 cmd[4] = code; 884 return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 885 } 886 887 static char* 888 e(int status) 889 { 890 if(status < 0) 891 return geterrstr(); 892 return nil; 893 } 894 895 static char* 896 mmcctl(Drive *drive, int argc, char **argv) 897 { 898 if(argc < 1) 899 return nil; 900 901 if(strcmp(argv[0], "blank") == 0) 902 return e(mmcblank(drive, 0)); 903 if(strcmp(argv[0], "quickblank") == 0) 904 return e(mmcblank(drive, 1)); 905 if(strcmp(argv[0], "eject") == 0) 906 return e(start(drive, 2)); 907 if(strcmp(argv[0], "ingest") == 0) 908 return e(start(drive, 3)); 909 return "bad arg"; 910 } 911 912 static char* 913 mmcsetspeed(Drive *drive, int r, int w) 914 { 915 char *rv; 916 uchar cmd[12]; 917 918 memset(cmd, 0, sizeof(cmd)); 919 cmd[0] = 0xBB; 920 cmd[2] = r>>8; 921 cmd[3] = r; 922 cmd[4] = w>>8; 923 cmd[5] = w; 924 rv = e(scsi(drive, cmd, sizeof(cmd), nil, 0, Snone)); 925 mmcgetspeed(drive); 926 return rv; 927 } 928 929 static Dev mmcdev = { 930 mmcopenrd, 931 mmccreate, 932 bufread, 933 bufwrite, 934 mmcclose, 935 mmcgettoc, 936 mmcfixate, 937 mmcctl, 938 mmcsetspeed, 939 }; 940