mmc.c 17 KB

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