1e67f3b95SDavid du Colombier /* 2e67f3b95SDavid du Colombier * multi-media commands 3e67f3b95SDavid du Colombier * 4e67f3b95SDavid du Colombier * as of mmc-6, mode page 0x2a (capabilities & mechanical status) is legacy 5e67f3b95SDavid du Colombier * and read-only, last defined in mmc-3. mode page 5 (write parameters) 6e67f3b95SDavid du Colombier * applies only to cd-r(w) and dvd-r(w); *-rom, dvd+* and bd-* are right out. 7e67f3b95SDavid du Colombier */ 87dd7cddfSDavid du Colombier #include <u.h> 97dd7cddfSDavid du Colombier #include <libc.h> 107dd7cddfSDavid du Colombier #include <disk.h> 11e67f3b95SDavid du Colombier #include "../scuzz/scsireq.h" 127dd7cddfSDavid du Colombier #include "dat.h" 137dd7cddfSDavid du Colombier #include "fns.h" 147dd7cddfSDavid du Colombier 157dd7cddfSDavid du Colombier enum 167dd7cddfSDavid du Colombier { 1760ba15acSDavid du Colombier Desperate = 0, /* non-zero grubs around in inquiry string */ 1860ba15acSDavid du Colombier 197dd7cddfSDavid du Colombier Pagesz = 255, 20e67f3b95SDavid du Colombier 21*efd1a06fSDavid du Colombier Pagerrrecov = 1, /* error recovery */ 22cdf9e71cSDavid du Colombier Pagwrparams = 5, /* (cd|dvd)-r(w) device write parameters */ 23e67f3b95SDavid du Colombier Pagcache = 8, 24e67f3b95SDavid du Colombier Pagcapmechsts = 0x2a, 25684b447eSDavid du Colombier 26684b447eSDavid du Colombier Invistrack = 0xff, /* the invisible & incomplete track */ 27*efd1a06fSDavid du Colombier Maxresptracks = 120, /* (1024-slop) / 8 */ 28*efd1a06fSDavid du Colombier 29*efd1a06fSDavid du Colombier /* Pagerrrecov error control bits (buf[2]) */ 30*efd1a06fSDavid du Colombier Erawre = 1<<7, /* recover from write errors */ 31*efd1a06fSDavid du Colombier Erarre = 1<<6, /* recover from read errors */ 32*efd1a06fSDavid du Colombier Ertb = 1<<5, /* transfer bad block to host */ 33*efd1a06fSDavid du Colombier Errc = 1<<4, /* read continuous; no error recovery */ 34*efd1a06fSDavid du Colombier Erper = 1<<2, /* post error: report recovered errors */ 35*efd1a06fSDavid du Colombier Erdte = 1<<1, /* disable transfer on error */ 36*efd1a06fSDavid du Colombier Erdcr = 1<<0, /* disable correction */ 37*efd1a06fSDavid du Colombier 38*efd1a06fSDavid du Colombier Kilo = 1000LL, 39*efd1a06fSDavid du Colombier GB = Kilo * Kilo * Kilo, 407dd7cddfSDavid du Colombier }; 417dd7cddfSDavid du Colombier 42*efd1a06fSDavid du Colombier typedef struct Intfeat Intfeat; 43*efd1a06fSDavid du Colombier typedef struct Mmcaux Mmcaux; 44*efd1a06fSDavid du Colombier 457dd7cddfSDavid du Colombier static Dev mmcdev; 467dd7cddfSDavid du Colombier 477dd7cddfSDavid du Colombier struct Mmcaux { 48684b447eSDavid du Colombier /* drive characteristics */ 49cdf9e71cSDavid du Colombier uchar page05[Pagesz]; /* write parameters */ 507dd7cddfSDavid du Colombier int page05ok; 517dd7cddfSDavid du Colombier int pagecmdsz; 527dd7cddfSDavid du Colombier 53684b447eSDavid du Colombier /* disc characteristics */ 547f1c8a1dSDavid du Colombier long mmcnwa; /* next writable address (block #) */ 557dd7cddfSDavid du Colombier int nropen; 567dd7cddfSDavid du Colombier int nwopen; 57e67f3b95SDavid du Colombier vlong ntotby; 587dd7cddfSDavid du Colombier long ntotbk; 597dd7cddfSDavid du Colombier }; 607dd7cddfSDavid du Colombier 61*efd1a06fSDavid du Colombier struct Intfeat { 62*efd1a06fSDavid du Colombier int numb; 63*efd1a06fSDavid du Colombier char *name; 64*efd1a06fSDavid du Colombier }; 65*efd1a06fSDavid du Colombier 66*efd1a06fSDavid du Colombier enum { 67*efd1a06fSDavid du Colombier Featdfctmgmt = 0x24, 68*efd1a06fSDavid du Colombier Featedfctrpt = 0x29, 69*efd1a06fSDavid du Colombier }; 70*efd1a06fSDavid du Colombier 71*efd1a06fSDavid du Colombier Intfeat intfeats[] = { 72*efd1a06fSDavid du Colombier // 0x21, "incr. streaming writable", 73*efd1a06fSDavid du Colombier Featdfctmgmt, "hw defect mgmt.", 74*efd1a06fSDavid du Colombier Featedfctrpt, "enhanced defect reporting", 75*efd1a06fSDavid du Colombier 0x38, "pseudo-overwrite", 76*efd1a06fSDavid du Colombier // 0x40, "bd read", 77*efd1a06fSDavid du Colombier // 0x41, "bd write", 78*efd1a06fSDavid du Colombier }; 79*efd1a06fSDavid du Colombier 800dc12738SDavid du Colombier /* these will be printed as user ids, so no spaces please */ 81c038c065SDavid du Colombier static char *dvdtype[] = { 82c038c065SDavid du Colombier "dvd-rom", 83c038c065SDavid du Colombier "dvd-ram", 84c038c065SDavid du Colombier "dvd-r", 85c038c065SDavid du Colombier "dvd-rw", 860dc12738SDavid du Colombier "hd-dvd-rom", 870dc12738SDavid du Colombier "hd-dvd-ram", 880dc12738SDavid du Colombier "hd-dvd-r", 890dc12738SDavid du Colombier "type-7-unknown", 900dc12738SDavid du Colombier "type-8-unknown", 91c038c065SDavid du Colombier "dvd+rw", 92c038c065SDavid du Colombier "dvd+r", 930dc12738SDavid du Colombier "type-11-unknown", 940dc12738SDavid du Colombier "type-12-unknown", 950dc12738SDavid du Colombier "dvd+rw-dl", 960dc12738SDavid du Colombier "dvd+r-dl", 970dc12738SDavid du Colombier "type-15-unknown", 98c038c065SDavid du Colombier }; 99c038c065SDavid du Colombier 100*efd1a06fSDavid du Colombier static int format(Drive *drive); 1017f1c8a1dSDavid du Colombier static int getinvistrack(Drive *drive); 1027f1c8a1dSDavid du Colombier 1037dd7cddfSDavid du Colombier static ulong 1047dd7cddfSDavid du Colombier bige(void *p) 1057dd7cddfSDavid du Colombier { 1067dd7cddfSDavid du Colombier uchar *a; 1077dd7cddfSDavid du Colombier 1087dd7cddfSDavid du Colombier a = p; 1097dd7cddfSDavid du Colombier return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); 1107dd7cddfSDavid du Colombier } 1117dd7cddfSDavid du Colombier 1127dd7cddfSDavid du Colombier static ushort 1137dd7cddfSDavid du Colombier biges(void *p) 1147dd7cddfSDavid du Colombier { 1157dd7cddfSDavid du Colombier uchar *a; 1167dd7cddfSDavid du Colombier 1177dd7cddfSDavid du Colombier a = p; 1187dd7cddfSDavid du Colombier return (a[0]<<8) | a[1]; 1197dd7cddfSDavid du Colombier } 1207dd7cddfSDavid du Colombier 1218ad6bb6aSDavid du Colombier ulong 1228ad6bb6aSDavid du Colombier getnwa(Drive *drive) 1238ad6bb6aSDavid du Colombier { 1248ad6bb6aSDavid du Colombier Mmcaux *aux; 1258ad6bb6aSDavid du Colombier 1268ad6bb6aSDavid du Colombier aux = drive->aux; 1278ad6bb6aSDavid du Colombier return aux->mmcnwa; 1288ad6bb6aSDavid du Colombier } 1298ad6bb6aSDavid du Colombier 1307dd7cddfSDavid du Colombier static void 1317dd7cddfSDavid du Colombier hexdump(void *v, int n) 1327dd7cddfSDavid du Colombier { 1337dd7cddfSDavid du Colombier int i; 1347dd7cddfSDavid du Colombier uchar *p; 1357dd7cddfSDavid du Colombier 1367dd7cddfSDavid du Colombier p = v; 1377dd7cddfSDavid du Colombier for(i=0; i<n; i++){ 1387dd7cddfSDavid du Colombier print("%.2ux ", p[i]); 1397dd7cddfSDavid du Colombier if((i%8) == 7) 1407dd7cddfSDavid du Colombier print("\n"); 1417dd7cddfSDavid du Colombier } 1427dd7cddfSDavid du Colombier if(i%8) 1437dd7cddfSDavid du Colombier print("\n"); 1447dd7cddfSDavid du Colombier } 1457dd7cddfSDavid du Colombier 146c038c065SDavid du Colombier static void 147c038c065SDavid du Colombier initcdb(uchar *cdb, int len, int cmd) 148c038c065SDavid du Colombier { 149c038c065SDavid du Colombier memset(cdb, 0, len); 150c038c065SDavid du Colombier cdb[0] = cmd; 151c038c065SDavid du Colombier } 152c038c065SDavid du Colombier 153e67f3b95SDavid du Colombier /* 154c038c065SDavid du Colombier * SCSI CDBs (cmd arrays) are 6, 10, 12, 16 or 32 bytes long. 155e67f3b95SDavid du Colombier * The mode sense/select commands implicitly refer to 156e67f3b95SDavid du Colombier * a mode parameter list, which consists of an 8-byte 157e67f3b95SDavid du Colombier * mode parameter header, followed by zero or more block 158e67f3b95SDavid du Colombier * descriptors and zero or more mode pages (MMC-2 §5.5.2). 159e67f3b95SDavid du Colombier * We'll ignore mode sub-pages. 160e67f3b95SDavid du Colombier * Integers are stored big-endian. 161e67f3b95SDavid du Colombier * 162e67f3b95SDavid du Colombier * The format of the mode parameter (10) header is: 163e67f3b95SDavid du Colombier * ushort mode_data_length; // of following bytes 164e67f3b95SDavid du Colombier * uchar medium_type; 165e67f3b95SDavid du Colombier * uchar device_specific; 166e67f3b95SDavid du Colombier * uchar reserved[2]; 167e67f3b95SDavid du Colombier * ushort block_descriptor_length; // zero 168e67f3b95SDavid du Colombier * 169e67f3b95SDavid du Colombier * The format of the mode parameter (6) header is: 170e67f3b95SDavid du Colombier * uchar mode_data_length; // of following bytes 171e67f3b95SDavid du Colombier * uchar medium_type; 172e67f3b95SDavid du Colombier * uchar device_specific; 173e67f3b95SDavid du Colombier * uchar block_descriptor_length; // zero 174e67f3b95SDavid du Colombier * 175e67f3b95SDavid du Colombier * The format of the mode pages is: 176e67f3b95SDavid du Colombier * uchar page_code_and_PS; 177e67f3b95SDavid du Colombier * uchar page_len; // of following bytes 178e67f3b95SDavid du Colombier * uchar parameter[page_len]; 179e67f3b95SDavid du Colombier * 180e67f3b95SDavid du Colombier * see SPC-3 §4.3.4.6 for allocation length and §7.4 for mode parameter lists. 181e67f3b95SDavid du Colombier */ 182e67f3b95SDavid du Colombier 183e67f3b95SDavid du Colombier enum { 184e67f3b95SDavid du Colombier Mode10parmhdrlen= 8, 185e67f3b95SDavid du Colombier Mode6parmhdrlen = 4, 186e67f3b95SDavid du Colombier Modepaghdrlen = 2, 187e67f3b95SDavid du Colombier }; 188e67f3b95SDavid du Colombier 1897dd7cddfSDavid du Colombier static int 1907dd7cddfSDavid du Colombier mmcgetpage10(Drive *drive, int page, void *v) 1917dd7cddfSDavid du Colombier { 1927dd7cddfSDavid du Colombier uchar cmd[10], resp[512]; 1937dd7cddfSDavid du Colombier int n, r; 1947dd7cddfSDavid du Colombier 19560ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMsense10); 1967dd7cddfSDavid du Colombier cmd[2] = page; 197e67f3b95SDavid du Colombier cmd[8] = 255; /* allocation length: buffer size */ 1987dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 199e67f3b95SDavid du Colombier if(n < Mode10parmhdrlen) 2007dd7cddfSDavid du Colombier return -1; 2017dd7cddfSDavid du Colombier 202e67f3b95SDavid du Colombier r = (resp[6]<<8) | resp[7]; /* block descriptor length */ 203e67f3b95SDavid du Colombier n -= Mode10parmhdrlen + r; 2047dd7cddfSDavid du Colombier 2057dd7cddfSDavid du Colombier if(n < 0) 2067dd7cddfSDavid du Colombier return -1; 2077dd7cddfSDavid du Colombier if(n > Pagesz) 2087dd7cddfSDavid du Colombier n = Pagesz; 2097dd7cddfSDavid du Colombier 210e67f3b95SDavid du Colombier memmove(v, &resp[Mode10parmhdrlen + r], n); 2117dd7cddfSDavid du Colombier return n; 2127dd7cddfSDavid du Colombier } 2137dd7cddfSDavid du Colombier 2147dd7cddfSDavid du Colombier static int 2157dd7cddfSDavid du Colombier mmcgetpage6(Drive *drive, int page, void *v) 2167dd7cddfSDavid du Colombier { 2177dd7cddfSDavid du Colombier uchar cmd[6], resp[512]; 2187dd7cddfSDavid du Colombier int n; 2197dd7cddfSDavid du Colombier 22060ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMsense6); 2217dd7cddfSDavid du Colombier cmd[2] = page; 222e67f3b95SDavid du Colombier cmd[4] = 255; /* allocation length */ 223e67f3b95SDavid du Colombier 2247dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 225e67f3b95SDavid du Colombier if(n < Mode6parmhdrlen) 2267dd7cddfSDavid du Colombier return -1; 2277dd7cddfSDavid du Colombier 228e67f3b95SDavid du Colombier n -= Mode6parmhdrlen + resp[3]; 2297dd7cddfSDavid du Colombier if(n < 0) 2307dd7cddfSDavid du Colombier return -1; 2317dd7cddfSDavid du Colombier if(n > Pagesz) 2327dd7cddfSDavid du Colombier n = Pagesz; 2337dd7cddfSDavid du Colombier 234e67f3b95SDavid du Colombier memmove(v, &resp[Mode6parmhdrlen + resp[3]], n); 2357dd7cddfSDavid du Colombier return n; 2367dd7cddfSDavid du Colombier } 2377dd7cddfSDavid du Colombier 2387dd7cddfSDavid du Colombier static int 2397dd7cddfSDavid du Colombier mmcsetpage10(Drive *drive, int page, void *v) 2407dd7cddfSDavid du Colombier { 2417dd7cddfSDavid du Colombier uchar cmd[10], *p, *pagedata; 2427dd7cddfSDavid du Colombier int len, n; 2437dd7cddfSDavid du Colombier 244e67f3b95SDavid du Colombier /* allocate parameter list, copy in mode page, fill in header */ 2457dd7cddfSDavid du Colombier pagedata = v; 2467dd7cddfSDavid du Colombier assert(pagedata[0] == page); 247e67f3b95SDavid du Colombier len = Mode10parmhdrlen + Modepaghdrlen + pagedata[1]; 2487dd7cddfSDavid du Colombier p = emalloc(len); 249e67f3b95SDavid du Colombier memmove(p + Mode10parmhdrlen, pagedata, pagedata[1]); 250e67f3b95SDavid du Colombier /* parameter list header */ 251e67f3b95SDavid du Colombier p[0] = 0; 252e67f3b95SDavid du Colombier p[1] = len - 2; 253e67f3b95SDavid du Colombier 254c038c065SDavid du Colombier /* set up CDB */ 25560ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMselect10); 256e67f3b95SDavid du Colombier cmd[1] = 0x10; /* format not vendor-specific */ 2577dd7cddfSDavid du Colombier cmd[8] = len; 2587dd7cddfSDavid du Colombier 259e67f3b95SDavid du Colombier // print("set: sending cmd\n"); 2607dd7cddfSDavid du Colombier // hexdump(cmd, 10); 261e67f3b95SDavid du Colombier // print("parameter list header\n"); 262e67f3b95SDavid du Colombier // hexdump(p, Mode10parmhdrlen); 2637dd7cddfSDavid du Colombier // print("page\n"); 264e67f3b95SDavid du Colombier // hexdump(p + Mode10parmhdrlen, len - Mode10parmhdrlen); 2657dd7cddfSDavid du Colombier 2667dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite); 267e67f3b95SDavid du Colombier 268e67f3b95SDavid du Colombier // print("set: got cmd\n"); 269e67f3b95SDavid du Colombier // hexdump(cmd, 10); 270e67f3b95SDavid du Colombier 2717dd7cddfSDavid du Colombier free(p); 2727dd7cddfSDavid du Colombier if(n < len) 2737dd7cddfSDavid du Colombier return -1; 2747dd7cddfSDavid du Colombier return 0; 2757dd7cddfSDavid du Colombier } 2767dd7cddfSDavid du Colombier 2777dd7cddfSDavid du Colombier static int 2787dd7cddfSDavid du Colombier mmcsetpage6(Drive *drive, int page, void *v) 2797dd7cddfSDavid du Colombier { 2807dd7cddfSDavid du Colombier uchar cmd[6], *p, *pagedata; 2817dd7cddfSDavid du Colombier int len, n; 2827dd7cddfSDavid du Colombier 283e67f3b95SDavid du Colombier if (vflag) 284e67f3b95SDavid du Colombier print("mmcsetpage6 called!\n"); 2857dd7cddfSDavid du Colombier pagedata = v; 2867dd7cddfSDavid du Colombier assert(pagedata[0] == page); 287e67f3b95SDavid du Colombier len = Mode6parmhdrlen + Modepaghdrlen + pagedata[1]; 2887dd7cddfSDavid du Colombier p = emalloc(len); 289e67f3b95SDavid du Colombier memmove(p + Mode6parmhdrlen, pagedata, pagedata[1]); 290e67f3b95SDavid du Colombier 29160ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMselect6); 292e67f3b95SDavid du Colombier cmd[1] = 0x10; /* format not vendor-specific */ 2937dd7cddfSDavid du Colombier cmd[4] = len; 2947dd7cddfSDavid du Colombier 2957dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite); 2967dd7cddfSDavid du Colombier free(p); 2977dd7cddfSDavid du Colombier if(n < len) 2987dd7cddfSDavid du Colombier return -1; 2997dd7cddfSDavid du Colombier return 0; 3007dd7cddfSDavid du Colombier } 3017dd7cddfSDavid du Colombier 3027dd7cddfSDavid du Colombier static int 3037dd7cddfSDavid du Colombier mmcgetpage(Drive *drive, int page, void *v) 3047dd7cddfSDavid du Colombier { 3057dd7cddfSDavid du Colombier Mmcaux *aux; 3067dd7cddfSDavid du Colombier 3077dd7cddfSDavid du Colombier aux = drive->aux; 3087dd7cddfSDavid du Colombier switch(aux->pagecmdsz) { 3097dd7cddfSDavid du Colombier case 10: 3107dd7cddfSDavid du Colombier return mmcgetpage10(drive, page, v); 3117dd7cddfSDavid du Colombier case 6: 3127dd7cddfSDavid du Colombier return mmcgetpage6(drive, page, v); 3137dd7cddfSDavid du Colombier default: 3147dd7cddfSDavid du Colombier assert(0); 3157dd7cddfSDavid du Colombier } 3167dd7cddfSDavid du Colombier return -1; 3177dd7cddfSDavid du Colombier } 3187dd7cddfSDavid du Colombier 3197dd7cddfSDavid du Colombier static int 3207dd7cddfSDavid du Colombier mmcsetpage(Drive *drive, int page, void *v) 3217dd7cddfSDavid du Colombier { 3227dd7cddfSDavid du Colombier Mmcaux *aux; 3237dd7cddfSDavid du Colombier 3247dd7cddfSDavid du Colombier aux = drive->aux; 3257dd7cddfSDavid du Colombier switch(aux->pagecmdsz) { 3267dd7cddfSDavid du Colombier case 10: 3277dd7cddfSDavid du Colombier return mmcsetpage10(drive, page, v); 3287dd7cddfSDavid du Colombier case 6: 3297dd7cddfSDavid du Colombier return mmcsetpage6(drive, page, v); 3307dd7cddfSDavid du Colombier default: 3317dd7cddfSDavid du Colombier assert(0); 3327dd7cddfSDavid du Colombier } 3337dd7cddfSDavid du Colombier return -1; 3347dd7cddfSDavid du Colombier } 3357dd7cddfSDavid du Colombier 3367dd7cddfSDavid du Colombier int 3377dd7cddfSDavid du Colombier mmcstatus(Drive *drive) 3387dd7cddfSDavid du Colombier { 3397dd7cddfSDavid du Colombier uchar cmd[12]; 3407dd7cddfSDavid du Colombier 341c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdCDstatus); /* mechanism status */ 3427dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), nil, 0, Sread); 3437dd7cddfSDavid du Colombier } 3447dd7cddfSDavid du Colombier 3459a747e4fSDavid du Colombier void 3469a747e4fSDavid du Colombier mmcgetspeed(Drive *drive) 3479a747e4fSDavid du Colombier { 3489a747e4fSDavid du Colombier int n, maxread, curread, maxwrite, curwrite; 3499a747e4fSDavid du Colombier uchar buf[Pagesz]; 3509a747e4fSDavid du Colombier 351dd12a5c6SDavid du Colombier memset(buf, 0, 22); 352c038c065SDavid du Colombier n = mmcgetpage(drive, Pagcapmechsts, buf); /* legacy page */ 353055c7668SDavid du Colombier if (n < 22) { 354055c7668SDavid du Colombier if (vflag) 355055c7668SDavid du Colombier fprint(2, "no Pagcapmechsts mode page!\n"); 356055c7668SDavid du Colombier return; 357055c7668SDavid du Colombier } 3589a747e4fSDavid du Colombier maxread = (buf[8]<<8)|buf[9]; 3599a747e4fSDavid du Colombier curread = (buf[14]<<8)|buf[15]; 3609a747e4fSDavid du Colombier maxwrite = (buf[18]<<8)|buf[19]; 3619a747e4fSDavid du Colombier curwrite = (buf[20]<<8)|buf[21]; 3629a747e4fSDavid du Colombier 363055c7668SDavid du Colombier if(maxread && maxread < 170 || curread && curread < 170) 3649a747e4fSDavid du Colombier return; /* bogus data */ 3659a747e4fSDavid du Colombier 3669a747e4fSDavid du Colombier drive->readspeed = curread; 3679a747e4fSDavid du Colombier drive->writespeed = curwrite; 3689a747e4fSDavid du Colombier drive->maxreadspeed = maxread; 3699a747e4fSDavid du Colombier drive->maxwritespeed = maxwrite; 3709a747e4fSDavid du Colombier } 3719a747e4fSDavid du Colombier 372cdf9e71cSDavid du Colombier static int 373cdf9e71cSDavid du Colombier getdevtype(Drive *drive) 374cdf9e71cSDavid du Colombier { 375cdf9e71cSDavid du Colombier int n; 376cdf9e71cSDavid du Colombier uchar cmd[6], resp[Pagesz]; 377cdf9e71cSDavid du Colombier 378cdf9e71cSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdInq); 379cdf9e71cSDavid du Colombier cmd[3] = sizeof resp >> 8; 380cdf9e71cSDavid du Colombier cmd[4] = sizeof resp; 381cdf9e71cSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread); 382cdf9e71cSDavid du Colombier if (n < 8) 383cdf9e71cSDavid du Colombier return -1; 384cdf9e71cSDavid du Colombier return resp[0] & 037; 385cdf9e71cSDavid du Colombier } 386cdf9e71cSDavid du Colombier 387684b447eSDavid du Colombier static int 388684b447eSDavid du Colombier start(Drive *drive, int code) 389684b447eSDavid du Colombier { 390684b447eSDavid du Colombier uchar cmd[6]; 391684b447eSDavid du Colombier 392684b447eSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdStart); 393684b447eSDavid du Colombier cmd[4] = code; 394684b447eSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 395684b447eSDavid du Colombier } 396684b447eSDavid du Colombier 397*efd1a06fSDavid du Colombier static int 398*efd1a06fSDavid du Colombier setcaching(Drive *drive) 399*efd1a06fSDavid du Colombier { 400*efd1a06fSDavid du Colombier int n; 401*efd1a06fSDavid du Colombier uchar buf[Pagesz]; 402*efd1a06fSDavid du Colombier 403*efd1a06fSDavid du Colombier /* 404*efd1a06fSDavid du Colombier * we can't actually control caching much. 405*efd1a06fSDavid du Colombier * see SBC-2 §6.3.3 but also MMC-6 §7.6. 406*efd1a06fSDavid du Colombier * 407*efd1a06fSDavid du Colombier * should set read ahead, MMC-6 §6.37; seems to control caching. 408*efd1a06fSDavid du Colombier */ 409*efd1a06fSDavid du Colombier n = mmcgetpage(drive, Pagcache, buf); 410*efd1a06fSDavid du Colombier if (n < 3) 411*efd1a06fSDavid du Colombier return -1; 412*efd1a06fSDavid du Colombier /* n == 255; buf[1] == 10 (10 bytes after buf[1]) */ 413*efd1a06fSDavid du Colombier buf[0] &= 077; /* clear reserved bits, MMC-6 §7.2.3 */ 414*efd1a06fSDavid du Colombier assert(buf[0] == Pagcache); 415*efd1a06fSDavid du Colombier assert(buf[1] >= 10); 416*efd1a06fSDavid du Colombier buf[2] = Ccwce; 417*efd1a06fSDavid du Colombier if (mmcsetpage(drive, Pagcache, buf) < 0) { 418*efd1a06fSDavid du Colombier if (vflag) 419*efd1a06fSDavid du Colombier print("mmcprobe: cache control NOT set\n"); 420*efd1a06fSDavid du Colombier return -1; 421*efd1a06fSDavid du Colombier } 422*efd1a06fSDavid du Colombier return 0; 423*efd1a06fSDavid du Colombier } 424*efd1a06fSDavid du Colombier 4257dd7cddfSDavid du Colombier Drive* 4267dd7cddfSDavid du Colombier mmcprobe(Scsi *scsi) 4277dd7cddfSDavid du Colombier { 4287dd7cddfSDavid du Colombier Mmcaux *aux; 4297dd7cddfSDavid du Colombier Drive *drive; 4307dd7cddfSDavid du Colombier uchar buf[Pagesz]; 431*efd1a06fSDavid du Colombier int cap; 4327dd7cddfSDavid du Colombier 433e67f3b95SDavid du Colombier if (vflag) 434e67f3b95SDavid du Colombier print("mmcprobe: inquiry: %s\n", scsi->inquire); 4357f1c8a1dSDavid du Colombier 4367dd7cddfSDavid du Colombier drive = emalloc(sizeof(Drive)); 4377dd7cddfSDavid du Colombier drive->Scsi = *scsi; 4387dd7cddfSDavid du Colombier drive->Dev = mmcdev; 4397f1c8a1dSDavid du Colombier drive->invistrack = -1; 4407f1c8a1dSDavid du Colombier getinvistrack(drive); 4417f1c8a1dSDavid du Colombier 4427dd7cddfSDavid du Colombier aux = emalloc(sizeof(Mmcaux)); 4437dd7cddfSDavid du Colombier drive->aux = aux; 4447f1c8a1dSDavid du Colombier 445684b447eSDavid du Colombier scsiready(drive); 446cdf9e71cSDavid du Colombier drive->type = getdevtype(drive); 447cdf9e71cSDavid du Colombier if (drive->type != TypeCD) { 448cdf9e71cSDavid du Colombier werrstr("not an mmc device"); 449cdf9e71cSDavid du Colombier free(aux); 450cdf9e71cSDavid du Colombier free(drive); 451cdf9e71cSDavid du Colombier return nil; 452cdf9e71cSDavid du Colombier } 4537dd7cddfSDavid du Colombier 454cdf9e71cSDavid du Colombier /* 455cdf9e71cSDavid du Colombier * drive is an mmc device; learn what we can about it 456cdf9e71cSDavid du Colombier * (as opposed to the disc in it). 457cdf9e71cSDavid du Colombier */ 458cdf9e71cSDavid du Colombier 459684b447eSDavid du Colombier start(drive, 1); 460e67f3b95SDavid du Colombier /* attempt to read CD capabilities page, but it's now legacy */ 461e67f3b95SDavid du Colombier if(mmcgetpage10(drive, Pagcapmechsts, buf) >= 0) 4627dd7cddfSDavid du Colombier aux->pagecmdsz = 10; 463e67f3b95SDavid du Colombier else if(mmcgetpage6(drive, Pagcapmechsts, buf) >= 0) 4647dd7cddfSDavid du Colombier aux->pagecmdsz = 6; 4657dd7cddfSDavid du Colombier else { 466055c7668SDavid du Colombier if (vflag) 467055c7668SDavid du Colombier fprint(2, "no Pagcapmechsts mode page!\n"); 468055c7668SDavid du Colombier werrstr("can't read mode page %d!", Pagcapmechsts); 469cdf9e71cSDavid du Colombier free(aux); 4707dd7cddfSDavid du Colombier free(drive); 4717dd7cddfSDavid du Colombier return nil; 4727dd7cddfSDavid du Colombier } 473cdf9e71cSDavid du Colombier 4747dd7cddfSDavid du Colombier cap = 0; 475c038c065SDavid du Colombier if(buf[Capwrite] & (Capcdr|Capcdrw|Capdvdr|Capdvdram) || 476c038c065SDavid du Colombier buf[Capmisc] & Caprw) 4777dd7cddfSDavid du Colombier cap |= Cwrite; 478c038c065SDavid du Colombier if(buf[Capmisc] & Capcdda) /* CD-DA commands supported? */ 479c038c065SDavid du Colombier cap |= Ccdda; /* not used anywhere else */ 4807dd7cddfSDavid du Colombier 4817dd7cddfSDavid du Colombier // print("read %d max %d\n", biges(buf+14), biges(buf+8)); 4827dd7cddfSDavid du Colombier // print("write %d max %d\n", biges(buf+20), biges(buf+18)); 4837dd7cddfSDavid du Colombier 484e67f3b95SDavid du Colombier /* cache optional page 05 (write parameter page) */ 485c038c065SDavid du Colombier if(/* (cap & Cwrite) && */ 486c038c065SDavid du Colombier mmcgetpage(drive, Pagwrparams, aux->page05) >= 0) { 4877dd7cddfSDavid du Colombier aux->page05ok = 1; 488c038c065SDavid du Colombier cap |= Cwrite; 489c038c065SDavid du Colombier if (vflag) 490684b447eSDavid du Colombier fprint(2, "mmcprobe: got page 5, assuming drive can write\n"); 491055c7668SDavid du Colombier } else { 492055c7668SDavid du Colombier if (vflag) 493055c7668SDavid du Colombier fprint(2, "no Pagwrparams mode page!\n"); 4947dd7cddfSDavid du Colombier cap &= ~Cwrite; 495055c7668SDavid du Colombier } 4967dd7cddfSDavid du Colombier drive->cap = cap; 4977dd7cddfSDavid du Colombier 4989a747e4fSDavid du Colombier mmcgetspeed(drive); 499*efd1a06fSDavid du Colombier setcaching(drive); 5007dd7cddfSDavid du Colombier return drive; 5017dd7cddfSDavid du Colombier } 5027dd7cddfSDavid du Colombier 5030599ba09SDavid du Colombier static char *tracktype[] = { /* indices are track modes (Tm*) */ 504684b447eSDavid du Colombier "audio cdda", 505684b447eSDavid du Colombier "2 audio channels", 506684b447eSDavid du Colombier "2", 507684b447eSDavid du Colombier "3", 508684b447eSDavid du Colombier "data, recorded uninterrupted", 509684b447eSDavid du Colombier "data, recorded interrupted", 510684b447eSDavid du Colombier }; 511684b447eSDavid du Colombier 512*efd1a06fSDavid du Colombier /* 513*efd1a06fSDavid du Colombier * figure out the first writable block, if we can, into drive->aux->mmcnwa. 514*efd1a06fSDavid du Colombier * resp must be from ScmdRtrackinfo. 515*efd1a06fSDavid du Colombier */ 516*efd1a06fSDavid du Colombier static long 517*efd1a06fSDavid du Colombier gettracknwa(Drive *drive, int t, ulong beg, uchar *resp) 5187dd7cddfSDavid du Colombier { 5197f1c8a1dSDavid du Colombier long newnwa; 5207dd7cddfSDavid du Colombier Mmcaux *aux; 5217dd7cddfSDavid du Colombier 5227dd7cddfSDavid du Colombier aux = drive->aux; 523*efd1a06fSDavid du Colombier if(resp[7] & 1) { /* nwa valid? */ 524*efd1a06fSDavid du Colombier newnwa = bige(&resp[12]); 525*efd1a06fSDavid du Colombier if (newnwa >= 0) 526*efd1a06fSDavid du Colombier if (aux->mmcnwa < 0) 527*efd1a06fSDavid du Colombier aux->mmcnwa = newnwa; 528*efd1a06fSDavid du Colombier else if (aux->mmcnwa != newnwa) 529*efd1a06fSDavid du Colombier fprint(2, "nwa is %ld but invis track starts blk %ld\n", 530*efd1a06fSDavid du Colombier newnwa, aux->mmcnwa); 531*efd1a06fSDavid du Colombier } 532*efd1a06fSDavid du Colombier /* resp[6] & (1<<7) of zero: invisible track */ 533*efd1a06fSDavid du Colombier if(t == Invistrack || t == drive->invistrack) 534*efd1a06fSDavid du Colombier if (aux->mmcnwa < 0) 535*efd1a06fSDavid du Colombier aux->mmcnwa = beg; 536*efd1a06fSDavid du Colombier else if (aux->mmcnwa != beg) 537*efd1a06fSDavid du Colombier fprint(2, "invis track starts blk %ld but nwa is %ld\n", 538*efd1a06fSDavid du Colombier beg, aux->mmcnwa); 539*efd1a06fSDavid du Colombier if (vflag && aux->mmcnwa >= 0) 540*efd1a06fSDavid du Colombier print(" nwa %lud", aux->mmcnwa); 541*efd1a06fSDavid du Colombier return 0; 5427dd7cddfSDavid du Colombier } 5437dd7cddfSDavid du Colombier 544*efd1a06fSDavid du Colombier /* 545*efd1a06fSDavid du Colombier * map tmode to type & bs. 546*efd1a06fSDavid du Colombier */ 547*efd1a06fSDavid du Colombier static void 548*efd1a06fSDavid du Colombier gettypebs(uchar tmode, int t, int i, int *typep, int *bsp) 549*efd1a06fSDavid du Colombier { 550*efd1a06fSDavid du Colombier int type, bs; 5517dd7cddfSDavid du Colombier 5527dd7cddfSDavid du Colombier if(vflag) 5538ad6bb6aSDavid du Colombier print("track %d type %d (%s)", t, tmode, 554684b447eSDavid du Colombier (tmode < nelem(tracktype)? tracktype[tmode]: "**GOK**")); 5557dd7cddfSDavid du Colombier type = TypeNone; 5567dd7cddfSDavid du Colombier bs = BScdda; 5577dd7cddfSDavid du Colombier switch(tmode){ 5580599ba09SDavid du Colombier case Tmcdda: 5597dd7cddfSDavid du Colombier type = TypeAudio; 5607dd7cddfSDavid du Colombier bs = BScdda; 5617dd7cddfSDavid du Colombier break; 5620599ba09SDavid du Colombier case Tm2audio: /* 2 audio channels, with pre-emphasis 50/15 μs */ 5637dd7cddfSDavid du Colombier if(vflag) 564684b447eSDavid du Colombier print("audio channels with preemphasis on track %d " 565684b447eSDavid du Colombier "(u%.3d)\n", t, i); 5667dd7cddfSDavid du Colombier type = TypeNone; 5677dd7cddfSDavid du Colombier break; 5680599ba09SDavid du Colombier case Tmunintr: /* data track, recorded uninterrupted */ 5690599ba09SDavid du Colombier case Tmintr: /* data track, recorded interrupted */ 5700599ba09SDavid du Colombier /* treat Tmintr (5) as cdrom; it's probably dvd or bd */ 571e67f3b95SDavid du Colombier type = TypeData; 572e67f3b95SDavid du Colombier bs = BScdrom; 573e67f3b95SDavid du Colombier break; 5747dd7cddfSDavid du Colombier default: 5757dd7cddfSDavid du Colombier if(vflag) 5767dd7cddfSDavid du Colombier print("unknown track type %d\n", tmode); 577e67f3b95SDavid du Colombier break; 5787dd7cddfSDavid du Colombier } 579*efd1a06fSDavid du Colombier *bsp = bs; 580*efd1a06fSDavid du Colombier *typep = type; 581*efd1a06fSDavid du Colombier } 5827dd7cddfSDavid du Colombier 583*efd1a06fSDavid du Colombier /* t is a track number on disc, i is an index into drive->track[] for result */ 584*efd1a06fSDavid du Colombier static int 585*efd1a06fSDavid du Colombier mmctrackinfo(Drive *drive, int t, int i) 586*efd1a06fSDavid du Colombier { 587*efd1a06fSDavid du Colombier int n, type, bs; 588*efd1a06fSDavid du Colombier ulong beg, size; 589*efd1a06fSDavid du Colombier uchar tmode; 590*efd1a06fSDavid du Colombier uchar cmd[10], resp[255]; 591*efd1a06fSDavid du Colombier Track *track; 592*efd1a06fSDavid du Colombier 593*efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRtrackinfo); 594*efd1a06fSDavid du Colombier cmd[1] = 1; /* address below is logical track # */ 595*efd1a06fSDavid du Colombier cmd[2] = t>>24; 596*efd1a06fSDavid du Colombier cmd[3] = t>>16; 597*efd1a06fSDavid du Colombier cmd[4] = t>>8; 598*efd1a06fSDavid du Colombier cmd[5] = t; 599*efd1a06fSDavid du Colombier cmd[7] = sizeof(resp)>>8; 600*efd1a06fSDavid du Colombier cmd[8] = sizeof(resp); 601*efd1a06fSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 602*efd1a06fSDavid du Colombier if(n < 28) { 603*efd1a06fSDavid du Colombier if(vflag) 604*efd1a06fSDavid du Colombier print("trackinfo %d fails n=%d: %r\n", t, n); 605*efd1a06fSDavid du Colombier return -1; 606*efd1a06fSDavid du Colombier } 607*efd1a06fSDavid du Colombier 608*efd1a06fSDavid du Colombier tmode = resp[5] & 0x0D; 609*efd1a06fSDavid du Colombier // dmode = resp[6] & 0x0F; 610*efd1a06fSDavid du Colombier 611*efd1a06fSDavid du Colombier gettypebs(tmode, t, i, &type, &bs); 612*efd1a06fSDavid du Colombier 613*efd1a06fSDavid du Colombier beg = bige(&resp[8]); 614*efd1a06fSDavid du Colombier size = bige(&resp[24]); 615*efd1a06fSDavid du Colombier 616*efd1a06fSDavid du Colombier track = &drive->track[i]; 617*efd1a06fSDavid du Colombier track->mtime = drive->changetime; 618*efd1a06fSDavid du Colombier track->beg = beg; 619*efd1a06fSDavid du Colombier track->end = beg + size; 620*efd1a06fSDavid du Colombier track->type = type; 621*efd1a06fSDavid du Colombier track->bs = bs; 622*efd1a06fSDavid du Colombier track->size = (vlong)(size-2) * bs; /* -2: skip lead out */ 6237dd7cddfSDavid du Colombier 6248ad6bb6aSDavid du Colombier if(resp[6] & (1<<6)) { /* blank? */ 625*efd1a06fSDavid du Colombier track->type = TypeBlank; 62660ba15acSDavid du Colombier drive->writeok = Yes; 6277dd7cddfSDavid du Colombier } 6287dd7cddfSDavid du Colombier 6298ad6bb6aSDavid du Colombier if(vflag) 6308ad6bb6aSDavid du Colombier print(" start %lud end %lud", beg, beg + size - 1); 631*efd1a06fSDavid du Colombier gettracknwa(drive, t, beg, resp); 6328ad6bb6aSDavid du Colombier if (vflag) 6338ad6bb6aSDavid du Colombier print("\n"); 6347dd7cddfSDavid du Colombier return 0; 6357dd7cddfSDavid du Colombier } 6367dd7cddfSDavid du Colombier 637c038c065SDavid du Colombier /* this may fail for blank media */ 6387dd7cddfSDavid du Colombier static int 6399a747e4fSDavid du Colombier mmcreadtoc(Drive *drive, int type, int track, void *data, int nbytes) 6407dd7cddfSDavid du Colombier { 6417dd7cddfSDavid du Colombier uchar cmd[10]; 6427dd7cddfSDavid du Colombier 64360ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRTOC); 644436f307dSDavid du Colombier cmd[1] = type; /* msf bit & reserved */ 645436f307dSDavid du Colombier cmd[2] = Tocfmttoc; 646436f307dSDavid du Colombier cmd[6] = track; /* track/session */ 6477dd7cddfSDavid du Colombier cmd[7] = nbytes>>8; 6487dd7cddfSDavid du Colombier cmd[8] = nbytes; 6497dd7cddfSDavid du Colombier 6507ec5746aSDavid du Colombier /* 6517ec5746aSDavid du Colombier * printing iounit(drive->Scsi.rawfd) here yields 6527ec5746aSDavid du Colombier * iounit(3) = 0; # for local access 6537ec5746aSDavid du Colombier * iounit(3) = 65512; # for remote access via /mnt/term 6547ec5746aSDavid du Colombier */ 6557dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), data, nbytes, Sread); 6567dd7cddfSDavid du Colombier } 6577dd7cddfSDavid du Colombier 6589a747e4fSDavid du Colombier static Msf 6599a747e4fSDavid du Colombier rdmsf(uchar *p) 6609a747e4fSDavid du Colombier { 6619a747e4fSDavid du Colombier Msf msf; 6629a747e4fSDavid du Colombier 6639a747e4fSDavid du Colombier msf.m = p[0]; 6649a747e4fSDavid du Colombier msf.s = p[1]; 6659a747e4fSDavid du Colombier msf.f = p[2]; 6669a747e4fSDavid du Colombier return msf; 6679a747e4fSDavid du Colombier } 6689a747e4fSDavid du Colombier 6697dd7cddfSDavid du Colombier static int 670c038c065SDavid du Colombier getdiscinfo(Drive *drive, uchar resp[], int resplen) 671c038c065SDavid du Colombier { 672c038c065SDavid du Colombier int n; 67360ba15acSDavid du Colombier uchar cmd[10]; 674c038c065SDavid du Colombier 67560ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRdiscinfo); 67660ba15acSDavid du Colombier cmd[7] = resplen>>8; 67760ba15acSDavid du Colombier cmd[8] = resplen; 67860ba15acSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, resplen, Sread); 67960ba15acSDavid du Colombier if(n < 24) { 68060ba15acSDavid du Colombier if(n >= 0) 68160ba15acSDavid du Colombier werrstr("rdiscinfo returns %d", n); 68260ba15acSDavid du Colombier else if (vflag) 683436f307dSDavid du Colombier fprint(2, "read disc info failed\n"); 68460ba15acSDavid du Colombier return -1; 685436f307dSDavid du Colombier } 686c038c065SDavid du Colombier if (vflag) 687c038c065SDavid du Colombier fprint(2, "read disc info succeeded\n"); 688c038c065SDavid du Colombier assert((resp[2] & 0340) == 0); /* data type 0 */ 689c038c065SDavid du Colombier drive->erasable = ((resp[2] & 0x10) != 0); /* -RW? */ 69060ba15acSDavid du Colombier return n; 69160ba15acSDavid du Colombier } 69260ba15acSDavid du Colombier 693*efd1a06fSDavid du Colombier int 694*efd1a06fSDavid du Colombier isbitset(uint bit, uchar *map) 695*efd1a06fSDavid du Colombier { 696*efd1a06fSDavid du Colombier return map[bit/8] & (1 << (bit%8)); 697*efd1a06fSDavid du Colombier } 698*efd1a06fSDavid du Colombier 699*efd1a06fSDavid du Colombier /* 700*efd1a06fSDavid du Colombier * set read-write error recovery mode page 1 (10 bytes), mmc-6 §7.3. 701*efd1a06fSDavid du Colombier * 702*efd1a06fSDavid du Colombier * requires defect management feature (0x24) mmc-6 §5.3.13 (mandatory on bd, 703*efd1a06fSDavid du Colombier * but has to be enabled by allocating spares with FORMAT UNIT command) 704*efd1a06fSDavid du Colombier * or enhanced defect reporting feature (0x29) mmc-6 §5.3.17, 705*efd1a06fSDavid du Colombier * and they are mutually exclusive. 706*efd1a06fSDavid du Colombier */ 70760ba15acSDavid du Colombier static int 708*efd1a06fSDavid du Colombier seterrrecov(Drive *drive) 70960ba15acSDavid du Colombier { 71060ba15acSDavid du Colombier int n; 711*efd1a06fSDavid du Colombier uchar buf[Pagesz]; 71260ba15acSDavid du Colombier 713*efd1a06fSDavid du Colombier if (!isbitset(Featdfctmgmt, drive->features) && 714*efd1a06fSDavid du Colombier !isbitset(Featedfctrpt, drive->features)) { 715*efd1a06fSDavid du Colombier fprint(2, "defect mgmt. and enhanced defect reporting disabled!\n"); 71660ba15acSDavid du Colombier return -1; 71760ba15acSDavid du Colombier } 718*efd1a06fSDavid du Colombier n = mmcgetpage(drive, Pagerrrecov, buf); 719*efd1a06fSDavid du Colombier if (n < 3) 72060ba15acSDavid du Colombier return -1; 721*efd1a06fSDavid du Colombier /* n == 255; buf[1] == 10 (10 bytes after buf[1]) */ 72260ba15acSDavid du Colombier /* 723*efd1a06fSDavid du Colombier * error recovery page as read: 724*efd1a06fSDavid du Colombier * 0: 01 (page: error recovery) 725*efd1a06fSDavid du Colombier * 1: 0a (length: 10) 726*efd1a06fSDavid du Colombier * 2: c0 (error control bits: 0300 == Erawre|Erarre) 727*efd1a06fSDavid du Colombier * 3: 20 (read retry count: 32) 728*efd1a06fSDavid du Colombier * 4: 00 729*efd1a06fSDavid du Colombier * 5: 00 730*efd1a06fSDavid du Colombier * 6: 00 731*efd1a06fSDavid du Colombier * 7: 00 732*efd1a06fSDavid du Colombier * 8: 01 (write retry count: 1) 733*efd1a06fSDavid du Colombier * 9: 00 00 00 (error reporting window size) 73460ba15acSDavid du Colombier */ 735*efd1a06fSDavid du Colombier buf[0] &= ~(1<<6); /* clear reserved bit */ 736*efd1a06fSDavid du Colombier assert((buf[0] & 077) == Pagerrrecov); 737*efd1a06fSDavid du Colombier assert(buf[1] >= 10); 738*efd1a06fSDavid du Colombier 739*efd1a06fSDavid du Colombier buf[2] = Erawre | Erarre; /* default: Erawre; can't set Ertb */ 740*efd1a06fSDavid du Colombier if (isbitset(Featedfctrpt, drive->features)) 741*efd1a06fSDavid du Colombier buf[7] = 1; /* emcdr: 1==recover, don't tell us; default 0 */ 742*efd1a06fSDavid du Colombier 743*efd1a06fSDavid du Colombier // buf[3] = 32; /* rd retry count; default 32; arbitrary */ 744*efd1a06fSDavid du Colombier // buf[8] = 1; /* wr retry count; default 1; arbitrary */ 745*efd1a06fSDavid du Colombier // memset(buf+9, 0, 3); /* err reporting win siz: 0 == no tsr; default 0 */ 746*efd1a06fSDavid du Colombier 747*efd1a06fSDavid du Colombier if (mmcsetpage(drive, Pagerrrecov, buf) < 0) { 748*efd1a06fSDavid du Colombier if (vflag) 749*efd1a06fSDavid du Colombier fprint(2, "error recovery NOT set\n"); 750*efd1a06fSDavid du Colombier return -1; 751*efd1a06fSDavid du Colombier } 752*efd1a06fSDavid du Colombier if (vflag) 753*efd1a06fSDavid du Colombier fprint(2, "error recovery set\n"); 754*efd1a06fSDavid du Colombier return 0; 755*efd1a06fSDavid du Colombier } 756*efd1a06fSDavid du Colombier 757*efd1a06fSDavid du Colombier char * 758*efd1a06fSDavid du Colombier featname(int feat) 759*efd1a06fSDavid du Colombier { 760*efd1a06fSDavid du Colombier Intfeat *ifp; 761*efd1a06fSDavid du Colombier 762*efd1a06fSDavid du Colombier for (ifp = intfeats; ifp < intfeats + nelem(intfeats); ifp++) 763*efd1a06fSDavid du Colombier if (feat == ifp->numb) 764*efd1a06fSDavid du Colombier return ifp->name; 765*efd1a06fSDavid du Colombier return nil; 766*efd1a06fSDavid du Colombier } 767*efd1a06fSDavid du Colombier 768*efd1a06fSDavid du Colombier static int 769*efd1a06fSDavid du Colombier prof2mmctype(Drive *drive, ushort prof) 770*efd1a06fSDavid du Colombier { 77160ba15acSDavid du Colombier if(prof == 0 || prof == 0xffff) /* none or whacko? */ 772*efd1a06fSDavid du Colombier return -1; 77360ba15acSDavid du Colombier if(drive->mmctype != Mmcnone) 774*efd1a06fSDavid du Colombier return -1; 77560ba15acSDavid du Colombier switch (prof >> 4) { 77660ba15acSDavid du Colombier case 0: 77760ba15acSDavid du Colombier drive->mmctype = Mmccd; 77860ba15acSDavid du Colombier break; 77960ba15acSDavid du Colombier case 1: 78060ba15acSDavid du Colombier if (prof == 0x1a || prof == 0x1b) 78160ba15acSDavid du Colombier drive->mmctype = Mmcdvdplus; 78260ba15acSDavid du Colombier else 78360ba15acSDavid du Colombier drive->mmctype = Mmcdvdminus; 78460ba15acSDavid du Colombier break; 78560ba15acSDavid du Colombier case 2: 78660ba15acSDavid du Colombier drive->mmctype = Mmcdvdplus; /* dual layer */ 78760ba15acSDavid du Colombier break; 78860ba15acSDavid du Colombier case 4: 78960ba15acSDavid du Colombier drive->mmctype = Mmcbd; 790*efd1a06fSDavid du Colombier if (vflag) 791*efd1a06fSDavid du Colombier fprint(2, "getconf profile for bd = %#x\n", prof); 79260ba15acSDavid du Colombier /* 79360ba15acSDavid du Colombier * further decode prof to set writability flags. 79460ba15acSDavid du Colombier * mostly for Pioneer BDR-206M. there may be unnecessary 795*efd1a06fSDavid du Colombier * future profiles for triple and quad layer; let's hope not. 79660ba15acSDavid du Colombier */ 79760ba15acSDavid du Colombier switch (prof) { 79860ba15acSDavid du Colombier case 0x40: 79960ba15acSDavid du Colombier drive->erasable = drive->recordable = No; 80060ba15acSDavid du Colombier case 0x41: 80160ba15acSDavid du Colombier case 0x42: 80260ba15acSDavid du Colombier drive->erasable = No; 80360ba15acSDavid du Colombier drive->recordable = Yes; 80460ba15acSDavid du Colombier break; 80560ba15acSDavid du Colombier case 0x43: 80660ba15acSDavid du Colombier drive->erasable = Yes; 80760ba15acSDavid du Colombier drive->recordable = No; 80860ba15acSDavid du Colombier break; 80960ba15acSDavid du Colombier } 81060ba15acSDavid du Colombier break; 81160ba15acSDavid du Colombier case 5: 81260ba15acSDavid du Colombier drive->mmctype = Mmcdvdminus; /* hd dvd, obs. */ 81360ba15acSDavid du Colombier break; 81460ba15acSDavid du Colombier } 815*efd1a06fSDavid du Colombier return 0; 816*efd1a06fSDavid du Colombier } 817*efd1a06fSDavid du Colombier 818*efd1a06fSDavid du Colombier static void 819*efd1a06fSDavid du Colombier notefeats(Drive *drive, uchar *p, ulong datalen) 820*efd1a06fSDavid du Colombier { 821*efd1a06fSDavid du Colombier int left, len; 822*efd1a06fSDavid du Colombier uint feat; 823*efd1a06fSDavid du Colombier char *ftnm; 824*efd1a06fSDavid du Colombier 825*efd1a06fSDavid du Colombier if (vflag) 826*efd1a06fSDavid du Colombier fprint(2, "features: "); 827*efd1a06fSDavid du Colombier for (left = datalen; left > 0; left -= len, p += len) { 828*efd1a06fSDavid du Colombier feat = p[0]<<8 | p[1]; 829*efd1a06fSDavid du Colombier len = 4 + p[3]; 830*efd1a06fSDavid du Colombier ftnm = featname(feat); 831*efd1a06fSDavid du Colombier if (vflag && ftnm) 832*efd1a06fSDavid du Colombier fprint(2, "%#ux (%s) curr %d\n", feat, ftnm, p[2] & 1); 833*efd1a06fSDavid du Colombier if (feat >= Maxfeatures) 834*efd1a06fSDavid du Colombier fprint(2, "feature %d too big for bit map\n", feat); 835*efd1a06fSDavid du Colombier else if (p[2] & 1) 836*efd1a06fSDavid du Colombier drive->features[feat/8] |= 1 << (feat%8); 837*efd1a06fSDavid du Colombier } 838*efd1a06fSDavid du Colombier } 839*efd1a06fSDavid du Colombier 840*efd1a06fSDavid du Colombier static int 841*efd1a06fSDavid du Colombier getconfcmd(Drive *drive, uchar *resp, int respsz) 842*efd1a06fSDavid du Colombier { 843*efd1a06fSDavid du Colombier int n; 844*efd1a06fSDavid du Colombier ulong datalen; 845*efd1a06fSDavid du Colombier uchar cmd[10]; 846*efd1a06fSDavid du Colombier 847*efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, Scmdgetconf); 848*efd1a06fSDavid du Colombier cmd[3] = 0; /* start with profile list feature */ 849*efd1a06fSDavid du Colombier cmd[7] = respsz >> 8; 850*efd1a06fSDavid du Colombier cmd[8] = respsz; 851*efd1a06fSDavid du Colombier n = scsi(drive, cmd, sizeof cmd, resp, respsz, Sread); 852*efd1a06fSDavid du Colombier if (n < 0) { 853*efd1a06fSDavid du Colombier if(vflag) 854*efd1a06fSDavid du Colombier fprint(2, "get config cmd failed\n"); 855*efd1a06fSDavid du Colombier return -1; 856*efd1a06fSDavid du Colombier } 857*efd1a06fSDavid du Colombier if (n < 4) 858*efd1a06fSDavid du Colombier return -1; 859*efd1a06fSDavid du Colombier datalen = GETBELONG(resp+0); 860*efd1a06fSDavid du Colombier if (datalen < 8) 861*efd1a06fSDavid du Colombier return -1; 862*efd1a06fSDavid du Colombier return datalen; 863*efd1a06fSDavid du Colombier } 864*efd1a06fSDavid du Colombier 865*efd1a06fSDavid du Colombier static int 866*efd1a06fSDavid du Colombier getconf(Drive *drive) 867*efd1a06fSDavid du Colombier { 868*efd1a06fSDavid du Colombier ushort prof; 869*efd1a06fSDavid du Colombier long datalen; 870*efd1a06fSDavid du Colombier uchar resp[64*1024 - 8]; /* 64k-8 max, 450 typical */ 871*efd1a06fSDavid du Colombier 872*efd1a06fSDavid du Colombier memset(drive->features, 0, sizeof drive->features); 873*efd1a06fSDavid du Colombier datalen = getconfcmd(drive, resp, sizeof resp); 874*efd1a06fSDavid du Colombier if(datalen < 0) 875*efd1a06fSDavid du Colombier return -1; 876*efd1a06fSDavid du Colombier 877*efd1a06fSDavid du Colombier /* 878*efd1a06fSDavid du Colombier * features start with an 8-byte header: 879*efd1a06fSDavid du Colombier * ulong datalen, ushort reserved, ushort current profile. 880*efd1a06fSDavid du Colombier * profile codes (table 92) are: 0 reserved, 1-7 legacy, 8-0xf cd, 881*efd1a06fSDavid du Colombier * 0x10-0x1f 0x2a-0x2b dvd*, 0x40-0x4f bd, 0x50-0x5f hd dvd, 882*efd1a06fSDavid du Colombier * 0xffff whacko. 883*efd1a06fSDavid du Colombier * 884*efd1a06fSDavid du Colombier * this is followed by multiple feature descriptors: 885*efd1a06fSDavid du Colombier * ushort code, uchar bits, uchar addnl_len, addnl_len bytes. 886*efd1a06fSDavid du Colombier */ 887*efd1a06fSDavid du Colombier prof = resp[6]<<8 | resp[7]; 888*efd1a06fSDavid du Colombier if (prof2mmctype(drive, prof) < 0) 889*efd1a06fSDavid du Colombier return datalen; 890*efd1a06fSDavid du Colombier 891*efd1a06fSDavid du Colombier /* 892*efd1a06fSDavid du Colombier * for bd-r, establish non-default recording mode before first write, 893*efd1a06fSDavid du Colombier * with FORMAT UNIT command to allocate spares (BD MMC set 894*efd1a06fSDavid du Colombier * description, v1.1, §4.4.4). 895*efd1a06fSDavid du Colombier */ 896*efd1a06fSDavid du Colombier if (drive->mmctype == Mmcbd && drive->recordable == Yes && 897*efd1a06fSDavid du Colombier drive->erasable == No) { 898*efd1a06fSDavid du Colombier format(drive); /* no-op with error if already been done */ 899*efd1a06fSDavid du Colombier /* read config again, now that bd-r has spares */ 900*efd1a06fSDavid du Colombier datalen = getconfcmd(drive, resp, sizeof resp); 901*efd1a06fSDavid du Colombier if(datalen < 0) 902*efd1a06fSDavid du Colombier return -1; 903*efd1a06fSDavid du Colombier } 904*efd1a06fSDavid du Colombier 905*efd1a06fSDavid du Colombier /* skip header to find feature list */ 906*efd1a06fSDavid du Colombier notefeats(drive, resp + 8, datalen - 8); 907*efd1a06fSDavid du Colombier 908*efd1a06fSDavid du Colombier if ((prof >> 4) == 4) 909*efd1a06fSDavid du Colombier seterrrecov(drive); 910*efd1a06fSDavid du Colombier return datalen; 911c038c065SDavid du Colombier } 912c038c065SDavid du Colombier 913c038c065SDavid du Colombier static int 914c038c065SDavid du Colombier getdvdstruct(Drive *drive) 915c038c065SDavid du Colombier { 916436f307dSDavid du Colombier int n, cat; 917c038c065SDavid du Colombier uchar cmd[12], resp[Pagesz]; 918c038c065SDavid du Colombier 919c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReadDVD); /* actually, read disc structure */ 920c038c065SDavid du Colombier cmd[1] = 0; /* media type: dvd */ 921c038c065SDavid du Colombier cmd[7] = 0; /* format code: physical format */ 922c038c065SDavid du Colombier cmd[8] = sizeof resp >> 8; /* allocation length */ 923c038c065SDavid du Colombier cmd[9] = sizeof resp; 924c038c065SDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread); 92560ba15acSDavid du Colombier if (n < 7) { 92660ba15acSDavid du Colombier if(vflag) 92760ba15acSDavid du Colombier fprint(2, "read disc structure (dvd) cmd failed\n"); 928c038c065SDavid du Colombier return -1; 92960ba15acSDavid du Colombier } 930436f307dSDavid du Colombier 931436f307dSDavid du Colombier /* resp[0..1] is resp length */ 932436f307dSDavid du Colombier cat = (resp[4] & 0xf0) >> 4; /* disk category, MMC-6 §6.22.3.2.1 */ 933c038c065SDavid du Colombier if (vflag) 934436f307dSDavid du Colombier fprint(2, "dvd type is %s\n", dvdtype[cat]); 9358ad6bb6aSDavid du Colombier drive->dvdtype = dvdtype[cat]; 936c038c065SDavid du Colombier /* write parameters mode page may suffice to compute writeok for dvd */ 93760ba15acSDavid du Colombier drive->erasable = drive->recordable = No; 938436f307dSDavid du Colombier /* 939436f307dSDavid du Colombier * the layer-type field is a *bit array*, 940436f307dSDavid du Colombier * though an enumeration of types would make more sense, 941436f307dSDavid du Colombier * since the types are exclusive, not orthogonal. 942436f307dSDavid du Colombier */ 943436f307dSDavid du Colombier if (resp[6] & (1<<2)) /* rewritable? */ 94460ba15acSDavid du Colombier drive->erasable = Yes; 945436f307dSDavid du Colombier else if (resp[6] & (1<<1)) /* recordable once? */ 94660ba15acSDavid du Colombier drive->recordable = Yes; 9477f1c8a1dSDavid du Colombier /* else it's a factory-pressed disk */ 948cdf9e71cSDavid du Colombier drive->mmctype = (cat >= 8? Mmcdvdplus: Mmcdvdminus); 949c038c065SDavid du Colombier return 0; 950c038c065SDavid du Colombier } 951c038c065SDavid du Colombier 95260ba15acSDavid du Colombier /* 95360ba15acSDavid du Colombier * ugly hack to divine device type from inquiry string as last resort. 95460ba15acSDavid du Colombier * mostly for Pioneer BDR-206M. 95560ba15acSDavid du Colombier */ 95660ba15acSDavid du Colombier static int 95760ba15acSDavid du Colombier bdguess(Drive *drive) 95860ba15acSDavid du Colombier { 95960ba15acSDavid du Colombier if (drive->mmctype == Mmcnone) { 96060ba15acSDavid du Colombier if (strstr(drive->Scsi.inquire, "BD") == nil) 96160ba15acSDavid du Colombier return -1; 96260ba15acSDavid du Colombier if (vflag) 96360ba15acSDavid du Colombier fprint(2, "drive probably a BD (from inquiry string)\n"); 96460ba15acSDavid du Colombier drive->mmctype = Mmcbd; 96560ba15acSDavid du Colombier } else if (drive->mmctype == Mmcbd) { 96660ba15acSDavid du Colombier if (drive->erasable != Unset && drive->recordable != Unset) 96760ba15acSDavid du Colombier return 0; 96860ba15acSDavid du Colombier } else 96960ba15acSDavid du Colombier return -1; 97060ba15acSDavid du Colombier 97160ba15acSDavid du Colombier drive->recordable = drive->writeok = No; 97260ba15acSDavid du Colombier if (strstr(drive->Scsi.inquire, "RW") != nil) { 97360ba15acSDavid du Colombier if (vflag) 97460ba15acSDavid du Colombier fprint(2, "drive probably a burner (from inquiry string)\n"); 97560ba15acSDavid du Colombier drive->recordable = drive->writeok = Yes; 97660ba15acSDavid du Colombier if (drive->erasable == Unset) { /* set by getdiscinfo perhaps */ 97760ba15acSDavid du Colombier drive->erasable = No; /* no way to tell, alas */ 97860ba15acSDavid du Colombier if (vflag) 979*efd1a06fSDavid du Colombier fprint(2, "\tassuming -r not -re\n"); 98060ba15acSDavid du Colombier } 98160ba15acSDavid du Colombier } else { 98260ba15acSDavid du Colombier if (drive->erasable == Unset) 98360ba15acSDavid du Colombier drive->erasable = No; 98460ba15acSDavid du Colombier } 98560ba15acSDavid du Colombier if (drive->erasable == Yes) 98660ba15acSDavid du Colombier drive->recordable = No; /* mutually exclusive */ 98760ba15acSDavid du Colombier return 0; 98860ba15acSDavid du Colombier } 98960ba15acSDavid du Colombier 990c038c065SDavid du Colombier static int 991c038c065SDavid du Colombier getbdstruct(Drive *drive) 992c038c065SDavid du Colombier { 993c038c065SDavid du Colombier int n; 99460ba15acSDavid du Colombier uchar cmd[12], resp[4+4096]; 99560ba15acSDavid du Colombier uchar *di, *body; 996c038c065SDavid du Colombier 997c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReadDVD); /* actually, read disc structure */ 998c038c065SDavid du Colombier cmd[1] = 1; /* media type: bd */ 99960ba15acSDavid du Colombier /* cmd[6] is layer #, 0 is first */ 1000c038c065SDavid du Colombier cmd[7] = 0; /* format code: disc info */ 1001c038c065SDavid du Colombier cmd[8] = sizeof resp >> 8; /* allocation length */ 1002c038c065SDavid du Colombier cmd[9] = sizeof resp; 1003c038c065SDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread); 100460ba15acSDavid du Colombier if(n < 0) { 100560ba15acSDavid du Colombier if(vflag) 100660ba15acSDavid du Colombier fprint(2, "read disc structure (bd) cmd failed\n"); 100760ba15acSDavid du Colombier return -1; 100860ba15acSDavid du Colombier } 100960ba15acSDavid du Colombier 1010436f307dSDavid du Colombier /* 101160ba15acSDavid du Colombier * resp[0..1] is resp length (4100); 2 & 3 are reserved. 101260ba15acSDavid du Colombier * there may be multiple disc info structs of 112 bytes each. 101360ba15acSDavid du Colombier * disc info (di) starts at 4. di[0..7] are header, followed by body. 101460ba15acSDavid du Colombier * body[0..2] is bd type (disc type identifier): 101560ba15acSDavid du Colombier * BDO|BDW|BDR, MMC-6 §6.22.3.3.1. The above scsi command should 1016436f307dSDavid du Colombier * fail on DVD drives, but some seem to ignore media type 1017436f307dSDavid du Colombier * and return successfully, so verify that it's a BD drive. 1018436f307dSDavid du Colombier */ 101960ba15acSDavid du Colombier di = resp + 4; 102060ba15acSDavid du Colombier body = di + 8; 102160ba15acSDavid du Colombier n -= 4 + 8; 102260ba15acSDavid du Colombier if (n < 3 || di[0] != 'D' || di[1] != 'I' || 102360ba15acSDavid du Colombier body[0] != 'B' || body[1] != 'D') { 1024c038c065SDavid du Colombier if(vflag) 102560ba15acSDavid du Colombier fprint(2, "it's not a bd\n"); 1026c038c065SDavid du Colombier return -1; 1027c038c065SDavid du Colombier } 102860ba15acSDavid du Colombier if (vflag) 102960ba15acSDavid du Colombier fprint(2, "read disc structure (bd) succeeded; di format %d\n", 103060ba15acSDavid du Colombier di[2]); 103160ba15acSDavid du Colombier 103260ba15acSDavid du Colombier drive->erasable = drive->recordable = No; 103360ba15acSDavid du Colombier switch (body[2]) { 103460ba15acSDavid du Colombier case 'O': /* read-Only */ 103560ba15acSDavid du Colombier break; 103660ba15acSDavid du Colombier case 'R': /* Recordable */ 103760ba15acSDavid du Colombier drive->recordable = Yes; 103860ba15acSDavid du Colombier break; 103960ba15acSDavid du Colombier case 'W': /* reWritable */ 104060ba15acSDavid du Colombier drive->erasable = Yes; 104160ba15acSDavid du Colombier break; 104260ba15acSDavid du Colombier default: 104360ba15acSDavid du Colombier fprint(2, "%s: unknown bd type BD%c\n", argv0, body[2]); 104460ba15acSDavid du Colombier return -1; 104560ba15acSDavid du Colombier } 1046*efd1a06fSDavid du Colombier /* printed getbdstruct: di bytes 98 di units/layers 32 */ 1047*efd1a06fSDavid du Colombier // fprint(2, "getbdstruct: di bytes %d di units/layers %d\n", di[6], di[3]); 1048cdf9e71cSDavid du Colombier drive->mmctype = Mmcbd; 1049c038c065SDavid du Colombier return 0; 1050c038c065SDavid du Colombier } 1051c038c065SDavid du Colombier 1052079e489fSDavid du Colombier /* 1053079e489fSDavid du Colombier * infer endings from the beginnings of other tracks. 1054079e489fSDavid du Colombier */ 1055*efd1a06fSDavid du Colombier static int 1056cdf9e71cSDavid du Colombier mmcinfertracks(Drive *drive, int first, int last) 1057cdf9e71cSDavid du Colombier { 1058cdf9e71cSDavid du Colombier int i; 1059cdf9e71cSDavid du Colombier uchar resp[1024]; 1060cdf9e71cSDavid du Colombier ulong tot; 1061cdf9e71cSDavid du Colombier Track *t; 1062cdf9e71cSDavid du Colombier 1063079e489fSDavid du Colombier if (vflag) 1064*efd1a06fSDavid du Colombier fprint(2, "inferring tracks from toc\n"); 1065cdf9e71cSDavid du Colombier for(i = first; i <= last; i++) { 1066cdf9e71cSDavid du Colombier memset(resp, 0, sizeof(resp)); 1067*efd1a06fSDavid du Colombier if(mmcreadtoc(drive, 0, i, resp, sizeof(resp)) < 0) { 1068*efd1a06fSDavid du Colombier last = i - 1; 1069*efd1a06fSDavid du Colombier if (last < 1) 1070*efd1a06fSDavid du Colombier last = 1; 1071cdf9e71cSDavid du Colombier break; 1072*efd1a06fSDavid du Colombier } 1073cdf9e71cSDavid du Colombier t = &drive->track[i-first]; 1074cdf9e71cSDavid du Colombier t->mtime = drive->changetime; 1075cdf9e71cSDavid du Colombier t->type = TypeData; 1076cdf9e71cSDavid du Colombier t->bs = BScdrom; 1077cdf9e71cSDavid du Colombier t->beg = bige(resp+8); 1078cdf9e71cSDavid du Colombier if(!(resp[5] & 4)) { 1079cdf9e71cSDavid du Colombier t->type = TypeAudio; 1080cdf9e71cSDavid du Colombier t->bs = BScdda; 1081cdf9e71cSDavid du Colombier } 1082cdf9e71cSDavid du Colombier } 1083cdf9e71cSDavid du Colombier 1084cdf9e71cSDavid du Colombier if((long)drive->track[0].beg < 0) /* i've seen negative track 0's */ 1085cdf9e71cSDavid du Colombier drive->track[0].beg = 0; 1086cdf9e71cSDavid du Colombier 1087cdf9e71cSDavid du Colombier tot = 0; 1088cdf9e71cSDavid du Colombier memset(resp, 0, sizeof(resp)); 1089cdf9e71cSDavid du Colombier /* 0xAA is lead-out */ 1090cdf9e71cSDavid du Colombier if(mmcreadtoc(drive, 0, 0xAA, resp, sizeof(resp)) < 0) 1091*efd1a06fSDavid du Colombier print("bad mmcreadtoc\n"); 1092cdf9e71cSDavid du Colombier if(resp[6]) 1093cdf9e71cSDavid du Colombier tot = bige(resp+8); 1094cdf9e71cSDavid du Colombier 1095*efd1a06fSDavid du Colombier t = nil; 1096cdf9e71cSDavid du Colombier for(i=last; i>=first; i--) { 1097cdf9e71cSDavid du Colombier t = &drive->track[i-first]; 1098cdf9e71cSDavid du Colombier t->end = tot; 1099cdf9e71cSDavid du Colombier tot = t->beg; 110073ee67a1SDavid du Colombier if(t->end <= t->beg) 110173ee67a1SDavid du Colombier t->beg = t->end = 0; 1102cdf9e71cSDavid du Colombier /* -2: skip lead out */ 1103cdf9e71cSDavid du Colombier t->size = (t->end - t->beg - 2) * (vlong)t->bs; 1104cdf9e71cSDavid du Colombier } 1105*efd1a06fSDavid du Colombier fprint(2, "infertracks: nwa should be %,lld; tot = %,ld\n", t->size, tot); 1106*efd1a06fSDavid du Colombier return last; 1107cdf9e71cSDavid du Colombier } 1108cdf9e71cSDavid du Colombier 1109*efd1a06fSDavid du Colombier static void 1110*efd1a06fSDavid du Colombier initdrive(Drive *drive) 11117dd7cddfSDavid du Colombier { 1112*efd1a06fSDavid du Colombier int i; 1113684b447eSDavid du Colombier Mmcaux *aux; 11147dd7cddfSDavid du Colombier 11157dd7cddfSDavid du Colombier drive->ntrack = 0; 11167dd7cddfSDavid du Colombier drive->nameok = 0; 11177dd7cddfSDavid du Colombier drive->nchange = drive->Scsi.nchange; 11187dd7cddfSDavid du Colombier drive->changetime = drive->Scsi.changetime; 111960ba15acSDavid du Colombier drive->writeok = No; 11207f1c8a1dSDavid du Colombier drive->erasable = drive->recordable = Unset; 11217f1c8a1dSDavid du Colombier getinvistrack(drive); 1122684b447eSDavid du Colombier aux = drive->aux; 11237f1c8a1dSDavid du Colombier aux->mmcnwa = -1; 1124684b447eSDavid du Colombier aux->nropen = aux->nwopen = 0; 1125684b447eSDavid du Colombier aux->ntotby = aux->ntotbk = 0; 11267dd7cddfSDavid du Colombier 11279a747e4fSDavid du Colombier for(i=0; i<nelem(drive->track); i++){ 11289a747e4fSDavid du Colombier memset(&drive->track[i].mbeg, 0, sizeof(Msf)); 11299a747e4fSDavid du Colombier memset(&drive->track[i].mend, 0, sizeof(Msf)); 11309a747e4fSDavid du Colombier } 1131*efd1a06fSDavid du Colombier } 1132*efd1a06fSDavid du Colombier 1133*efd1a06fSDavid du Colombier static long 1134*efd1a06fSDavid du Colombier computenwa(Drive *drive, int first, int last) 1135*efd1a06fSDavid du Colombier { 1136*efd1a06fSDavid du Colombier int i; 1137*efd1a06fSDavid du Colombier ulong nwa; 1138*efd1a06fSDavid du Colombier Track *t; 11399a747e4fSDavid du Colombier 11407dd7cddfSDavid du Colombier /* 1141*efd1a06fSDavid du Colombier * work through the tracks in reverse order (last to first). 1142*efd1a06fSDavid du Colombier * assume last is the to-be-written invisible track. 1143c038c065SDavid du Colombier */ 1144*efd1a06fSDavid du Colombier nwa = 0; 1145*efd1a06fSDavid du Colombier for(i=last-1; i>=first; i--) { 1146*efd1a06fSDavid du Colombier t = &drive->track[i-first]; 1147*efd1a06fSDavid du Colombier nwa += t->end - t->beg; 1148*efd1a06fSDavid du Colombier } 1149*efd1a06fSDavid du Colombier return nwa; 1150*efd1a06fSDavid du Colombier } 1151c038c065SDavid du Colombier 1152*efd1a06fSDavid du Colombier static int 1153*efd1a06fSDavid du Colombier findntracks(Drive *drive, int *firstp, int *lastp) 1154*efd1a06fSDavid du Colombier { 1155*efd1a06fSDavid du Colombier int i, n, first, last; 1156*efd1a06fSDavid du Colombier uchar resp[1024]; 1157*efd1a06fSDavid du Colombier 1158436f307dSDavid du Colombier if((n = mmcreadtoc(drive, Msfbit, 0, resp, sizeof(resp))) < 4) { 11597dd7cddfSDavid du Colombier /* 11607f1c8a1dSDavid du Colombier * it could be a blank disc. in case it's a blank disc in a 11617f1c8a1dSDavid du Colombier * cd-rw drive, use readdiscinfo to try to find the track info. 11627dd7cddfSDavid du Colombier */ 1163c038c065SDavid du Colombier if(getdiscinfo(drive, resp, sizeof(resp)) < 7) 11647dd7cddfSDavid du Colombier return -1; 1165c038c065SDavid du Colombier assert((resp[2] & 0340) == 0); /* data type 0 */ 11667dd7cddfSDavid du Colombier if(resp[4] != 1) 11677dd7cddfSDavid du Colombier print("multi-session disc %d\n", resp[4]); 1168*efd1a06fSDavid du Colombier drive->writeok = Yes; 1169*efd1a06fSDavid du Colombier 1170*efd1a06fSDavid du Colombier /* 1171*efd1a06fSDavid du Colombier * some drives (e.g., LG's BDs) report only 1 track on an 1172*efd1a06fSDavid du Colombier * unfinalized disc, so probe every track. the nwa reported 1173*efd1a06fSDavid du Colombier * by such drives may be wrong (e.g., 0), so compute that too. 1174*efd1a06fSDavid du Colombier */ 11757dd7cddfSDavid du Colombier first = resp[3]; 11767dd7cddfSDavid du Colombier last = resp[6]; 11777dd7cddfSDavid du Colombier if(vflag) 1178*efd1a06fSDavid du Colombier print("rdiscinfo tracks %d-%d (last==1 means TBD)\n", 1179*efd1a06fSDavid du Colombier first, last); 1180*efd1a06fSDavid du Colombier if(last == 1) 1181*efd1a06fSDavid du Colombier last = Maxresptracks; /* probe the tracks */ 11827dd7cddfSDavid du Colombier } else { 11837dd7cddfSDavid du Colombier first = resp[2]; 11847dd7cddfSDavid du Colombier last = resp[3]; 1185*efd1a06fSDavid du Colombier if(vflag) 1186*efd1a06fSDavid du Colombier print("toc tracks %d-%d (last==1 means TBD)\n", 1187*efd1a06fSDavid du Colombier first, last); 1188*efd1a06fSDavid du Colombier if(last == 1) 1189*efd1a06fSDavid du Colombier last = Maxresptracks; /* probe the tracks */ 11909a747e4fSDavid du Colombier 11919a747e4fSDavid du Colombier if(n >= 4+8*(last-first+2)) { 1192436f307dSDavid du Colombier /* resp[4 + i*8 + 2] is track # */ 1193c038c065SDavid du Colombier /* <=: track[last-first+1] = end */ 1194c038c065SDavid du Colombier for(i=0; i<=last-first+1; i++) 11959a747e4fSDavid du Colombier drive->track[i].mbeg = rdmsf(resp+4+i*8+5); 11969a747e4fSDavid du Colombier for(i=0; i<last-first+1; i++) 11979a747e4fSDavid du Colombier drive->track[i].mend = drive->track[i+1].mbeg; 11989a747e4fSDavid du Colombier } 11997dd7cddfSDavid du Colombier } 1200*efd1a06fSDavid du Colombier *firstp = first; 1201*efd1a06fSDavid du Colombier *lastp = last; 1202*efd1a06fSDavid du Colombier return 0; 1203*efd1a06fSDavid du Colombier } 12047dd7cddfSDavid du Colombier 1205*efd1a06fSDavid du Colombier static int 1206*efd1a06fSDavid du Colombier alltrackinfo(Drive *drive, int first, int last) 1207*efd1a06fSDavid du Colombier { 1208*efd1a06fSDavid du Colombier int i; 1209*efd1a06fSDavid du Colombier long nwa; 1210*efd1a06fSDavid du Colombier vlong cap; 1211*efd1a06fSDavid du Colombier Mmcaux *aux; 1212*efd1a06fSDavid du Colombier Track *track; 1213*efd1a06fSDavid du Colombier 1214*efd1a06fSDavid du Colombier for(i = first; i <= last; i++) 1215*efd1a06fSDavid du Colombier if (mmctrackinfo(drive, i, i - first) < 0) { 1216*efd1a06fSDavid du Colombier last = i - 1; 1217*efd1a06fSDavid du Colombier if (last < 1) 1218*efd1a06fSDavid du Colombier last = 1; 1219*efd1a06fSDavid du Colombier break; 1220*efd1a06fSDavid du Colombier } 1221*efd1a06fSDavid du Colombier track = &drive->track[last - first]; 1222*efd1a06fSDavid du Colombier drive->end = track->end; 1223*efd1a06fSDavid du Colombier if (vflag && drive->mmctype == Mmcbd) { 1224*efd1a06fSDavid du Colombier /* allow for lower apparent capacity due to reserved spares */ 1225*efd1a06fSDavid du Colombier cap = (vlong)track->end * track->bs; 1226*efd1a06fSDavid du Colombier if (cap >= 101*GB) 1227*efd1a06fSDavid du Colombier drive->laysfx = "-ql"; /* 128GB nominal */ 1228*efd1a06fSDavid du Colombier else if (cap >= 51*GB) 1229*efd1a06fSDavid du Colombier drive->laysfx = "-tl"; /* 100GB nominal */ 1230*efd1a06fSDavid du Colombier else if (cap >= 26*GB) 1231*efd1a06fSDavid du Colombier drive->laysfx = "-dl"; /* 50GB nominal */ 1232*efd1a06fSDavid du Colombier else 1233*efd1a06fSDavid du Colombier drive->laysfx = ""; /* 25GB nominal */ 1234*efd1a06fSDavid du Colombier fprint(2, "capacity %,lud sectors (%,lld bytes); bd%s\n", 1235*efd1a06fSDavid du Colombier track->end, cap, drive->laysfx); 1236*efd1a06fSDavid du Colombier } 1237*efd1a06fSDavid du Colombier nwa = computenwa(drive, first, last); 1238*efd1a06fSDavid du Colombier aux = drive->aux; 1239*efd1a06fSDavid du Colombier if (vflag) 1240*efd1a06fSDavid du Colombier fprint(2, "nwa from drive %,ld; computed nwa %,ld\n", 1241*efd1a06fSDavid du Colombier aux->mmcnwa, nwa); 1242*efd1a06fSDavid du Colombier if (aux->mmcnwa == -1) 1243*efd1a06fSDavid du Colombier fprint(2, "%s: disc is full\n", argv0); 1244*efd1a06fSDavid du Colombier /* reconcile differing nwas */ 1245*efd1a06fSDavid du Colombier if (aux->mmcnwa != nwa) { 1246*efd1a06fSDavid du Colombier fprint(2, "%s: nwa from drive %,ld != computed nwa %,ld\n", 1247*efd1a06fSDavid du Colombier argv0, aux->mmcnwa, nwa); 1248*efd1a06fSDavid du Colombier fprint(2, "\tbe careful! assuming computed nwa\n"); 1249*efd1a06fSDavid du Colombier /* the invisible track may still start at the old nwa. */ 1250*efd1a06fSDavid du Colombier // aux->mmcnwa = nwa; 1251*efd1a06fSDavid du Colombier } 1252*efd1a06fSDavid du Colombier return last; 1253*efd1a06fSDavid du Colombier } 1254*efd1a06fSDavid du Colombier 1255*efd1a06fSDavid du Colombier static int 1256*efd1a06fSDavid du Colombier findtracks(Drive *drive, int first, int last) 1257*efd1a06fSDavid du Colombier { 1258*efd1a06fSDavid du Colombier if(first == 0 && last == 0) 1259*efd1a06fSDavid du Colombier first = 1; 1260*efd1a06fSDavid du Colombier 1261*efd1a06fSDavid du Colombier if(first <= 0 || first >= Maxtrack) { 1262*efd1a06fSDavid du Colombier werrstr("first table %d not in range", first); 1263*efd1a06fSDavid du Colombier return -1; 1264*efd1a06fSDavid du Colombier } 1265*efd1a06fSDavid du Colombier if(last <= 0 || last >= Maxtrack) { 1266*efd1a06fSDavid du Colombier werrstr("last table %d not in range", last); 1267*efd1a06fSDavid du Colombier return -1; 1268*efd1a06fSDavid du Colombier } 1269*efd1a06fSDavid du Colombier 1270*efd1a06fSDavid du Colombier if(!(drive->cap & Cwrite)) 1271*efd1a06fSDavid du Colombier last = mmcinfertracks(drive, first, last); 1272*efd1a06fSDavid du Colombier else 1273*efd1a06fSDavid du Colombier last = alltrackinfo(drive, first, last); 1274*efd1a06fSDavid du Colombier drive->firsttrack = first; 1275*efd1a06fSDavid du Colombier drive->ntrack = last+1-first; 1276*efd1a06fSDavid du Colombier if (vflag) 1277*efd1a06fSDavid du Colombier fprint(2, "this time for sure: first %d last %d\n", first, last); 1278*efd1a06fSDavid du Colombier return 0; 1279*efd1a06fSDavid du Colombier } 1280*efd1a06fSDavid du Colombier 1281*efd1a06fSDavid du Colombier static void 1282*efd1a06fSDavid du Colombier finddisctype(Drive *drive, int first, int last) 1283*efd1a06fSDavid du Colombier { 128460ba15acSDavid du Colombier /* deduce disc type */ 1285cdf9e71cSDavid du Colombier drive->mmctype = Mmcnone; 12868ad6bb6aSDavid du Colombier drive->dvdtype = nil; 1287c038c065SDavid du Colombier getdvdstruct(drive); 128860ba15acSDavid du Colombier getconf(drive); 1289c038c065SDavid du Colombier getbdstruct(drive); 129060ba15acSDavid du Colombier if (Desperate) 129160ba15acSDavid du Colombier bdguess(drive); 1292cdf9e71cSDavid du Colombier if (drive->mmctype == Mmcnone) 1293cdf9e71cSDavid du Colombier drive->mmctype = Mmccd; /* by default */ 129460ba15acSDavid du Colombier if (drive->recordable == Yes || drive->erasable == Yes) 129560ba15acSDavid du Colombier drive->writeok = Yes; 1296c038c065SDavid du Colombier 1297c038c065SDavid du Colombier if (vflag) { 1298c038c065SDavid du Colombier fprint(2, "writeok %d", drive->writeok); 129960ba15acSDavid du Colombier if (drive->recordable != Unset) 1300c038c065SDavid du Colombier fprint(2, " recordable %d", drive->recordable); 130160ba15acSDavid du Colombier if (drive->erasable != Unset) 1302c038c065SDavid du Colombier fprint(2, " erasable %d", drive->erasable); 1303c038c065SDavid du Colombier fprint(2, "\n"); 1304*efd1a06fSDavid du Colombier fprint(2, "first %d last ", first); 1305*efd1a06fSDavid du Colombier if (last == Maxresptracks) 1306*efd1a06fSDavid du Colombier print("TBD\n"); 1307cdf9e71cSDavid du Colombier else 1308*efd1a06fSDavid du Colombier print("%d\n", last); 1309*efd1a06fSDavid du Colombier fprint(2, "it's a %s disc. (number of layers may be " 1310*efd1a06fSDavid du Colombier "undetermined so far.)\n\n", disctype(drive)); /* leak */ 1311*efd1a06fSDavid du Colombier } 1312*efd1a06fSDavid du Colombier } 13137dd7cddfSDavid du Colombier 1314*efd1a06fSDavid du Colombier /* this gets called a lot from main.c's 9P routines */ 1315*efd1a06fSDavid du Colombier static int 1316*efd1a06fSDavid du Colombier mmcgettoc(Drive *drive) 1317*efd1a06fSDavid du Colombier { 1318*efd1a06fSDavid du Colombier int first, last; 1319*efd1a06fSDavid du Colombier uchar resp[1024]; 1320*efd1a06fSDavid du Colombier 1321*efd1a06fSDavid du Colombier /* 1322*efd1a06fSDavid du Colombier * if someone has swapped the cd, 1323*efd1a06fSDavid du Colombier * mmcreadtoc will get ``medium changed'' and the scsi routines 1324*efd1a06fSDavid du Colombier * will set nchange and changetime in the scsi device. 1325*efd1a06fSDavid du Colombier */ 1326*efd1a06fSDavid du Colombier mmcreadtoc(drive, 0, 0, resp, sizeof(resp)); 1327*efd1a06fSDavid du Colombier if(drive->Scsi.changetime == 0) { /* no media present */ 1328*efd1a06fSDavid du Colombier drive->mmctype = Mmcnone; 1329*efd1a06fSDavid du Colombier drive->ntrack = 0; 13307dd7cddfSDavid du Colombier return 0; 13317dd7cddfSDavid du Colombier } 1332*efd1a06fSDavid du Colombier /* 1333*efd1a06fSDavid du Colombier * if the disc doesn't appear to be have been changed, and there 1334*efd1a06fSDavid du Colombier * is a disc in this drive, there's nothing to do (the common case). 1335*efd1a06fSDavid du Colombier */ 1336*efd1a06fSDavid du Colombier if(drive->nchange == drive->Scsi.nchange && drive->changetime != 0) 1337*efd1a06fSDavid du Colombier return 0; 1338*efd1a06fSDavid du Colombier 1339*efd1a06fSDavid du Colombier /* 1340*efd1a06fSDavid du Colombier * the disc in the drive may have just been changed, 1341*efd1a06fSDavid du Colombier * so rescan it and relearn all about it. 1342*efd1a06fSDavid du Colombier */ 1343*efd1a06fSDavid du Colombier if (vflag) 1344*efd1a06fSDavid du Colombier fprint(2, "\nnew disc in drive\n"); 1345*efd1a06fSDavid du Colombier initdrive(drive); 1346*efd1a06fSDavid du Colombier 1347*efd1a06fSDavid du Colombier if (findntracks(drive, &first, &last) < 0) 1348*efd1a06fSDavid du Colombier return -1; 1349*efd1a06fSDavid du Colombier /* 1350*efd1a06fSDavid du Colombier * we could try calling findtracks before finddisctype so that 1351*efd1a06fSDavid du Colombier * we can `format' bd-rs at the right capacity with explicit spares 1352*efd1a06fSDavid du Colombier * ratio given. 1353*efd1a06fSDavid du Colombier */ 1354*efd1a06fSDavid du Colombier finddisctype(drive, first, last); 1355*efd1a06fSDavid du Colombier return findtracks(drive, first, last); 1356*efd1a06fSDavid du Colombier } 13577dd7cddfSDavid du Colombier 13580599ba09SDavid du Colombier static void 13590599ba09SDavid du Colombier settrkmode(uchar *p, int mode) 13600599ba09SDavid du Colombier { 13610599ba09SDavid du Colombier p[Wptrkmode] &= ~0xf; 13620599ba09SDavid du Colombier p[Wptrkmode] |= mode; 13630599ba09SDavid du Colombier } 13640599ba09SDavid du Colombier 1365e67f3b95SDavid du Colombier /* 1366e67f3b95SDavid du Colombier * this uses page 5, which is optional. 1367e67f3b95SDavid du Colombier */ 13687dd7cddfSDavid du Colombier static int 13697dd7cddfSDavid du Colombier mmcsetbs(Drive *drive, int bs) 13707dd7cddfSDavid du Colombier { 13717dd7cddfSDavid du Colombier uchar *p; 13727dd7cddfSDavid du Colombier Mmcaux *aux; 13737dd7cddfSDavid du Colombier 13747dd7cddfSDavid du Colombier aux = drive->aux; 1375e67f3b95SDavid du Colombier if (!aux->page05ok) 1376e67f3b95SDavid du Colombier return 0; /* harmless; assume 2k */ 13777dd7cddfSDavid du Colombier 13787dd7cddfSDavid du Colombier p = aux->page05; 1379c038c065SDavid du Colombier /* 1380c038c065SDavid du Colombier * establish defaults. 1381c038c065SDavid du Colombier */ 1382436f307dSDavid du Colombier p[0] &= 077; /* clear reserved bits, MMC-6 §7.2.3 */ 13830599ba09SDavid du Colombier p[Wpwrtype] = Bufe | Wttrackonce; 13840599ba09SDavid du Colombier // if(testonlyflag) 13850599ba09SDavid du Colombier // p[Wpwrtype] |= 0x10; /* test-write */ 13860599ba09SDavid du Colombier /* assume dvd values as defaults */ 13870599ba09SDavid du Colombier settrkmode(p, Tmintr); 13880599ba09SDavid du Colombier p[Wptrkmode] |= Msnext; 13890599ba09SDavid du Colombier p[Wpdatblktype] = Db2kdata; 13900599ba09SDavid du Colombier p[Wpsessfmt] = Sfdata; 13910599ba09SDavid du Colombier 1392cdf9e71cSDavid du Colombier switch(drive->mmctype) { 13930599ba09SDavid du Colombier case Mmcdvdplus: 13940599ba09SDavid du Colombier case Mmcbd: 13957dd7cddfSDavid du Colombier break; 13960599ba09SDavid du Colombier case Mmcdvdminus: 13970599ba09SDavid du Colombier /* dvd-r can only do disc-at-once or incremental */ 13980599ba09SDavid du Colombier p[Wpwrtype] = Bufe | Wtsessonce; 13990599ba09SDavid du Colombier break; 14000599ba09SDavid du Colombier case Mmccd: 14010599ba09SDavid du Colombier settrkmode(p, Tmunintr); /* data track, uninterrupted */ 14020599ba09SDavid du Colombier switch(bs){ 14037dd7cddfSDavid du Colombier case BScdda: 1404c038c065SDavid du Colombier /* 2 audio channels without pre-emphasis */ 140560ba15acSDavid du Colombier settrkmode(p, Tmcdda); /* should be Tm2audio? */ 14060599ba09SDavid du Colombier p[Wpdatblktype] = Dbraw; 14070599ba09SDavid du Colombier break; 14080599ba09SDavid du Colombier case BScdrom: 14097dd7cddfSDavid du Colombier break; 14107dd7cddfSDavid du Colombier case BScdxa: 14110599ba09SDavid du Colombier p[Wpdatblktype] = Db2336; 14120599ba09SDavid du Colombier p[Wpsessfmt] = Sfcdxa; 14137dd7cddfSDavid du Colombier break; 14147dd7cddfSDavid du Colombier default: 1415c038c065SDavid du Colombier fprint(2, "%s: unknown CD type; bs %d\n", argv0, bs); 14167dd7cddfSDavid du Colombier assert(0); 14177dd7cddfSDavid du Colombier } 1418c038c065SDavid du Colombier break; 1419c038c065SDavid du Colombier default: 1420c038c065SDavid du Colombier fprint(2, "%s: unknown disc sub-type %d\n", 1421cdf9e71cSDavid du Colombier argv0, drive->mmctype); 1422c038c065SDavid du Colombier break; 1423c038c065SDavid du Colombier } 1424c038c065SDavid du Colombier if(mmcsetpage(drive, Pagwrparams, p) < 0) { 1425c038c065SDavid du Colombier if (vflag) 1426c038c065SDavid du Colombier fprint(2, "mmcsetbs: could NOT set write parameters page\n"); 14277dd7cddfSDavid du Colombier return -1; 1428c038c065SDavid du Colombier } 1429ec46fab0SDavid du Colombier return 0; 14307dd7cddfSDavid du Colombier } 14317dd7cddfSDavid du Colombier 1432*efd1a06fSDavid du Colombier /* 1433*efd1a06fSDavid du Colombier * `read cd' only works for CDs; for everybody else, 1434*efd1a06fSDavid du Colombier * we'll try plain `read (12)'. only use read cd if it's 1435*efd1a06fSDavid du Colombier * a cd drive with a cd in it and we're not reading data 1436*efd1a06fSDavid du Colombier * (e.g., reading audio). 1437*efd1a06fSDavid du Colombier */ 1438*efd1a06fSDavid du Colombier static int 1439*efd1a06fSDavid du Colombier fillread12cdb(Drive *drive, uchar *cmd, long nblock, ulong off, int bs) 1440*efd1a06fSDavid du Colombier { 1441*efd1a06fSDavid du Colombier if (drive->type == TypeCD && drive->mmctype == Mmccd && bs != BScdrom) { 1442*efd1a06fSDavid du Colombier initcdb(cmd, 12, ScmdReadcd); 1443*efd1a06fSDavid du Colombier cmd[6] = nblock>>16; 1444*efd1a06fSDavid du Colombier cmd[7] = nblock>>8; 1445*efd1a06fSDavid du Colombier cmd[8] = nblock>>0; 1446*efd1a06fSDavid du Colombier cmd[9] = 0x10; 1447*efd1a06fSDavid du Colombier switch(bs){ 1448*efd1a06fSDavid du Colombier case BScdda: 1449*efd1a06fSDavid du Colombier cmd[1] = 0x04; 1450*efd1a06fSDavid du Colombier break; 1451*efd1a06fSDavid du Colombier case BScdrom: 1452*efd1a06fSDavid du Colombier cmd[1] = 0x08; 1453*efd1a06fSDavid du Colombier break; 1454*efd1a06fSDavid du Colombier case BScdxa: 1455*efd1a06fSDavid du Colombier cmd[1] = 0x0C; 1456*efd1a06fSDavid du Colombier break; 1457*efd1a06fSDavid du Colombier default: 1458*efd1a06fSDavid du Colombier werrstr("unknown bs %d", bs); 1459*efd1a06fSDavid du Colombier return -1; 1460*efd1a06fSDavid du Colombier } 1461*efd1a06fSDavid du Colombier } else { /* e.g., TypeDA */ 1462*efd1a06fSDavid du Colombier initcdb(cmd, 12, ScmdRead12); 1463*efd1a06fSDavid du Colombier cmd[6] = nblock>>24; 1464*efd1a06fSDavid du Colombier cmd[7] = nblock>>16; 1465*efd1a06fSDavid du Colombier cmd[8] = nblock>>8; 1466*efd1a06fSDavid du Colombier cmd[9] = nblock; 1467*efd1a06fSDavid du Colombier // cmd[10] = 0x80; /* streaming */ 1468*efd1a06fSDavid du Colombier } 1469*efd1a06fSDavid du Colombier cmd[2] = off>>24; 1470*efd1a06fSDavid du Colombier cmd[3] = off>>16; 1471*efd1a06fSDavid du Colombier cmd[4] = off>>8; 1472*efd1a06fSDavid du Colombier cmd[5] = off>>0; 1473*efd1a06fSDavid du Colombier return 0; 1474*efd1a06fSDavid du Colombier } 1475*efd1a06fSDavid du Colombier 147673ee67a1SDavid du Colombier /* off is a block number */ 14777dd7cddfSDavid du Colombier static long 1478dd12a5c6SDavid du Colombier mmcread(Buf *buf, void *v, long nblock, ulong off) 14797dd7cddfSDavid du Colombier { 14807dd7cddfSDavid du Colombier int bs; 14817dd7cddfSDavid du Colombier long n, nn; 148273ee67a1SDavid du Colombier uchar cmd[12]; 148373ee67a1SDavid du Colombier Drive *drive; 14847dd7cddfSDavid du Colombier Otrack *o; 14857dd7cddfSDavid du Colombier 14867dd7cddfSDavid du Colombier o = buf->otrack; 14877dd7cddfSDavid du Colombier drive = o->drive; 14887dd7cddfSDavid du Colombier bs = o->track->bs; 14897dd7cddfSDavid du Colombier off += o->track->beg; 14907dd7cddfSDavid du Colombier 14917dd7cddfSDavid du Colombier if(nblock >= (1<<10)) { 14927dd7cddfSDavid du Colombier werrstr("mmcread too big"); 14937dd7cddfSDavid du Colombier if(vflag) 14947dd7cddfSDavid du Colombier fprint(2, "mmcread too big\n"); 14957dd7cddfSDavid du Colombier return -1; 14967dd7cddfSDavid du Colombier } 14977dd7cddfSDavid du Colombier 149859cc4ca5SDavid du Colombier /* truncate nblock modulo size of track */ 149959cc4ca5SDavid du Colombier if(off > o->track->end - 2) { 150059cc4ca5SDavid du Colombier werrstr("read past end of track"); 150159cc4ca5SDavid du Colombier if(vflag) 1502e67f3b95SDavid du Colombier fprint(2, "end of track (%ld->%ld off %ld)", 1503e67f3b95SDavid du Colombier o->track->beg, o->track->end - 2, off); 150459cc4ca5SDavid du Colombier return -1; 150559cc4ca5SDavid du Colombier } 150659cc4ca5SDavid du Colombier if(off == o->track->end - 2) 150759cc4ca5SDavid du Colombier return 0; 150859cc4ca5SDavid du Colombier 150959cc4ca5SDavid du Colombier if(off+nblock > o->track->end - 2) 151059cc4ca5SDavid du Colombier nblock = o->track->end - 2 - off; 151159cc4ca5SDavid du Colombier 1512*efd1a06fSDavid du Colombier if (fillread12cdb(drive, cmd, nblock, off, bs) < 0) 15137dd7cddfSDavid du Colombier return -1; 1514*efd1a06fSDavid du Colombier 15157dd7cddfSDavid du Colombier n = nblock*bs; 15167dd7cddfSDavid du Colombier nn = scsi(drive, cmd, sizeof(cmd), v, n, Sread); 15177dd7cddfSDavid du Colombier if(nn != n) { 151864c7f6b6SDavid du Colombier if(nn != -1) 15197dd7cddfSDavid du Colombier werrstr("short read %ld/%ld", nn, n); 15207dd7cddfSDavid du Colombier if(vflag) 1521e67f3b95SDavid du Colombier print("read off %lud nblock %ld bs %d failed\n", 1522e67f3b95SDavid du Colombier off, nblock, bs); 15237dd7cddfSDavid du Colombier return -1; 15247dd7cddfSDavid du Colombier } 15257dd7cddfSDavid du Colombier return nblock; 15267dd7cddfSDavid du Colombier } 15277dd7cddfSDavid du Colombier 15287dd7cddfSDavid du Colombier static Otrack* 15297dd7cddfSDavid du Colombier mmcopenrd(Drive *drive, int trackno) 15307dd7cddfSDavid du Colombier { 15317dd7cddfSDavid du Colombier Otrack *o; 15327dd7cddfSDavid du Colombier Mmcaux *aux; 15337dd7cddfSDavid du Colombier 15347dd7cddfSDavid du Colombier if(trackno < 0 || trackno >= drive->ntrack) { 15357dd7cddfSDavid du Colombier werrstr("track number out of range"); 15367dd7cddfSDavid du Colombier return nil; 15377dd7cddfSDavid du Colombier } 15387dd7cddfSDavid du Colombier 15397dd7cddfSDavid du Colombier aux = drive->aux; 15407dd7cddfSDavid du Colombier if(aux->nwopen) { 15417dd7cddfSDavid du Colombier werrstr("disk in use for writing"); 15427dd7cddfSDavid du Colombier return nil; 15437dd7cddfSDavid du Colombier } 15447dd7cddfSDavid du Colombier 15457dd7cddfSDavid du Colombier o = emalloc(sizeof(Otrack)); 15467dd7cddfSDavid du Colombier o->drive = drive; 15477dd7cddfSDavid du Colombier o->track = &drive->track[trackno]; 15487dd7cddfSDavid du Colombier o->nchange = drive->nchange; 15497dd7cddfSDavid du Colombier o->omode = OREAD; 15507386956aSDavid du Colombier o->buf = bopen(mmcread, OREAD, o->track->bs, Readblock); 15517dd7cddfSDavid du Colombier o->buf->otrack = o; 15527dd7cddfSDavid du Colombier 15537dd7cddfSDavid du Colombier aux->nropen++; 15547dd7cddfSDavid du Colombier 15557dd7cddfSDavid du Colombier return o; 15567dd7cddfSDavid du Colombier } 15577dd7cddfSDavid du Colombier 1558*efd1a06fSDavid du Colombier enum { 1559*efd1a06fSDavid du Colombier Flhlen = 4, /* format list header length */ 1560*efd1a06fSDavid du Colombier Fdlen = 8, /* format descriptor length */ 1561*efd1a06fSDavid du Colombier 1562*efd1a06fSDavid du Colombier /* format unit cdb[1] */ 1563*efd1a06fSDavid du Colombier Formatdata = 0x10, 1564*efd1a06fSDavid du Colombier 1565*efd1a06fSDavid du Colombier /* format list header [1] */ 1566*efd1a06fSDavid du Colombier Immed = 1<<1, /* immediate return, don't wait */ 1567*efd1a06fSDavid du Colombier 1568*efd1a06fSDavid du Colombier /* format descriptor [4] */ 1569*efd1a06fSDavid du Colombier Fmtfull = 0, 1570*efd1a06fSDavid du Colombier Fullbdrsrm = 0, /* sequential */ 1571*efd1a06fSDavid du Colombier Fullbdrsrmspares= 1, /* + spares for defect mgmt. */ 1572*efd1a06fSDavid du Colombier Fullbdrrrm = 2, /* random */ 1573*efd1a06fSDavid du Colombier Fmtbdrspares = 0x32 << 2, 1574*efd1a06fSDavid du Colombier Bdrsparessrmplus= 0, /* srm+pow */ 1575*efd1a06fSDavid du Colombier Bdrsparessrmminus=1, /* srm-pow */ 1576*efd1a06fSDavid du Colombier Bdrsparesrrm = 2, /* random */ 1577*efd1a06fSDavid du Colombier }; 1578*efd1a06fSDavid du Colombier 1579c038c065SDavid du Colombier static int 1580c038c065SDavid du Colombier format(Drive *drive) 1581c038c065SDavid du Colombier { 1582*efd1a06fSDavid du Colombier int setblksz; 1583cdf9e71cSDavid du Colombier ulong nblks, blksize; 1584c038c065SDavid du Colombier uchar *fmtdesc; 1585*efd1a06fSDavid du Colombier uchar cmd[6], parms[Flhlen+Fdlen]; 1586c038c065SDavid du Colombier 158760ba15acSDavid du Colombier if (drive->recordable == Yes && drive->mmctype != Mmcbd) { 1588cdf9e71cSDavid du Colombier werrstr("don't format write-once cd or dvd media"); 1589cdf9e71cSDavid du Colombier return -1; 1590cdf9e71cSDavid du Colombier } 1591c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdFormat); /* format unit */ 1592*efd1a06fSDavid du Colombier cmd[1] = Formatdata | 1; /* mmc format code 1 */ 1593c038c065SDavid du Colombier 1594*efd1a06fSDavid du Colombier /* parms is format list header & format descriptor */ 1595c038c065SDavid du Colombier memset(parms, 0, sizeof parms); 1596c038c065SDavid du Colombier 1597*efd1a06fSDavid du Colombier /* format list header */ 1598*efd1a06fSDavid du Colombier parms[1] = Immed; 1599*efd1a06fSDavid du Colombier parms[3] = Fdlen; 1600*efd1a06fSDavid du Colombier 1601*efd1a06fSDavid du Colombier fmtdesc = parms + Flhlen; 1602*efd1a06fSDavid du Colombier fmtdesc[4] = Fmtfull; 1603*efd1a06fSDavid du Colombier /* 0-3 are big-endian # of blocks, 5-7 format-dependent data */ 1604cdf9e71cSDavid du Colombier 1605cdf9e71cSDavid du Colombier nblks = 0; 1606cdf9e71cSDavid du Colombier blksize = BScdrom; 1607*efd1a06fSDavid du Colombier setblksz = 1; 1608cdf9e71cSDavid du Colombier switch (drive->mmctype) { 1609cdf9e71cSDavid du Colombier case Mmccd: 1610cdf9e71cSDavid du Colombier nblks = 0; 1611cdf9e71cSDavid du Colombier break; 1612cdf9e71cSDavid du Colombier case Mmcdvdplus: 1613cdf9e71cSDavid du Colombier /* format type 0 is optional but equiv to format type 0x26 */ 1614cdf9e71cSDavid du Colombier fmtdesc[4] = 0x26 << 2; 1615cdf9e71cSDavid du Colombier nblks = ~0ul; 1616cdf9e71cSDavid du Colombier blksize = 0; 1617cdf9e71cSDavid du Colombier break; 1618*efd1a06fSDavid du Colombier case Mmcbd: 1619*efd1a06fSDavid du Colombier /* enable BD-R defect mgmt. */ 1620*efd1a06fSDavid du Colombier if (drive->recordable == Yes && drive->erasable == No) { 1621*efd1a06fSDavid du Colombier parms[1] &= ~Immed; 1622*efd1a06fSDavid du Colombier fmtdesc[4] |= Fullbdrsrmspares; 1623*efd1a06fSDavid du Colombier setblksz = 0; 1624*efd1a06fSDavid du Colombier } 1625*efd1a06fSDavid du Colombier break; 1626cdf9e71cSDavid du Colombier } 1627cdf9e71cSDavid du Colombier 1628cdf9e71cSDavid du Colombier PUTBELONG(fmtdesc, nblks); 1629*efd1a06fSDavid du Colombier if (setblksz) 1630cdf9e71cSDavid du Colombier PUTBE24(fmtdesc + 5, blksize); 1631cdf9e71cSDavid du Colombier 1632cdf9e71cSDavid du Colombier // print("format parameters:\n"); 1633cdf9e71cSDavid du Colombier // hexdump(parms, sizeof parms); 1634c038c065SDavid du Colombier 1635c038c065SDavid du Colombier if(vflag) 1636c038c065SDavid du Colombier print("%lld ns: format\n", nsec()); 1637c038c065SDavid du Colombier return scsi(drive, cmd, sizeof(cmd), parms, sizeof parms, Swrite); 1638c038c065SDavid du Colombier } 1639c038c065SDavid du Colombier 16407dd7cddfSDavid du Colombier static long 164159cc4ca5SDavid du Colombier mmcxwrite(Otrack *o, void *v, long nblk) 16427dd7cddfSDavid du Colombier { 16437f1c8a1dSDavid du Colombier int r; 16447dd7cddfSDavid du Colombier uchar cmd[10]; 16457dd7cddfSDavid du Colombier Mmcaux *aux; 16467dd7cddfSDavid du Colombier 16477dd7cddfSDavid du Colombier assert(o->omode == OWRITE); 16487dd7cddfSDavid du Colombier aux = o->drive->aux; 16497f1c8a1dSDavid du Colombier if (aux->mmcnwa == -1 && scsiready(o->drive) < 0) { 1650684b447eSDavid du Colombier werrstr("device not ready to write"); 1651684b447eSDavid du Colombier return -1; 1652684b447eSDavid du Colombier } 1653*efd1a06fSDavid du Colombier if (aux->mmcnwa == -1 || 1654*efd1a06fSDavid du Colombier o->drive->end != 0 && aux->mmcnwa + nblk > o->drive->end) { 1655*efd1a06fSDavid du Colombier werrstr("writing past last block"); 1656*efd1a06fSDavid du Colombier return -1; 1657*efd1a06fSDavid du Colombier } 1658*efd1a06fSDavid du Colombier if (nblk <= 0) 1659*efd1a06fSDavid du Colombier fprint(2, "mmcxwrite: nblk %ld <= 0\n", nblk); 16607dd7cddfSDavid du Colombier aux->ntotby += nblk*o->track->bs; 16617dd7cddfSDavid du Colombier aux->ntotbk += nblk; 1662c038c065SDavid du Colombier 1663*efd1a06fSDavid du Colombier while (scsiready(o->drive) < 0) /* paranoia */ 1664*efd1a06fSDavid du Colombier sleep(0); 1665*efd1a06fSDavid du Colombier 1666*efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdExtwritever); 16677dd7cddfSDavid du Colombier cmd[2] = aux->mmcnwa>>24; 16687dd7cddfSDavid du Colombier cmd[3] = aux->mmcnwa>>16; 16697dd7cddfSDavid du Colombier cmd[4] = aux->mmcnwa>>8; 16707dd7cddfSDavid du Colombier cmd[5] = aux->mmcnwa; 16717dd7cddfSDavid du Colombier cmd[7] = nblk>>8; 16727dd7cddfSDavid du Colombier cmd[8] = nblk>>0; 1673*efd1a06fSDavid du Colombier if(vflag > 1) 1674*efd1a06fSDavid du Colombier print("%lld ns: write+verify %ld at %#lux\n", 1675684b447eSDavid du Colombier nsec(), nblk, aux->mmcnwa); 1676*efd1a06fSDavid du Colombier /* 1677*efd1a06fSDavid du Colombier * we are using a private copy of scsi.c that doesn't retry writes 1678*efd1a06fSDavid du Colombier * to ensure that the write-verify command is issued exactly once. 1679*efd1a06fSDavid du Colombier * the drive may perform internal defect management on write errors, 1680*efd1a06fSDavid du Colombier * but explicit attempts to rewrite a given sector on write-once 1681*efd1a06fSDavid du Colombier * media are guaranteed to fail. 1682*efd1a06fSDavid du Colombier */ 16837f1c8a1dSDavid du Colombier r = scsi(o->drive, cmd, sizeof(cmd), v, nblk*o->track->bs, Swrite); 16847f1c8a1dSDavid du Colombier if (r < 0) 1685*efd1a06fSDavid du Colombier fprint(2, "%s: write+verify error at blk offset %,ld = " 16867f1c8a1dSDavid du Colombier "offset %,lld / bs %ld: %r\n", 16877f1c8a1dSDavid du Colombier argv0, aux->mmcnwa, (vlong)aux->mmcnwa * o->track->bs, 16887f1c8a1dSDavid du Colombier o->track->bs); 1689*efd1a06fSDavid du Colombier else 16907dd7cddfSDavid du Colombier aux->mmcnwa += nblk; 16917f1c8a1dSDavid du Colombier return r; 16927dd7cddfSDavid du Colombier } 16937dd7cddfSDavid du Colombier 169459cc4ca5SDavid du Colombier static long 1695dd12a5c6SDavid du Colombier mmcwrite(Buf *buf, void *v, long nblk, ulong) 169659cc4ca5SDavid du Colombier { 169759cc4ca5SDavid du Colombier return mmcxwrite(buf->otrack, v, nblk); 169859cc4ca5SDavid du Colombier } 169959cc4ca5SDavid du Colombier 17000599ba09SDavid du Colombier enum { 17010599ba09SDavid du Colombier Eccblk = 128, /* sectors per ecc block */ 17020599ba09SDavid du Colombier Rsvslop = 0, 17030599ba09SDavid du Colombier }; 17040599ba09SDavid du Colombier 17050599ba09SDavid du Colombier static int 17060599ba09SDavid du Colombier reserve(Drive *drive, int track) 17070599ba09SDavid du Colombier { 17080599ba09SDavid du Colombier ulong sz; 17090599ba09SDavid du Colombier uchar cmd[10]; 17100599ba09SDavid du Colombier 17110599ba09SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReserve); 17120599ba09SDavid du Colombier track -= drive->firsttrack; /* switch to zero-origin */ 17130599ba09SDavid du Colombier if (track >= 0 && track < drive->ntrack) 17140599ba09SDavid du Colombier /* .end is next sector past sz */ 17150599ba09SDavid du Colombier sz = drive->track[track].end - drive->track[track].beg - Rsvslop; 17160599ba09SDavid du Colombier else { 17170599ba09SDavid du Colombier sz = Eccblk; 17180599ba09SDavid du Colombier fprint(2, "%s: reserve: track #%d out of range 0-%d\n", 17190599ba09SDavid du Colombier argv0, track, drive->ntrack); 17200599ba09SDavid du Colombier } 17210599ba09SDavid du Colombier sz -= sz % Eccblk; /* round down to ecc-block multiple */ 17220599ba09SDavid du Colombier if ((long)sz < 0) { 17230599ba09SDavid du Colombier fprint(2, "%s: reserve: bogus size %lud\n", argv0, sz); 17240599ba09SDavid du Colombier return -1; 17250599ba09SDavid du Colombier } 17260599ba09SDavid du Colombier cmd[1] = 0; /* no ASRV: allocate by size not lba */ 17270599ba09SDavid du Colombier PUTBELONG(cmd + 2 + 3, sz); 17280599ba09SDavid du Colombier if (vflag) 17290599ba09SDavid du Colombier fprint(2, "reserving %ld sectors\n", sz); 17300599ba09SDavid du Colombier return scsi(drive, cmd, sizeof cmd, cmd, 0, Snone); 17310599ba09SDavid du Colombier } 17320599ba09SDavid du Colombier 1733684b447eSDavid du Colombier static int 1734684b447eSDavid du Colombier getinvistrack(Drive *drive) 1735684b447eSDavid du Colombier { 1736684b447eSDavid du Colombier int n; 1737684b447eSDavid du Colombier uchar cmd[10], resp[Pagesz]; 1738684b447eSDavid du Colombier 1739684b447eSDavid du Colombier initcdb(cmd, sizeof(cmd), ScmdRtrackinfo); 1740684b447eSDavid du Colombier cmd[1] = 1<<2 | 1; /* open; address below is logical track # */ 1741684b447eSDavid du Colombier PUTBELONG(cmd + 2, 1); /* find first open track */ 1742684b447eSDavid du Colombier cmd[7] = sizeof(resp)>>8; 1743684b447eSDavid du Colombier cmd[8] = sizeof(resp); 1744684b447eSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread); 1745684b447eSDavid du Colombier if(n < 4) { 1746684b447eSDavid du Colombier if(vflag) 1747684b447eSDavid du Colombier print("trackinfo for invis track fails n=%d: %r\n", n); 1748684b447eSDavid du Colombier return -1; 1749684b447eSDavid du Colombier } 1750684b447eSDavid du Colombier 1751684b447eSDavid du Colombier if(vflag) 1752684b447eSDavid du Colombier print("getinvistrack: track #%d session #%d\n", 1753684b447eSDavid du Colombier resp[2], resp[3]); 17547f1c8a1dSDavid du Colombier drive->invistrack = resp[2]; 1755684b447eSDavid du Colombier return resp[2]; 1756684b447eSDavid du Colombier } 1757684b447eSDavid du Colombier 17587dd7cddfSDavid du Colombier static Otrack* 17597dd7cddfSDavid du Colombier mmccreate(Drive *drive, int type) 17607dd7cddfSDavid du Colombier { 17617f1c8a1dSDavid du Colombier int bs; 17627dd7cddfSDavid du Colombier Mmcaux *aux; 17637dd7cddfSDavid du Colombier Track *t; 17647dd7cddfSDavid du Colombier Otrack *o; 17657dd7cddfSDavid du Colombier 17667dd7cddfSDavid du Colombier aux = drive->aux; 17677dd7cddfSDavid du Colombier 17687dd7cddfSDavid du Colombier if(aux->nropen || aux->nwopen) { 17697dd7cddfSDavid du Colombier werrstr("drive in use"); 17707dd7cddfSDavid du Colombier return nil; 17717dd7cddfSDavid du Colombier } 17727dd7cddfSDavid du Colombier 17737dd7cddfSDavid du Colombier switch(type){ 17747dd7cddfSDavid du Colombier case TypeAudio: 17757dd7cddfSDavid du Colombier bs = BScdda; 17767dd7cddfSDavid du Colombier break; 17777dd7cddfSDavid du Colombier case TypeData: 17787dd7cddfSDavid du Colombier bs = BScdrom; 17797dd7cddfSDavid du Colombier break; 17807dd7cddfSDavid du Colombier default: 17817dd7cddfSDavid du Colombier werrstr("bad type %d", type); 17827dd7cddfSDavid du Colombier return nil; 17837dd7cddfSDavid du Colombier } 17847dd7cddfSDavid du Colombier 1785c038c065SDavid du Colombier /* comment out the returns for now; it should be no big deal - geoff */ 17867f1c8a1dSDavid du Colombier if(mmctrackinfo(drive, drive->invistrack, Maxtrack)) { 1787684b447eSDavid du Colombier if (vflag) 1788684b447eSDavid du Colombier fprint(2, "mmccreate: mmctrackinfo for invis track %d" 17897f1c8a1dSDavid du Colombier " failed: %r\n", drive->invistrack); 17900599ba09SDavid du Colombier werrstr("disc not writable"); 1791c038c065SDavid du Colombier // return nil; 17927dd7cddfSDavid du Colombier } 17937dd7cddfSDavid du Colombier if(mmcsetbs(drive, bs) < 0) { 17947dd7cddfSDavid du Colombier werrstr("cannot set bs mode"); 1795c038c065SDavid du Colombier // return nil; 17967dd7cddfSDavid du Colombier } 17977f1c8a1dSDavid du Colombier if(mmctrackinfo(drive, drive->invistrack, Maxtrack)) { 1798684b447eSDavid du Colombier if (vflag) 1799684b447eSDavid du Colombier fprint(2, "mmccreate: mmctrackinfo for invis track %d" 18007f1c8a1dSDavid du Colombier " (2) failed: %r\n", drive->invistrack); 18010599ba09SDavid du Colombier werrstr("disc not writable 2"); 1802c038c065SDavid du Colombier // return nil; 18037dd7cddfSDavid du Colombier } 18047dd7cddfSDavid du Colombier 18050599ba09SDavid du Colombier /* special hack for dvd-r: reserve the invisible track */ 18060599ba09SDavid du Colombier if (drive->mmctype == Mmcdvdminus && drive->writeok && 18077f1c8a1dSDavid du Colombier drive->recordable == Yes && reserve(drive, drive->invistrack) < 0) { 18080599ba09SDavid du Colombier if (vflag) 18090599ba09SDavid du Colombier fprint(2, "mmcreate: reserving track %d for dvd-r " 18107f1c8a1dSDavid du Colombier "failed: %r\n", drive->invistrack); 18110599ba09SDavid du Colombier return nil; 18120599ba09SDavid du Colombier } 18130599ba09SDavid du Colombier 18147dd7cddfSDavid du Colombier aux->ntotby = 0; 18157dd7cddfSDavid du Colombier aux->ntotbk = 0; 18167dd7cddfSDavid du Colombier 18177dd7cddfSDavid du Colombier t = &drive->track[drive->ntrack++]; 18187dd7cddfSDavid du Colombier t->size = 0; 18197dd7cddfSDavid du Colombier t->bs = bs; 18207dd7cddfSDavid du Colombier t->beg = aux->mmcnwa; 18217dd7cddfSDavid du Colombier t->end = 0; 18227dd7cddfSDavid du Colombier t->type = type; 18237dd7cddfSDavid du Colombier drive->nameok = 0; 18247dd7cddfSDavid du Colombier 18257dd7cddfSDavid du Colombier o = emalloc(sizeof(Otrack)); 18267dd7cddfSDavid du Colombier o->drive = drive; 18277dd7cddfSDavid du Colombier o->nchange = drive->nchange; 18287dd7cddfSDavid du Colombier o->omode = OWRITE; 18297dd7cddfSDavid du Colombier o->track = t; 18307386956aSDavid du Colombier o->buf = bopen(mmcwrite, OWRITE, bs, Readblock); 18317dd7cddfSDavid du Colombier o->buf->otrack = o; 18327dd7cddfSDavid du Colombier 18337dd7cddfSDavid du Colombier aux->nwopen++; 18347dd7cddfSDavid du Colombier 18357dd7cddfSDavid du Colombier if(vflag) 1836*efd1a06fSDavid du Colombier print("mmcinit: nwa = %#luX\n", aux->mmcnwa); 18377dd7cddfSDavid du Colombier return o; 18387dd7cddfSDavid du Colombier } 18397dd7cddfSDavid du Colombier 1840436f307dSDavid du Colombier /* 1841436f307dSDavid du Colombier * issue some form of close track, close session or finalize disc command. 18428ad6bb6aSDavid du Colombier * see The Matrix, table 252 in MMC-6 §6.3.2.3. 1843436f307dSDavid du Colombier */ 1844436f307dSDavid du Colombier static int 1845436f307dSDavid du Colombier mmcxclose(Drive *drive, int clf, int trackno) 1846436f307dSDavid du Colombier { 1847436f307dSDavid du Colombier uchar cmd[10]; 1848436f307dSDavid du Colombier 184960ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdClosetracksess); 1850cdf9e71cSDavid du Colombier /* cmd[1] & 1 is the immediate bit */ 1851436f307dSDavid du Colombier cmd[2] = clf; /* close function */ 1852436f307dSDavid du Colombier if(clf == Closetrack) 1853436f307dSDavid du Colombier cmd[5] = trackno; 1854436f307dSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 1855436f307dSDavid du Colombier } 1856436f307dSDavid du Colombier 1857079e489fSDavid du Colombier /* flush drive cache, close current track */ 18587dd7cddfSDavid du Colombier void 18597dd7cddfSDavid du Colombier mmcsynccache(Drive *drive) 18607dd7cddfSDavid du Colombier { 18617dd7cddfSDavid du Colombier uchar cmd[10]; 18627dd7cddfSDavid du Colombier Mmcaux *aux; 18637dd7cddfSDavid du Colombier 18640599ba09SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdSynccache); 18650599ba09SDavid du Colombier /* 18660599ba09SDavid du Colombier * this will take a long time to burn the remainder of a dvd-r 18670599ba09SDavid du Colombier * with a reserved track covering the whole disc. 18680599ba09SDavid du Colombier */ 18690599ba09SDavid du Colombier if (vflag) { 18700599ba09SDavid du Colombier fprint(2, "syncing cache"); 18710599ba09SDavid du Colombier if (drive->mmctype == Mmcdvdminus && drive->writeok && 187260ba15acSDavid du Colombier drive->recordable == Yes) 18730599ba09SDavid du Colombier fprint(2, "; dvd-r burning rest of track reservation, " 18740599ba09SDavid du Colombier "will be slow"); 18750599ba09SDavid du Colombier fprint(2, "\n"); 18760599ba09SDavid du Colombier } 18777dd7cddfSDavid du Colombier scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 18787dd7cddfSDavid du Colombier if(vflag) { 18797dd7cddfSDavid du Colombier aux = drive->aux; 1880*efd1a06fSDavid du Colombier print("mmcsynccache: bytes = %lld blocks = %ld, mmcnwa %#luX\n", 18817dd7cddfSDavid du Colombier aux->ntotby, aux->ntotbk, aux->mmcnwa); 18827dd7cddfSDavid du Colombier } 1883079e489fSDavid du Colombier 1884684b447eSDavid du Colombier /* 1885684b447eSDavid du Colombier * rsc: seems not to work on some drives. 1886684b447eSDavid du Colombier * so ignore return code & don't issue on dvd+rw. 1887684b447eSDavid du Colombier */ 188860ba15acSDavid du Colombier if(drive->mmctype != Mmcdvdplus || drive->erasable == No) { 1889684b447eSDavid du Colombier if (vflag) 1890684b447eSDavid du Colombier fprint(2, "closing invisible track %d (not dvd+rw)...\n", 18917f1c8a1dSDavid du Colombier drive->invistrack); 18927f1c8a1dSDavid du Colombier mmcxclose(drive, Closetrack, drive->invistrack); 1893684b447eSDavid du Colombier if (vflag) 1894cdf9e71cSDavid du Colombier fprint(2, "... done.\n"); 1895cdf9e71cSDavid du Colombier } 18967f1c8a1dSDavid du Colombier getinvistrack(drive); /* track # has probably changed */ 18977dd7cddfSDavid du Colombier } 18987dd7cddfSDavid du Colombier 1899436f307dSDavid du Colombier /* 1900436f307dSDavid du Colombier * close the open track `o'. 1901436f307dSDavid du Colombier */ 19027dd7cddfSDavid du Colombier static void 19037dd7cddfSDavid du Colombier mmcclose(Otrack *o) 19047dd7cddfSDavid du Colombier { 19057dd7cddfSDavid du Colombier Mmcaux *aux; 190659cc4ca5SDavid du Colombier static uchar zero[2*BSmax]; 19077dd7cddfSDavid du Colombier 19087dd7cddfSDavid du Colombier aux = o->drive->aux; 19097dd7cddfSDavid du Colombier if(o->omode == OREAD) 19107dd7cddfSDavid du Colombier aux->nropen--; 19117dd7cddfSDavid du Colombier else if(o->omode == OWRITE) { 19127dd7cddfSDavid du Colombier aux->nwopen--; 191359cc4ca5SDavid du Colombier mmcxwrite(o, zero, 2); /* write lead out */ 19147dd7cddfSDavid du Colombier mmcsynccache(o->drive); 19157dd7cddfSDavid du Colombier o->drive->nchange = -1; /* force reread toc */ 19167dd7cddfSDavid du Colombier } 19177dd7cddfSDavid du Colombier free(o); 19187dd7cddfSDavid du Colombier } 19197dd7cddfSDavid du Colombier 19208ad6bb6aSDavid du Colombier static int 1921079e489fSDavid du Colombier setonesess(Drive *drive) 19228ad6bb6aSDavid du Colombier { 19238ad6bb6aSDavid du Colombier uchar *p; 19248ad6bb6aSDavid du Colombier Mmcaux *aux; 19258ad6bb6aSDavid du Colombier 19268ad6bb6aSDavid du Colombier /* page 5 is legacy and now read-only; see MMC-6 §7.5.4.1 */ 19278ad6bb6aSDavid du Colombier aux = drive->aux; 1928*efd1a06fSDavid du Colombier if (!aux->page05ok) 1929*efd1a06fSDavid du Colombier return -1; 19308ad6bb6aSDavid du Colombier p = aux->page05; 19310599ba09SDavid du Colombier p[Wptrkmode] &= ~Msbits; 19320599ba09SDavid du Colombier p[Wptrkmode] |= Msnonext; 1933079e489fSDavid du Colombier return mmcsetpage(drive, Pagwrparams, p); 19348ad6bb6aSDavid du Colombier } 19358ad6bb6aSDavid du Colombier 19368ad6bb6aSDavid du Colombier /* 1937436f307dSDavid du Colombier * close the current session, then finalize the disc. 19387dd7cddfSDavid du Colombier */ 19397dd7cddfSDavid du Colombier static int 19407dd7cddfSDavid du Colombier mmcfixate(Drive *drive) 19417dd7cddfSDavid du Colombier { 1942436f307dSDavid du Colombier int r; 19437dd7cddfSDavid du Colombier 19447dd7cddfSDavid du Colombier if((drive->cap & Cwrite) == 0) { 19457dd7cddfSDavid du Colombier werrstr("not a writer"); 19467dd7cddfSDavid du Colombier return -1; 19477dd7cddfSDavid du Colombier } 19487dd7cddfSDavid du Colombier drive->nchange = -1; /* force reread toc */ 19497dd7cddfSDavid du Colombier 1950079e489fSDavid du Colombier setonesess(drive); 19517dd7cddfSDavid du Colombier 1952684b447eSDavid du Colombier /* skip explicit close session on bd-r */ 195360ba15acSDavid du Colombier if (drive->mmctype != Mmcbd || drive->erasable == Yes) { 1954684b447eSDavid du Colombier if (vflag) 1955cdf9e71cSDavid du Colombier fprint(2, "closing session and maybe finalizing...\n"); 1956436f307dSDavid du Colombier r = mmcxclose(drive, Closesessfinal, 0); 1957684b447eSDavid du Colombier if (vflag) 1958cdf9e71cSDavid du Colombier fprint(2, "... done.\n"); 1959cdf9e71cSDavid du Colombier if (r < 0) 1960cdf9e71cSDavid du Colombier return r; 1961684b447eSDavid du Colombier } 1962436f307dSDavid du Colombier /* 1963436f307dSDavid du Colombier * Closesessfinal only closes & doesn't finalize on dvd+r and bd-r. 1964079e489fSDavid du Colombier * Closedvdrbdfinal closes & finalizes dvd+r and bd-r. 1965436f307dSDavid du Colombier */ 1966cdf9e71cSDavid du Colombier if ((drive->mmctype == Mmcdvdplus || drive->mmctype == Mmcbd) && 196760ba15acSDavid du Colombier drive->erasable == No) { 1968684b447eSDavid du Colombier if (vflag) 1969684b447eSDavid du Colombier fprint(2, "finalizing dvd+r or bd-r... " 1970684b447eSDavid du Colombier "(won't print `done').\n"); 1971436f307dSDavid du Colombier return mmcxclose(drive, Closedvdrbdfinal, 0); 1972cdf9e71cSDavid du Colombier } 1973cdf9e71cSDavid du Colombier return 0; 19747dd7cddfSDavid du Colombier } 19757dd7cddfSDavid du Colombier 19767dd7cddfSDavid du Colombier static int 19777dd7cddfSDavid du Colombier mmcblank(Drive *drive, int quick) 19787dd7cddfSDavid du Colombier { 19797dd7cddfSDavid du Colombier uchar cmd[12]; 19807dd7cddfSDavid du Colombier 19817dd7cddfSDavid du Colombier drive->nchange = -1; /* force reread toc */ 19827dd7cddfSDavid du Colombier 198360ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdBlank); /* blank cd-rw media */ 1984cdf9e71cSDavid du Colombier /* immediate bit is 0x10 */ 19857dd7cddfSDavid du Colombier /* cmd[1] = 0 means blank the whole disc; = 1 just the header */ 19867dd7cddfSDavid du Colombier cmd[1] = quick ? 0x01 : 0x00; 19877dd7cddfSDavid du Colombier 19887dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone); 19897dd7cddfSDavid du Colombier } 19907dd7cddfSDavid du Colombier 19917dd7cddfSDavid du Colombier static char* 19927dd7cddfSDavid du Colombier e(int status) 19937dd7cddfSDavid du Colombier { 19947dd7cddfSDavid du Colombier if(status < 0) 19957dd7cddfSDavid du Colombier return geterrstr(); 19967dd7cddfSDavid du Colombier return nil; 19977dd7cddfSDavid du Colombier } 19987dd7cddfSDavid du Colombier 19997dd7cddfSDavid du Colombier static char* 20007dd7cddfSDavid du Colombier mmcctl(Drive *drive, int argc, char **argv) 20017dd7cddfSDavid du Colombier { 20028ad6bb6aSDavid du Colombier char *cmd; 20038ad6bb6aSDavid du Colombier 20047dd7cddfSDavid du Colombier if(argc < 1) 20057dd7cddfSDavid du Colombier return nil; 20068ad6bb6aSDavid du Colombier cmd = argv[0]; 20078ad6bb6aSDavid du Colombier if(strcmp(cmd, "format") == 0) 2008c038c065SDavid du Colombier return e(format(drive)); 20098ad6bb6aSDavid du Colombier if(strcmp(cmd, "blank") == 0) 20107dd7cddfSDavid du Colombier return e(mmcblank(drive, 0)); 20118ad6bb6aSDavid du Colombier if(strcmp(cmd, "quickblank") == 0) 20127dd7cddfSDavid du Colombier return e(mmcblank(drive, 1)); 20138ad6bb6aSDavid du Colombier if(strcmp(cmd, "eject") == 0) 20147dd7cddfSDavid du Colombier return e(start(drive, 2)); 20158ad6bb6aSDavid du Colombier if(strcmp(cmd, "ingest") == 0) 20167dd7cddfSDavid du Colombier return e(start(drive, 3)); 20177dd7cddfSDavid du Colombier return "bad arg"; 20187dd7cddfSDavid du Colombier } 20197dd7cddfSDavid du Colombier 20209a747e4fSDavid du Colombier static char* 20219a747e4fSDavid du Colombier mmcsetspeed(Drive *drive, int r, int w) 20229a747e4fSDavid du Colombier { 20239a747e4fSDavid du Colombier char *rv; 20249a747e4fSDavid du Colombier uchar cmd[12]; 20259a747e4fSDavid du Colombier 202660ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdSetcdspeed); 20279a747e4fSDavid du Colombier cmd[2] = r>>8; 20289a747e4fSDavid du Colombier cmd[3] = r; 20299a747e4fSDavid du Colombier cmd[4] = w>>8; 20309a747e4fSDavid du Colombier cmd[5] = w; 20319a747e4fSDavid du Colombier rv = e(scsi(drive, cmd, sizeof(cmd), nil, 0, Snone)); 20329a747e4fSDavid du Colombier mmcgetspeed(drive); 20339a747e4fSDavid du Colombier return rv; 20349a747e4fSDavid du Colombier } 20359a747e4fSDavid du Colombier 20367dd7cddfSDavid du Colombier static Dev mmcdev = { 20377dd7cddfSDavid du Colombier mmcopenrd, 20387dd7cddfSDavid du Colombier mmccreate, 20397dd7cddfSDavid du Colombier bufread, 20407dd7cddfSDavid du Colombier bufwrite, 20417dd7cddfSDavid du Colombier mmcclose, 20427dd7cddfSDavid du Colombier mmcgettoc, 20437dd7cddfSDavid du Colombier mmcfixate, 20447dd7cddfSDavid du Colombier mmcctl, 20459a747e4fSDavid du Colombier mmcsetspeed, 20467dd7cddfSDavid du Colombier }; 2047