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
21efd1a06fSDavid 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 */
27efd1a06fSDavid du Colombier Maxresptracks = 120, /* (1024-slop) / 8 */
28efd1a06fSDavid du Colombier
29efd1a06fSDavid du Colombier /* Pagerrrecov error control bits (buf[2]) */
30efd1a06fSDavid du Colombier Erawre = 1<<7, /* recover from write errors */
31efd1a06fSDavid du Colombier Erarre = 1<<6, /* recover from read errors */
32efd1a06fSDavid du Colombier Ertb = 1<<5, /* transfer bad block to host */
33efd1a06fSDavid du Colombier Errc = 1<<4, /* read continuous; no error recovery */
34efd1a06fSDavid du Colombier Erper = 1<<2, /* post error: report recovered errors */
35efd1a06fSDavid du Colombier Erdte = 1<<1, /* disable transfer on error */
36efd1a06fSDavid du Colombier Erdcr = 1<<0, /* disable correction */
37efd1a06fSDavid du Colombier
38efd1a06fSDavid du Colombier Kilo = 1000LL,
39efd1a06fSDavid du Colombier GB = Kilo * Kilo * Kilo,
407dd7cddfSDavid du Colombier };
417dd7cddfSDavid du Colombier
42efd1a06fSDavid du Colombier typedef struct Intfeat Intfeat;
43efd1a06fSDavid du Colombier typedef struct Mmcaux Mmcaux;
44efd1a06fSDavid 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
61efd1a06fSDavid du Colombier struct Intfeat {
62efd1a06fSDavid du Colombier int numb;
63efd1a06fSDavid du Colombier char *name;
64efd1a06fSDavid du Colombier };
65efd1a06fSDavid du Colombier
66efd1a06fSDavid du Colombier enum {
676397270fSDavid du Colombier Featrandwrite = 0x20,
68efd1a06fSDavid du Colombier Featdfctmgmt = 0x24,
696397270fSDavid du Colombier Featwriteonce = 0x25,
70efd1a06fSDavid du Colombier Featedfctrpt = 0x29,
716397270fSDavid du Colombier Featdvdrw = 0x2a,
72efd1a06fSDavid du Colombier };
73efd1a06fSDavid du Colombier
74efd1a06fSDavid du Colombier Intfeat intfeats[] = {
756397270fSDavid du Colombier Featrandwrite, "random writable", /* for write-and-verify */
76efd1a06fSDavid du Colombier // 0x21, "incr. streaming writable",
77efd1a06fSDavid du Colombier Featdfctmgmt, "hw defect mgmt.",
786397270fSDavid du Colombier Featwriteonce, "write once", /* for write-and-verify */
79efd1a06fSDavid du Colombier Featedfctrpt, "enhanced defect reporting",
806397270fSDavid du Colombier Featdvdrw, "dvd+rw", /* for dvd write-and-verify */
81efd1a06fSDavid du Colombier 0x38, "pseudo-overwrite",
82efd1a06fSDavid du Colombier // 0x40, "bd read",
83efd1a06fSDavid du Colombier // 0x41, "bd write",
84efd1a06fSDavid du Colombier };
85efd1a06fSDavid du Colombier
860dc12738SDavid du Colombier /* these will be printed as user ids, so no spaces please */
87c038c065SDavid du Colombier static char *dvdtype[] = {
88c038c065SDavid du Colombier "dvd-rom",
89c038c065SDavid du Colombier "dvd-ram",
90c038c065SDavid du Colombier "dvd-r",
91c038c065SDavid du Colombier "dvd-rw",
920dc12738SDavid du Colombier "hd-dvd-rom",
930dc12738SDavid du Colombier "hd-dvd-ram",
940dc12738SDavid du Colombier "hd-dvd-r",
950dc12738SDavid du Colombier "type-7-unknown",
960dc12738SDavid du Colombier "type-8-unknown",
97c038c065SDavid du Colombier "dvd+rw",
98c038c065SDavid du Colombier "dvd+r",
990dc12738SDavid du Colombier "type-11-unknown",
1000dc12738SDavid du Colombier "type-12-unknown",
1010dc12738SDavid du Colombier "dvd+rw-dl",
1020dc12738SDavid du Colombier "dvd+r-dl",
1030dc12738SDavid du Colombier "type-15-unknown",
104c038c065SDavid du Colombier };
105c038c065SDavid du Colombier
106efd1a06fSDavid du Colombier static int format(Drive *drive);
1077f1c8a1dSDavid du Colombier static int getinvistrack(Drive *drive);
1087f1c8a1dSDavid du Colombier
1097dd7cddfSDavid du Colombier static ulong
bige(void * p)1107dd7cddfSDavid du Colombier bige(void *p)
1117dd7cddfSDavid du Colombier {
1127dd7cddfSDavid du Colombier uchar *a;
1137dd7cddfSDavid du Colombier
1147dd7cddfSDavid du Colombier a = p;
1157dd7cddfSDavid du Colombier return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier static ushort
biges(void * p)1197dd7cddfSDavid du Colombier biges(void *p)
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier uchar *a;
1227dd7cddfSDavid du Colombier
1237dd7cddfSDavid du Colombier a = p;
1247dd7cddfSDavid du Colombier return (a[0]<<8) | a[1];
1257dd7cddfSDavid du Colombier }
1267dd7cddfSDavid du Colombier
1278ad6bb6aSDavid du Colombier ulong
getnwa(Drive * drive)1288ad6bb6aSDavid du Colombier getnwa(Drive *drive)
1298ad6bb6aSDavid du Colombier {
1308ad6bb6aSDavid du Colombier Mmcaux *aux;
1318ad6bb6aSDavid du Colombier
1328ad6bb6aSDavid du Colombier aux = drive->aux;
1338ad6bb6aSDavid du Colombier return aux->mmcnwa;
1348ad6bb6aSDavid du Colombier }
1358ad6bb6aSDavid du Colombier
1367dd7cddfSDavid du Colombier static void
hexdump(void * v,int n)1377dd7cddfSDavid du Colombier hexdump(void *v, int n)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier int i;
1407dd7cddfSDavid du Colombier uchar *p;
1417dd7cddfSDavid du Colombier
1427dd7cddfSDavid du Colombier p = v;
1437dd7cddfSDavid du Colombier for(i=0; i<n; i++){
1447dd7cddfSDavid du Colombier print("%.2ux ", p[i]);
1457dd7cddfSDavid du Colombier if((i%8) == 7)
1467dd7cddfSDavid du Colombier print("\n");
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier if(i%8)
1497dd7cddfSDavid du Colombier print("\n");
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier
152c038c065SDavid du Colombier static void
initcdb(uchar * cdb,int len,int cmd)153c038c065SDavid du Colombier initcdb(uchar *cdb, int len, int cmd)
154c038c065SDavid du Colombier {
155c038c065SDavid du Colombier memset(cdb, 0, len);
156c038c065SDavid du Colombier cdb[0] = cmd;
157c038c065SDavid du Colombier }
158c038c065SDavid du Colombier
159e67f3b95SDavid du Colombier /*
160c038c065SDavid du Colombier * SCSI CDBs (cmd arrays) are 6, 10, 12, 16 or 32 bytes long.
161e67f3b95SDavid du Colombier * The mode sense/select commands implicitly refer to
162e67f3b95SDavid du Colombier * a mode parameter list, which consists of an 8-byte
163e67f3b95SDavid du Colombier * mode parameter header, followed by zero or more block
164e67f3b95SDavid du Colombier * descriptors and zero or more mode pages (MMC-2 §5.5.2).
165e67f3b95SDavid du Colombier * We'll ignore mode sub-pages.
166e67f3b95SDavid du Colombier * Integers are stored big-endian.
167e67f3b95SDavid du Colombier *
168e67f3b95SDavid du Colombier * The format of the mode parameter (10) header is:
169e67f3b95SDavid du Colombier * ushort mode_data_length; // of following bytes
170e67f3b95SDavid du Colombier * uchar medium_type;
171e67f3b95SDavid du Colombier * uchar device_specific;
172e67f3b95SDavid du Colombier * uchar reserved[2];
173e67f3b95SDavid du Colombier * ushort block_descriptor_length; // zero
174e67f3b95SDavid du Colombier *
175e67f3b95SDavid du Colombier * The format of the mode parameter (6) header is:
176e67f3b95SDavid du Colombier * uchar mode_data_length; // of following bytes
177e67f3b95SDavid du Colombier * uchar medium_type;
178e67f3b95SDavid du Colombier * uchar device_specific;
179e67f3b95SDavid du Colombier * uchar block_descriptor_length; // zero
180e67f3b95SDavid du Colombier *
181e67f3b95SDavid du Colombier * The format of the mode pages is:
182e67f3b95SDavid du Colombier * uchar page_code_and_PS;
183e67f3b95SDavid du Colombier * uchar page_len; // of following bytes
184e67f3b95SDavid du Colombier * uchar parameter[page_len];
185e67f3b95SDavid du Colombier *
186e67f3b95SDavid du Colombier * see SPC-3 §4.3.4.6 for allocation length and §7.4 for mode parameter lists.
187e67f3b95SDavid du Colombier */
188e67f3b95SDavid du Colombier
189e67f3b95SDavid du Colombier enum {
190e67f3b95SDavid du Colombier Mode10parmhdrlen= 8,
191e67f3b95SDavid du Colombier Mode6parmhdrlen = 4,
192e67f3b95SDavid du Colombier Modepaghdrlen = 2,
193e67f3b95SDavid du Colombier };
194e67f3b95SDavid du Colombier
1957dd7cddfSDavid du Colombier static int
mmcgetpage10(Drive * drive,int page,void * v)1967dd7cddfSDavid du Colombier mmcgetpage10(Drive *drive, int page, void *v)
1977dd7cddfSDavid du Colombier {
1987dd7cddfSDavid du Colombier uchar cmd[10], resp[512];
1997dd7cddfSDavid du Colombier int n, r;
2007dd7cddfSDavid du Colombier
20160ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMsense10);
2027dd7cddfSDavid du Colombier cmd[2] = page;
203e67f3b95SDavid du Colombier cmd[8] = 255; /* allocation length: buffer size */
2047dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread);
205e67f3b95SDavid du Colombier if(n < Mode10parmhdrlen)
2067dd7cddfSDavid du Colombier return -1;
2077dd7cddfSDavid du Colombier
208e67f3b95SDavid du Colombier r = (resp[6]<<8) | resp[7]; /* block descriptor length */
209e67f3b95SDavid du Colombier n -= Mode10parmhdrlen + r;
2107dd7cddfSDavid du Colombier
2117dd7cddfSDavid du Colombier if(n < 0)
2127dd7cddfSDavid du Colombier return -1;
2137dd7cddfSDavid du Colombier if(n > Pagesz)
2147dd7cddfSDavid du Colombier n = Pagesz;
2157dd7cddfSDavid du Colombier
216e67f3b95SDavid du Colombier memmove(v, &resp[Mode10parmhdrlen + r], n);
2177dd7cddfSDavid du Colombier return n;
2187dd7cddfSDavid du Colombier }
2197dd7cddfSDavid du Colombier
2207dd7cddfSDavid du Colombier static int
mmcgetpage6(Drive * drive,int page,void * v)2217dd7cddfSDavid du Colombier mmcgetpage6(Drive *drive, int page, void *v)
2227dd7cddfSDavid du Colombier {
2237dd7cddfSDavid du Colombier uchar cmd[6], resp[512];
2247dd7cddfSDavid du Colombier int n;
2257dd7cddfSDavid du Colombier
22660ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMsense6);
2277dd7cddfSDavid du Colombier cmd[2] = page;
228e67f3b95SDavid du Colombier cmd[4] = 255; /* allocation length */
229e67f3b95SDavid du Colombier
2307dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread);
231e67f3b95SDavid du Colombier if(n < Mode6parmhdrlen)
2327dd7cddfSDavid du Colombier return -1;
2337dd7cddfSDavid du Colombier
234e67f3b95SDavid du Colombier n -= Mode6parmhdrlen + resp[3];
2357dd7cddfSDavid du Colombier if(n < 0)
2367dd7cddfSDavid du Colombier return -1;
2377dd7cddfSDavid du Colombier if(n > Pagesz)
2387dd7cddfSDavid du Colombier n = Pagesz;
2397dd7cddfSDavid du Colombier
240e67f3b95SDavid du Colombier memmove(v, &resp[Mode6parmhdrlen + resp[3]], n);
2417dd7cddfSDavid du Colombier return n;
2427dd7cddfSDavid du Colombier }
2437dd7cddfSDavid du Colombier
2447dd7cddfSDavid du Colombier static int
mmcsetpage10(Drive * drive,int page,void * v)2457dd7cddfSDavid du Colombier mmcsetpage10(Drive *drive, int page, void *v)
2467dd7cddfSDavid du Colombier {
2477dd7cddfSDavid du Colombier uchar cmd[10], *p, *pagedata;
2487dd7cddfSDavid du Colombier int len, n;
2497dd7cddfSDavid du Colombier
250e67f3b95SDavid du Colombier /* allocate parameter list, copy in mode page, fill in header */
2517dd7cddfSDavid du Colombier pagedata = v;
2527dd7cddfSDavid du Colombier assert(pagedata[0] == page);
253e67f3b95SDavid du Colombier len = Mode10parmhdrlen + Modepaghdrlen + pagedata[1];
2547dd7cddfSDavid du Colombier p = emalloc(len);
255e67f3b95SDavid du Colombier memmove(p + Mode10parmhdrlen, pagedata, pagedata[1]);
256e67f3b95SDavid du Colombier /* parameter list header */
257e67f3b95SDavid du Colombier p[0] = 0;
258e67f3b95SDavid du Colombier p[1] = len - 2;
259e67f3b95SDavid du Colombier
260c038c065SDavid du Colombier /* set up CDB */
26160ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMselect10);
262e67f3b95SDavid du Colombier cmd[1] = 0x10; /* format not vendor-specific */
2637dd7cddfSDavid du Colombier cmd[8] = len;
2647dd7cddfSDavid du Colombier
265e67f3b95SDavid du Colombier // print("set: sending cmd\n");
2667dd7cddfSDavid du Colombier // hexdump(cmd, 10);
267e67f3b95SDavid du Colombier // print("parameter list header\n");
268e67f3b95SDavid du Colombier // hexdump(p, Mode10parmhdrlen);
2697dd7cddfSDavid du Colombier // print("page\n");
270e67f3b95SDavid du Colombier // hexdump(p + Mode10parmhdrlen, len - Mode10parmhdrlen);
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite);
273e67f3b95SDavid du Colombier
274e67f3b95SDavid du Colombier // print("set: got cmd\n");
275e67f3b95SDavid du Colombier // hexdump(cmd, 10);
276e67f3b95SDavid du Colombier
2777dd7cddfSDavid du Colombier free(p);
2787dd7cddfSDavid du Colombier if(n < len)
2797dd7cddfSDavid du Colombier return -1;
2807dd7cddfSDavid du Colombier return 0;
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier
2837dd7cddfSDavid du Colombier static int
mmcsetpage6(Drive * drive,int page,void * v)2847dd7cddfSDavid du Colombier mmcsetpage6(Drive *drive, int page, void *v)
2857dd7cddfSDavid du Colombier {
2867dd7cddfSDavid du Colombier uchar cmd[6], *p, *pagedata;
2877dd7cddfSDavid du Colombier int len, n;
2887dd7cddfSDavid du Colombier
289e67f3b95SDavid du Colombier if (vflag)
290e67f3b95SDavid du Colombier print("mmcsetpage6 called!\n");
2917dd7cddfSDavid du Colombier pagedata = v;
2927dd7cddfSDavid du Colombier assert(pagedata[0] == page);
293e67f3b95SDavid du Colombier len = Mode6parmhdrlen + Modepaghdrlen + pagedata[1];
2947dd7cddfSDavid du Colombier p = emalloc(len);
295e67f3b95SDavid du Colombier memmove(p + Mode6parmhdrlen, pagedata, pagedata[1]);
296e67f3b95SDavid du Colombier
29760ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdMselect6);
298e67f3b95SDavid du Colombier cmd[1] = 0x10; /* format not vendor-specific */
2997dd7cddfSDavid du Colombier cmd[4] = len;
3007dd7cddfSDavid du Colombier
3017dd7cddfSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), p, len, Swrite);
3027dd7cddfSDavid du Colombier free(p);
3037dd7cddfSDavid du Colombier if(n < len)
3047dd7cddfSDavid du Colombier return -1;
3057dd7cddfSDavid du Colombier return 0;
3067dd7cddfSDavid du Colombier }
3077dd7cddfSDavid du Colombier
3087dd7cddfSDavid du Colombier static int
mmcgetpage(Drive * drive,int page,void * v)3097dd7cddfSDavid du Colombier mmcgetpage(Drive *drive, int page, void *v)
3107dd7cddfSDavid du Colombier {
3117dd7cddfSDavid du Colombier Mmcaux *aux;
3127dd7cddfSDavid du Colombier
3137dd7cddfSDavid du Colombier aux = drive->aux;
3147dd7cddfSDavid du Colombier switch(aux->pagecmdsz) {
3157dd7cddfSDavid du Colombier case 10:
3167dd7cddfSDavid du Colombier return mmcgetpage10(drive, page, v);
3177dd7cddfSDavid du Colombier case 6:
3187dd7cddfSDavid du Colombier return mmcgetpage6(drive, page, v);
3197dd7cddfSDavid du Colombier default:
3207dd7cddfSDavid du Colombier assert(0);
3217dd7cddfSDavid du Colombier }
3227dd7cddfSDavid du Colombier return -1;
3237dd7cddfSDavid du Colombier }
3247dd7cddfSDavid du Colombier
3257dd7cddfSDavid du Colombier static int
mmcsetpage(Drive * drive,int page,void * v)3267dd7cddfSDavid du Colombier mmcsetpage(Drive *drive, int page, void *v)
3277dd7cddfSDavid du Colombier {
3287dd7cddfSDavid du Colombier Mmcaux *aux;
3297dd7cddfSDavid du Colombier
3307dd7cddfSDavid du Colombier aux = drive->aux;
3317dd7cddfSDavid du Colombier switch(aux->pagecmdsz) {
3327dd7cddfSDavid du Colombier case 10:
3337dd7cddfSDavid du Colombier return mmcsetpage10(drive, page, v);
3347dd7cddfSDavid du Colombier case 6:
3357dd7cddfSDavid du Colombier return mmcsetpage6(drive, page, v);
3367dd7cddfSDavid du Colombier default:
3377dd7cddfSDavid du Colombier assert(0);
3387dd7cddfSDavid du Colombier }
3397dd7cddfSDavid du Colombier return -1;
3407dd7cddfSDavid du Colombier }
3417dd7cddfSDavid du Colombier
3427dd7cddfSDavid du Colombier int
mmcstatus(Drive * drive)3437dd7cddfSDavid du Colombier mmcstatus(Drive *drive)
3447dd7cddfSDavid du Colombier {
3457dd7cddfSDavid du Colombier uchar cmd[12];
3467dd7cddfSDavid du Colombier
347c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdCDstatus); /* mechanism status */
3487dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), nil, 0, Sread);
3497dd7cddfSDavid du Colombier }
3507dd7cddfSDavid du Colombier
3519a747e4fSDavid du Colombier void
mmcgetspeed(Drive * drive)3529a747e4fSDavid du Colombier mmcgetspeed(Drive *drive)
3539a747e4fSDavid du Colombier {
3549a747e4fSDavid du Colombier int n, maxread, curread, maxwrite, curwrite;
3559a747e4fSDavid du Colombier uchar buf[Pagesz];
3569a747e4fSDavid du Colombier
357dd12a5c6SDavid du Colombier memset(buf, 0, 22);
358c038c065SDavid du Colombier n = mmcgetpage(drive, Pagcapmechsts, buf); /* legacy page */
359055c7668SDavid du Colombier if (n < 22) {
360055c7668SDavid du Colombier if (vflag)
361055c7668SDavid du Colombier fprint(2, "no Pagcapmechsts mode page!\n");
362055c7668SDavid du Colombier return;
363055c7668SDavid du Colombier }
3649a747e4fSDavid du Colombier maxread = (buf[8]<<8)|buf[9];
3659a747e4fSDavid du Colombier curread = (buf[14]<<8)|buf[15];
3669a747e4fSDavid du Colombier maxwrite = (buf[18]<<8)|buf[19];
3679a747e4fSDavid du Colombier curwrite = (buf[20]<<8)|buf[21];
3689a747e4fSDavid du Colombier
369055c7668SDavid du Colombier if(maxread && maxread < 170 || curread && curread < 170)
3709a747e4fSDavid du Colombier return; /* bogus data */
3719a747e4fSDavid du Colombier
3729a747e4fSDavid du Colombier drive->readspeed = curread;
3739a747e4fSDavid du Colombier drive->writespeed = curwrite;
3749a747e4fSDavid du Colombier drive->maxreadspeed = maxread;
3759a747e4fSDavid du Colombier drive->maxwritespeed = maxwrite;
3769a747e4fSDavid du Colombier }
3779a747e4fSDavid du Colombier
378cdf9e71cSDavid du Colombier static int
getdevtype(Drive * drive)379cdf9e71cSDavid du Colombier getdevtype(Drive *drive)
380cdf9e71cSDavid du Colombier {
381cdf9e71cSDavid du Colombier int n;
382cdf9e71cSDavid du Colombier uchar cmd[6], resp[Pagesz];
383cdf9e71cSDavid du Colombier
384cdf9e71cSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdInq);
385cdf9e71cSDavid du Colombier cmd[3] = sizeof resp >> 8;
386cdf9e71cSDavid du Colombier cmd[4] = sizeof resp;
387cdf9e71cSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread);
388cdf9e71cSDavid du Colombier if (n < 8)
389cdf9e71cSDavid du Colombier return -1;
390cdf9e71cSDavid du Colombier return resp[0] & 037;
391cdf9e71cSDavid du Colombier }
392cdf9e71cSDavid du Colombier
393684b447eSDavid du Colombier static int
start(Drive * drive,int code)394684b447eSDavid du Colombier start(Drive *drive, int code)
395684b447eSDavid du Colombier {
396684b447eSDavid du Colombier uchar cmd[6];
397684b447eSDavid du Colombier
398684b447eSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdStart);
399684b447eSDavid du Colombier cmd[4] = code;
400684b447eSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone);
401684b447eSDavid du Colombier }
402684b447eSDavid du Colombier
403efd1a06fSDavid du Colombier static int
setcaching(Drive * drive)404efd1a06fSDavid du Colombier setcaching(Drive *drive)
405efd1a06fSDavid du Colombier {
406efd1a06fSDavid du Colombier int n;
407efd1a06fSDavid du Colombier uchar buf[Pagesz];
408efd1a06fSDavid du Colombier
409efd1a06fSDavid du Colombier /*
410efd1a06fSDavid du Colombier * we can't actually control caching much.
411efd1a06fSDavid du Colombier * see SBC-2 §6.3.3 but also MMC-6 §7.6.
412efd1a06fSDavid du Colombier *
413efd1a06fSDavid du Colombier * should set read ahead, MMC-6 §6.37; seems to control caching.
414efd1a06fSDavid du Colombier */
415efd1a06fSDavid du Colombier n = mmcgetpage(drive, Pagcache, buf);
416efd1a06fSDavid du Colombier if (n < 3)
417efd1a06fSDavid du Colombier return -1;
418efd1a06fSDavid du Colombier /* n == 255; buf[1] == 10 (10 bytes after buf[1]) */
419efd1a06fSDavid du Colombier buf[0] &= 077; /* clear reserved bits, MMC-6 §7.2.3 */
420efd1a06fSDavid du Colombier assert(buf[0] == Pagcache);
421efd1a06fSDavid du Colombier assert(buf[1] >= 10);
422efd1a06fSDavid du Colombier buf[2] = Ccwce;
423efd1a06fSDavid du Colombier if (mmcsetpage(drive, Pagcache, buf) < 0) {
424efd1a06fSDavid du Colombier if (vflag)
425efd1a06fSDavid du Colombier print("mmcprobe: cache control NOT set\n");
426efd1a06fSDavid du Colombier return -1;
427efd1a06fSDavid du Colombier }
428efd1a06fSDavid du Colombier return 0;
429efd1a06fSDavid du Colombier }
430efd1a06fSDavid du Colombier
4317dd7cddfSDavid du Colombier Drive*
mmcprobe(Scsi * scsi)4327dd7cddfSDavid du Colombier mmcprobe(Scsi *scsi)
4337dd7cddfSDavid du Colombier {
4347dd7cddfSDavid du Colombier Mmcaux *aux;
4357dd7cddfSDavid du Colombier Drive *drive;
4367dd7cddfSDavid du Colombier uchar buf[Pagesz];
437efd1a06fSDavid du Colombier int cap;
4387dd7cddfSDavid du Colombier
439e67f3b95SDavid du Colombier if (vflag)
440e67f3b95SDavid du Colombier print("mmcprobe: inquiry: %s\n", scsi->inquire);
4417f1c8a1dSDavid du Colombier
4427dd7cddfSDavid du Colombier drive = emalloc(sizeof(Drive));
4437dd7cddfSDavid du Colombier drive->Scsi = *scsi;
4447dd7cddfSDavid du Colombier drive->Dev = mmcdev;
4457f1c8a1dSDavid du Colombier drive->invistrack = -1;
4467f1c8a1dSDavid du Colombier getinvistrack(drive);
4477f1c8a1dSDavid du Colombier
4487dd7cddfSDavid du Colombier aux = emalloc(sizeof(Mmcaux));
4497dd7cddfSDavid du Colombier drive->aux = aux;
4507f1c8a1dSDavid du Colombier
451684b447eSDavid du Colombier scsiready(drive);
452cdf9e71cSDavid du Colombier drive->type = getdevtype(drive);
453cdf9e71cSDavid du Colombier if (drive->type != TypeCD) {
454cdf9e71cSDavid du Colombier werrstr("not an mmc device");
455cdf9e71cSDavid du Colombier free(aux);
456cdf9e71cSDavid du Colombier free(drive);
457cdf9e71cSDavid du Colombier return nil;
458cdf9e71cSDavid du Colombier }
4597dd7cddfSDavid du Colombier
460cdf9e71cSDavid du Colombier /*
461cdf9e71cSDavid du Colombier * drive is an mmc device; learn what we can about it
462cdf9e71cSDavid du Colombier * (as opposed to the disc in it).
463cdf9e71cSDavid du Colombier */
464cdf9e71cSDavid du Colombier
465684b447eSDavid du Colombier start(drive, 1);
466e67f3b95SDavid du Colombier /* attempt to read CD capabilities page, but it's now legacy */
467e67f3b95SDavid du Colombier if(mmcgetpage10(drive, Pagcapmechsts, buf) >= 0)
4687dd7cddfSDavid du Colombier aux->pagecmdsz = 10;
469e67f3b95SDavid du Colombier else if(mmcgetpage6(drive, Pagcapmechsts, buf) >= 0)
4707dd7cddfSDavid du Colombier aux->pagecmdsz = 6;
4717dd7cddfSDavid du Colombier else {
472055c7668SDavid du Colombier if (vflag)
473055c7668SDavid du Colombier fprint(2, "no Pagcapmechsts mode page!\n");
474055c7668SDavid du Colombier werrstr("can't read mode page %d!", Pagcapmechsts);
475cdf9e71cSDavid du Colombier free(aux);
4767dd7cddfSDavid du Colombier free(drive);
4777dd7cddfSDavid du Colombier return nil;
4787dd7cddfSDavid du Colombier }
479cdf9e71cSDavid du Colombier
4807dd7cddfSDavid du Colombier cap = 0;
481c038c065SDavid du Colombier if(buf[Capwrite] & (Capcdr|Capcdrw|Capdvdr|Capdvdram) ||
482c038c065SDavid du Colombier buf[Capmisc] & Caprw)
4837dd7cddfSDavid du Colombier cap |= Cwrite;
484c038c065SDavid du Colombier if(buf[Capmisc] & Capcdda) /* CD-DA commands supported? */
485c038c065SDavid du Colombier cap |= Ccdda; /* not used anywhere else */
4867dd7cddfSDavid du Colombier
4877dd7cddfSDavid du Colombier // print("read %d max %d\n", biges(buf+14), biges(buf+8));
4887dd7cddfSDavid du Colombier // print("write %d max %d\n", biges(buf+20), biges(buf+18));
4897dd7cddfSDavid du Colombier
490e67f3b95SDavid du Colombier /* cache optional page 05 (write parameter page) */
491c038c065SDavid du Colombier if(/* (cap & Cwrite) && */
492c038c065SDavid du Colombier mmcgetpage(drive, Pagwrparams, aux->page05) >= 0) {
4937dd7cddfSDavid du Colombier aux->page05ok = 1;
494c038c065SDavid du Colombier cap |= Cwrite;
495c038c065SDavid du Colombier if (vflag)
496684b447eSDavid du Colombier fprint(2, "mmcprobe: got page 5, assuming drive can write\n");
497055c7668SDavid du Colombier } else {
498055c7668SDavid du Colombier if (vflag)
499055c7668SDavid du Colombier fprint(2, "no Pagwrparams mode page!\n");
5007dd7cddfSDavid du Colombier cap &= ~Cwrite;
501055c7668SDavid du Colombier }
5027dd7cddfSDavid du Colombier drive->cap = cap;
5037dd7cddfSDavid du Colombier
5049a747e4fSDavid du Colombier mmcgetspeed(drive);
505efd1a06fSDavid du Colombier setcaching(drive);
5067dd7cddfSDavid du Colombier return drive;
5077dd7cddfSDavid du Colombier }
5087dd7cddfSDavid du Colombier
5090599ba09SDavid du Colombier static char *tracktype[] = { /* indices are track modes (Tm*) */
510684b447eSDavid du Colombier "audio cdda",
511684b447eSDavid du Colombier "2 audio channels",
512684b447eSDavid du Colombier "2",
513684b447eSDavid du Colombier "3",
514684b447eSDavid du Colombier "data, recorded uninterrupted",
515684b447eSDavid du Colombier "data, recorded interrupted",
516684b447eSDavid du Colombier };
517684b447eSDavid du Colombier
518efd1a06fSDavid du Colombier /*
519efd1a06fSDavid du Colombier * figure out the first writable block, if we can, into drive->aux->mmcnwa.
520efd1a06fSDavid du Colombier * resp must be from ScmdRtrackinfo.
521efd1a06fSDavid du Colombier */
522efd1a06fSDavid du Colombier static long
gettracknwa(Drive * drive,int t,ulong beg,uchar * resp)523efd1a06fSDavid du Colombier gettracknwa(Drive *drive, int t, ulong beg, uchar *resp)
5247dd7cddfSDavid du Colombier {
5257f1c8a1dSDavid du Colombier long newnwa;
5267dd7cddfSDavid du Colombier Mmcaux *aux;
5277dd7cddfSDavid du Colombier
5287dd7cddfSDavid du Colombier aux = drive->aux;
529efd1a06fSDavid du Colombier if(resp[7] & 1) { /* nwa valid? */
530efd1a06fSDavid du Colombier newnwa = bige(&resp[12]);
531efd1a06fSDavid du Colombier if (newnwa >= 0)
532efd1a06fSDavid du Colombier if (aux->mmcnwa < 0)
533efd1a06fSDavid du Colombier aux->mmcnwa = newnwa;
534efd1a06fSDavid du Colombier else if (aux->mmcnwa != newnwa)
535efd1a06fSDavid du Colombier fprint(2, "nwa is %ld but invis track starts blk %ld\n",
536efd1a06fSDavid du Colombier newnwa, aux->mmcnwa);
537efd1a06fSDavid du Colombier }
538efd1a06fSDavid du Colombier /* resp[6] & (1<<7) of zero: invisible track */
539efd1a06fSDavid du Colombier if(t == Invistrack || t == drive->invistrack)
540efd1a06fSDavid du Colombier if (aux->mmcnwa < 0)
541efd1a06fSDavid du Colombier aux->mmcnwa = beg;
542efd1a06fSDavid du Colombier else if (aux->mmcnwa != beg)
543efd1a06fSDavid du Colombier fprint(2, "invis track starts blk %ld but nwa is %ld\n",
544efd1a06fSDavid du Colombier beg, aux->mmcnwa);
545efd1a06fSDavid du Colombier if (vflag && aux->mmcnwa >= 0)
546efd1a06fSDavid du Colombier print(" nwa %lud", aux->mmcnwa);
547efd1a06fSDavid du Colombier return 0;
5487dd7cddfSDavid du Colombier }
5497dd7cddfSDavid du Colombier
550efd1a06fSDavid du Colombier /*
551efd1a06fSDavid du Colombier * map tmode to type & bs.
552efd1a06fSDavid du Colombier */
553efd1a06fSDavid du Colombier static void
gettypebs(uchar tmode,int t,int i,int * typep,int * bsp)554efd1a06fSDavid du Colombier gettypebs(uchar tmode, int t, int i, int *typep, int *bsp)
555efd1a06fSDavid du Colombier {
556efd1a06fSDavid du Colombier int type, bs;
5577dd7cddfSDavid du Colombier
5587dd7cddfSDavid du Colombier if(vflag)
5598ad6bb6aSDavid du Colombier print("track %d type %d (%s)", t, tmode,
560684b447eSDavid du Colombier (tmode < nelem(tracktype)? tracktype[tmode]: "**GOK**"));
5617dd7cddfSDavid du Colombier type = TypeNone;
5627dd7cddfSDavid du Colombier bs = BScdda;
5637dd7cddfSDavid du Colombier switch(tmode){
5640599ba09SDavid du Colombier case Tmcdda:
5657dd7cddfSDavid du Colombier type = TypeAudio;
5667dd7cddfSDavid du Colombier bs = BScdda;
5677dd7cddfSDavid du Colombier break;
5680599ba09SDavid du Colombier case Tm2audio: /* 2 audio channels, with pre-emphasis 50/15 μs */
5697dd7cddfSDavid du Colombier if(vflag)
570684b447eSDavid du Colombier print("audio channels with preemphasis on track %d "
571684b447eSDavid du Colombier "(u%.3d)\n", t, i);
5727dd7cddfSDavid du Colombier type = TypeNone;
5737dd7cddfSDavid du Colombier break;
5740599ba09SDavid du Colombier case Tmunintr: /* data track, recorded uninterrupted */
5750599ba09SDavid du Colombier case Tmintr: /* data track, recorded interrupted */
5760599ba09SDavid du Colombier /* treat Tmintr (5) as cdrom; it's probably dvd or bd */
577e67f3b95SDavid du Colombier type = TypeData;
578e67f3b95SDavid du Colombier bs = BScdrom;
579e67f3b95SDavid du Colombier break;
5807dd7cddfSDavid du Colombier default:
5817dd7cddfSDavid du Colombier if(vflag)
5827dd7cddfSDavid du Colombier print("unknown track type %d\n", tmode);
583e67f3b95SDavid du Colombier break;
5847dd7cddfSDavid du Colombier }
585efd1a06fSDavid du Colombier *bsp = bs;
586efd1a06fSDavid du Colombier *typep = type;
587efd1a06fSDavid du Colombier }
5887dd7cddfSDavid du Colombier
589efd1a06fSDavid du Colombier /* t is a track number on disc, i is an index into drive->track[] for result */
590efd1a06fSDavid du Colombier static int
mmctrackinfo(Drive * drive,int t,int i)591efd1a06fSDavid du Colombier mmctrackinfo(Drive *drive, int t, int i)
592efd1a06fSDavid du Colombier {
593efd1a06fSDavid du Colombier int n, type, bs;
594efd1a06fSDavid du Colombier ulong beg, size;
595efd1a06fSDavid du Colombier uchar tmode;
596efd1a06fSDavid du Colombier uchar cmd[10], resp[255];
597efd1a06fSDavid du Colombier Track *track;
598efd1a06fSDavid du Colombier
599efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRtrackinfo);
600efd1a06fSDavid du Colombier cmd[1] = 1; /* address below is logical track # */
601efd1a06fSDavid du Colombier cmd[2] = t>>24;
602efd1a06fSDavid du Colombier cmd[3] = t>>16;
603efd1a06fSDavid du Colombier cmd[4] = t>>8;
604efd1a06fSDavid du Colombier cmd[5] = t;
605efd1a06fSDavid du Colombier cmd[7] = sizeof(resp)>>8;
606efd1a06fSDavid du Colombier cmd[8] = sizeof(resp);
607efd1a06fSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread);
608efd1a06fSDavid du Colombier if(n < 28) {
609efd1a06fSDavid du Colombier if(vflag)
610efd1a06fSDavid du Colombier print("trackinfo %d fails n=%d: %r\n", t, n);
611efd1a06fSDavid du Colombier return -1;
612efd1a06fSDavid du Colombier }
613efd1a06fSDavid du Colombier
614efd1a06fSDavid du Colombier tmode = resp[5] & 0x0D;
615efd1a06fSDavid du Colombier // dmode = resp[6] & 0x0F;
616efd1a06fSDavid du Colombier
617efd1a06fSDavid du Colombier gettypebs(tmode, t, i, &type, &bs);
618efd1a06fSDavid du Colombier
619efd1a06fSDavid du Colombier beg = bige(&resp[8]);
620efd1a06fSDavid du Colombier size = bige(&resp[24]);
621efd1a06fSDavid du Colombier
622efd1a06fSDavid du Colombier track = &drive->track[i];
623efd1a06fSDavid du Colombier track->mtime = drive->changetime;
624efd1a06fSDavid du Colombier track->beg = beg;
625efd1a06fSDavid du Colombier track->end = beg + size;
626efd1a06fSDavid du Colombier track->type = type;
627efd1a06fSDavid du Colombier track->bs = bs;
628efd1a06fSDavid du Colombier track->size = (vlong)(size-2) * bs; /* -2: skip lead out */
6297dd7cddfSDavid du Colombier
6308ad6bb6aSDavid du Colombier if(resp[6] & (1<<6)) { /* blank? */
631efd1a06fSDavid du Colombier track->type = TypeBlank;
63260ba15acSDavid du Colombier drive->writeok = Yes;
6337dd7cddfSDavid du Colombier }
6347dd7cddfSDavid du Colombier
6358ad6bb6aSDavid du Colombier if(vflag)
6368ad6bb6aSDavid du Colombier print(" start %lud end %lud", beg, beg + size - 1);
637efd1a06fSDavid du Colombier gettracknwa(drive, t, beg, resp);
6388ad6bb6aSDavid du Colombier if (vflag)
6398ad6bb6aSDavid du Colombier print("\n");
6407dd7cddfSDavid du Colombier return 0;
6417dd7cddfSDavid du Colombier }
6427dd7cddfSDavid du Colombier
643c038c065SDavid du Colombier /* this may fail for blank media */
6447dd7cddfSDavid du Colombier static int
mmcreadtoc(Drive * drive,int type,int track,void * data,int nbytes)6459a747e4fSDavid du Colombier mmcreadtoc(Drive *drive, int type, int track, void *data, int nbytes)
6467dd7cddfSDavid du Colombier {
6477dd7cddfSDavid du Colombier uchar cmd[10];
6487dd7cddfSDavid du Colombier
64960ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRTOC);
650436f307dSDavid du Colombier cmd[1] = type; /* msf bit & reserved */
651436f307dSDavid du Colombier cmd[2] = Tocfmttoc;
652436f307dSDavid du Colombier cmd[6] = track; /* track/session */
6537dd7cddfSDavid du Colombier cmd[7] = nbytes>>8;
6547dd7cddfSDavid du Colombier cmd[8] = nbytes;
6557dd7cddfSDavid du Colombier
6567ec5746aSDavid du Colombier /*
6577ec5746aSDavid du Colombier * printing iounit(drive->Scsi.rawfd) here yields
6587ec5746aSDavid du Colombier * iounit(3) = 0; # for local access
6597ec5746aSDavid du Colombier * iounit(3) = 65512; # for remote access via /mnt/term
6607ec5746aSDavid du Colombier */
6617dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), data, nbytes, Sread);
6627dd7cddfSDavid du Colombier }
6637dd7cddfSDavid du Colombier
6649a747e4fSDavid du Colombier static Msf
rdmsf(uchar * p)6659a747e4fSDavid du Colombier rdmsf(uchar *p)
6669a747e4fSDavid du Colombier {
6679a747e4fSDavid du Colombier Msf msf;
6689a747e4fSDavid du Colombier
6699a747e4fSDavid du Colombier msf.m = p[0];
6709a747e4fSDavid du Colombier msf.s = p[1];
6719a747e4fSDavid du Colombier msf.f = p[2];
6729a747e4fSDavid du Colombier return msf;
6739a747e4fSDavid du Colombier }
6749a747e4fSDavid du Colombier
6757dd7cddfSDavid du Colombier static int
getdiscinfo(Drive * drive,uchar resp[],int resplen)676c038c065SDavid du Colombier getdiscinfo(Drive *drive, uchar resp[], int resplen)
677c038c065SDavid du Colombier {
678c038c065SDavid du Colombier int n;
67960ba15acSDavid du Colombier uchar cmd[10];
680c038c065SDavid du Colombier
68160ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdRdiscinfo);
68260ba15acSDavid du Colombier cmd[7] = resplen>>8;
68360ba15acSDavid du Colombier cmd[8] = resplen;
68460ba15acSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, resplen, Sread);
68560ba15acSDavid du Colombier if(n < 24) {
68660ba15acSDavid du Colombier if(n >= 0)
68760ba15acSDavid du Colombier werrstr("rdiscinfo returns %d", n);
68860ba15acSDavid du Colombier else if (vflag)
689436f307dSDavid du Colombier fprint(2, "read disc info failed\n");
69060ba15acSDavid du Colombier return -1;
691436f307dSDavid du Colombier }
692c038c065SDavid du Colombier if (vflag)
693c038c065SDavid du Colombier fprint(2, "read disc info succeeded\n");
694c038c065SDavid du Colombier assert((resp[2] & 0340) == 0); /* data type 0 */
695c038c065SDavid du Colombier drive->erasable = ((resp[2] & 0x10) != 0); /* -RW? */
69660ba15acSDavid du Colombier return n;
69760ba15acSDavid du Colombier }
69860ba15acSDavid du Colombier
699efd1a06fSDavid du Colombier int
isbitset(uint bit,uchar * map)700efd1a06fSDavid du Colombier isbitset(uint bit, uchar *map)
701efd1a06fSDavid du Colombier {
702efd1a06fSDavid du Colombier return map[bit/8] & (1 << (bit%8));
703efd1a06fSDavid du Colombier }
704efd1a06fSDavid du Colombier
705efd1a06fSDavid du Colombier /*
706efd1a06fSDavid du Colombier * set read-write error recovery mode page 1 (10 bytes), mmc-6 §7.3.
707efd1a06fSDavid du Colombier *
708efd1a06fSDavid du Colombier * requires defect management feature (0x24) mmc-6 §5.3.13 (mandatory on bd,
709efd1a06fSDavid du Colombier * but has to be enabled by allocating spares with FORMAT UNIT command)
710efd1a06fSDavid du Colombier * or enhanced defect reporting feature (0x29) mmc-6 §5.3.17,
711efd1a06fSDavid du Colombier * and they are mutually exclusive.
712efd1a06fSDavid du Colombier */
71360ba15acSDavid du Colombier static int
seterrrecov(Drive * drive)714efd1a06fSDavid du Colombier seterrrecov(Drive *drive)
71560ba15acSDavid du Colombier {
71660ba15acSDavid du Colombier int n;
717efd1a06fSDavid du Colombier uchar buf[Pagesz];
71860ba15acSDavid du Colombier
719efd1a06fSDavid du Colombier if (!isbitset(Featdfctmgmt, drive->features) &&
720efd1a06fSDavid du Colombier !isbitset(Featedfctrpt, drive->features)) {
721efd1a06fSDavid du Colombier fprint(2, "defect mgmt. and enhanced defect reporting disabled!\n");
72260ba15acSDavid du Colombier return -1;
72360ba15acSDavid du Colombier }
724efd1a06fSDavid du Colombier n = mmcgetpage(drive, Pagerrrecov, buf);
725efd1a06fSDavid du Colombier if (n < 3)
72660ba15acSDavid du Colombier return -1;
727efd1a06fSDavid du Colombier /* n == 255; buf[1] == 10 (10 bytes after buf[1]) */
72860ba15acSDavid du Colombier /*
729efd1a06fSDavid du Colombier * error recovery page as read:
730efd1a06fSDavid du Colombier * 0: 01 (page: error recovery)
731efd1a06fSDavid du Colombier * 1: 0a (length: 10)
732efd1a06fSDavid du Colombier * 2: c0 (error control bits: 0300 == Erawre|Erarre)
733efd1a06fSDavid du Colombier * 3: 20 (read retry count: 32)
734efd1a06fSDavid du Colombier * 4: 00
735efd1a06fSDavid du Colombier * 5: 00
736efd1a06fSDavid du Colombier * 6: 00
737efd1a06fSDavid du Colombier * 7: 00
738efd1a06fSDavid du Colombier * 8: 01 (write retry count: 1)
739efd1a06fSDavid du Colombier * 9: 00 00 00 (error reporting window size)
74060ba15acSDavid du Colombier */
741efd1a06fSDavid du Colombier buf[0] &= ~(1<<6); /* clear reserved bit */
742efd1a06fSDavid du Colombier assert((buf[0] & 077) == Pagerrrecov);
743efd1a06fSDavid du Colombier assert(buf[1] >= 10);
744efd1a06fSDavid du Colombier
745efd1a06fSDavid du Colombier buf[2] = Erawre | Erarre; /* default: Erawre; can't set Ertb */
746efd1a06fSDavid du Colombier if (isbitset(Featedfctrpt, drive->features))
747efd1a06fSDavid du Colombier buf[7] = 1; /* emcdr: 1==recover, don't tell us; default 0 */
748efd1a06fSDavid du Colombier
749efd1a06fSDavid du Colombier // buf[3] = 32; /* rd retry count; default 32; arbitrary */
750efd1a06fSDavid du Colombier // buf[8] = 1; /* wr retry count; default 1; arbitrary */
751efd1a06fSDavid du Colombier // memset(buf+9, 0, 3); /* err reporting win siz: 0 == no tsr; default 0 */
752efd1a06fSDavid du Colombier
753efd1a06fSDavid du Colombier if (mmcsetpage(drive, Pagerrrecov, buf) < 0) {
754efd1a06fSDavid du Colombier if (vflag)
755efd1a06fSDavid du Colombier fprint(2, "error recovery NOT set\n");
756efd1a06fSDavid du Colombier return -1;
757efd1a06fSDavid du Colombier }
758efd1a06fSDavid du Colombier if (vflag)
759efd1a06fSDavid du Colombier fprint(2, "error recovery set\n");
760efd1a06fSDavid du Colombier return 0;
761efd1a06fSDavid du Colombier }
762efd1a06fSDavid du Colombier
763efd1a06fSDavid du Colombier char *
featname(int feat)764efd1a06fSDavid du Colombier featname(int feat)
765efd1a06fSDavid du Colombier {
766efd1a06fSDavid du Colombier Intfeat *ifp;
767efd1a06fSDavid du Colombier
768efd1a06fSDavid du Colombier for (ifp = intfeats; ifp < intfeats + nelem(intfeats); ifp++)
769efd1a06fSDavid du Colombier if (feat == ifp->numb)
770efd1a06fSDavid du Colombier return ifp->name;
771efd1a06fSDavid du Colombier return nil;
772efd1a06fSDavid du Colombier }
773efd1a06fSDavid du Colombier
774efd1a06fSDavid du Colombier static int
prof2mmctype(Drive * drive,ushort prof)775efd1a06fSDavid du Colombier prof2mmctype(Drive *drive, ushort prof)
776efd1a06fSDavid du Colombier {
77760ba15acSDavid du Colombier if(prof == 0 || prof == 0xffff) /* none or whacko? */
778efd1a06fSDavid du Colombier return -1;
77960ba15acSDavid du Colombier if(drive->mmctype != Mmcnone)
780efd1a06fSDavid du Colombier return -1;
78160ba15acSDavid du Colombier switch (prof >> 4) {
78260ba15acSDavid du Colombier case 0:
78360ba15acSDavid du Colombier drive->mmctype = Mmccd;
78460ba15acSDavid du Colombier break;
78560ba15acSDavid du Colombier case 1:
78660ba15acSDavid du Colombier if (prof == 0x1a || prof == 0x1b)
78760ba15acSDavid du Colombier drive->mmctype = Mmcdvdplus;
78860ba15acSDavid du Colombier else
78960ba15acSDavid du Colombier drive->mmctype = Mmcdvdminus;
79060ba15acSDavid du Colombier break;
79160ba15acSDavid du Colombier case 2:
79260ba15acSDavid du Colombier drive->mmctype = Mmcdvdplus; /* dual layer */
79360ba15acSDavid du Colombier break;
79460ba15acSDavid du Colombier case 4:
79560ba15acSDavid du Colombier drive->mmctype = Mmcbd;
796efd1a06fSDavid du Colombier if (vflag)
797efd1a06fSDavid du Colombier fprint(2, "getconf profile for bd = %#x\n", prof);
79860ba15acSDavid du Colombier /*
79960ba15acSDavid du Colombier * further decode prof to set writability flags.
800586f04fdSDavid du Colombier * mostly for Pioneer BDR-206M.
80160ba15acSDavid du Colombier */
802586f04fdSDavid du Colombier drive->erasable = drive->recordable = No;
80360ba15acSDavid du Colombier switch (prof) {
80460ba15acSDavid du Colombier case 0x40:
805586f04fdSDavid du Colombier break;
80660ba15acSDavid du Colombier case 0x41:
80760ba15acSDavid du Colombier case 0x42:
80860ba15acSDavid du Colombier drive->recordable = Yes;
80960ba15acSDavid du Colombier break;
81060ba15acSDavid du Colombier case 0x43:
81160ba15acSDavid du Colombier drive->erasable = Yes;
81260ba15acSDavid du Colombier break;
81360ba15acSDavid du Colombier }
81460ba15acSDavid du Colombier break;
81560ba15acSDavid du Colombier case 5:
81660ba15acSDavid du Colombier drive->mmctype = Mmcdvdminus; /* hd dvd, obs. */
81760ba15acSDavid du Colombier break;
81860ba15acSDavid du Colombier }
819efd1a06fSDavid du Colombier return 0;
820efd1a06fSDavid du Colombier }
821efd1a06fSDavid du Colombier
822efd1a06fSDavid du Colombier static void
notefeats(Drive * drive,uchar * p,ulong datalen)823efd1a06fSDavid du Colombier notefeats(Drive *drive, uchar *p, ulong datalen)
824efd1a06fSDavid du Colombier {
825efd1a06fSDavid du Colombier int left, len;
826efd1a06fSDavid du Colombier uint feat;
827efd1a06fSDavid du Colombier char *ftnm;
828efd1a06fSDavid du Colombier
829efd1a06fSDavid du Colombier if (vflag)
830efd1a06fSDavid du Colombier fprint(2, "features: ");
831efd1a06fSDavid du Colombier for (left = datalen; left > 0; left -= len, p += len) {
832efd1a06fSDavid du Colombier feat = p[0]<<8 | p[1];
833efd1a06fSDavid du Colombier len = 4 + p[3];
834efd1a06fSDavid du Colombier ftnm = featname(feat);
835efd1a06fSDavid du Colombier if (vflag && ftnm)
836efd1a06fSDavid du Colombier fprint(2, "%#ux (%s) curr %d\n", feat, ftnm, p[2] & 1);
8376397270fSDavid du Colombier if (feat >= Maxfeatures) { /* could be vendor-specific */
8386397270fSDavid du Colombier if (vflag)
8396397270fSDavid du Colombier fprint(2, "feature %d too big for bit map\n",
8406397270fSDavid du Colombier feat);
8416397270fSDavid du Colombier } else if (p[2] & 1)
842efd1a06fSDavid du Colombier drive->features[feat/8] |= 1 << (feat%8);
843efd1a06fSDavid du Colombier }
844efd1a06fSDavid du Colombier }
845efd1a06fSDavid du Colombier
846efd1a06fSDavid du Colombier static int
getconfcmd(Drive * drive,uchar * resp,int respsz)847efd1a06fSDavid du Colombier getconfcmd(Drive *drive, uchar *resp, int respsz)
848efd1a06fSDavid du Colombier {
849efd1a06fSDavid du Colombier int n;
850efd1a06fSDavid du Colombier ulong datalen;
851efd1a06fSDavid du Colombier uchar cmd[10];
852efd1a06fSDavid du Colombier
853efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, Scmdgetconf);
854efd1a06fSDavid du Colombier cmd[3] = 0; /* start with profile list feature */
855efd1a06fSDavid du Colombier cmd[7] = respsz >> 8;
856efd1a06fSDavid du Colombier cmd[8] = respsz;
857efd1a06fSDavid du Colombier n = scsi(drive, cmd, sizeof cmd, resp, respsz, Sread);
858efd1a06fSDavid du Colombier if (n < 0) {
859efd1a06fSDavid du Colombier if(vflag)
860efd1a06fSDavid du Colombier fprint(2, "get config cmd failed\n");
861efd1a06fSDavid du Colombier return -1;
862efd1a06fSDavid du Colombier }
863efd1a06fSDavid du Colombier if (n < 4)
864efd1a06fSDavid du Colombier return -1;
865efd1a06fSDavid du Colombier datalen = GETBELONG(resp+0);
866efd1a06fSDavid du Colombier if (datalen < 8)
867efd1a06fSDavid du Colombier return -1;
868efd1a06fSDavid du Colombier return datalen;
869efd1a06fSDavid du Colombier }
870efd1a06fSDavid du Colombier
871efd1a06fSDavid du Colombier static int
getconf(Drive * drive)872efd1a06fSDavid du Colombier getconf(Drive *drive)
873efd1a06fSDavid du Colombier {
874efd1a06fSDavid du Colombier ushort prof;
875efd1a06fSDavid du Colombier long datalen;
876efd1a06fSDavid du Colombier uchar resp[64*1024 - 8]; /* 64k-8 max, 450 typical */
877efd1a06fSDavid du Colombier
878efd1a06fSDavid du Colombier memset(drive->features, 0, sizeof drive->features);
879efd1a06fSDavid du Colombier datalen = getconfcmd(drive, resp, sizeof resp);
880efd1a06fSDavid du Colombier if(datalen < 0)
881efd1a06fSDavid du Colombier return -1;
882efd1a06fSDavid du Colombier
883efd1a06fSDavid du Colombier /*
884efd1a06fSDavid du Colombier * features start with an 8-byte header:
885efd1a06fSDavid du Colombier * ulong datalen, ushort reserved, ushort current profile.
886efd1a06fSDavid du Colombier * profile codes (table 92) are: 0 reserved, 1-7 legacy, 8-0xf cd,
887efd1a06fSDavid du Colombier * 0x10-0x1f 0x2a-0x2b dvd*, 0x40-0x4f bd, 0x50-0x5f hd dvd,
888efd1a06fSDavid du Colombier * 0xffff whacko.
889efd1a06fSDavid du Colombier *
890efd1a06fSDavid du Colombier * this is followed by multiple feature descriptors:
891efd1a06fSDavid du Colombier * ushort code, uchar bits, uchar addnl_len, addnl_len bytes.
892efd1a06fSDavid du Colombier */
893efd1a06fSDavid du Colombier prof = resp[6]<<8 | resp[7];
894efd1a06fSDavid du Colombier if (prof2mmctype(drive, prof) < 0)
895efd1a06fSDavid du Colombier return datalen;
896efd1a06fSDavid du Colombier
897efd1a06fSDavid du Colombier /*
898efd1a06fSDavid du Colombier * for bd-r, establish non-default recording mode before first write,
899efd1a06fSDavid du Colombier * with FORMAT UNIT command to allocate spares (BD MMC set
900efd1a06fSDavid du Colombier * description, v1.1, §4.4.4).
901efd1a06fSDavid du Colombier */
902efd1a06fSDavid du Colombier if (drive->mmctype == Mmcbd && drive->recordable == Yes &&
903efd1a06fSDavid du Colombier drive->erasable == No) {
904efd1a06fSDavid du Colombier format(drive); /* no-op with error if already been done */
905efd1a06fSDavid du Colombier /* read config again, now that bd-r has spares */
906efd1a06fSDavid du Colombier datalen = getconfcmd(drive, resp, sizeof resp);
907efd1a06fSDavid du Colombier if(datalen < 0)
908efd1a06fSDavid du Colombier return -1;
909efd1a06fSDavid du Colombier }
910efd1a06fSDavid du Colombier
911efd1a06fSDavid du Colombier /* skip header to find feature list */
912efd1a06fSDavid du Colombier notefeats(drive, resp + 8, datalen - 8);
913efd1a06fSDavid du Colombier
914586f04fdSDavid du Colombier if (drive->mmctype == Mmcbd)
915efd1a06fSDavid du Colombier seterrrecov(drive);
916efd1a06fSDavid du Colombier return datalen;
917c038c065SDavid du Colombier }
918c038c065SDavid du Colombier
919c038c065SDavid du Colombier static int
getdvdstruct(Drive * drive)920c038c065SDavid du Colombier getdvdstruct(Drive *drive)
921c038c065SDavid du Colombier {
922436f307dSDavid du Colombier int n, cat;
923c038c065SDavid du Colombier uchar cmd[12], resp[Pagesz];
924c038c065SDavid du Colombier
925c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReadDVD); /* actually, read disc structure */
926c038c065SDavid du Colombier cmd[1] = 0; /* media type: dvd */
927c038c065SDavid du Colombier cmd[7] = 0; /* format code: physical format */
928c038c065SDavid du Colombier cmd[8] = sizeof resp >> 8; /* allocation length */
929c038c065SDavid du Colombier cmd[9] = sizeof resp;
930c038c065SDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread);
93160ba15acSDavid du Colombier if (n < 7) {
93260ba15acSDavid du Colombier if(vflag)
93360ba15acSDavid du Colombier fprint(2, "read disc structure (dvd) cmd failed\n");
934c038c065SDavid du Colombier return -1;
93560ba15acSDavid du Colombier }
936436f307dSDavid du Colombier
937436f307dSDavid du Colombier /* resp[0..1] is resp length */
938436f307dSDavid du Colombier cat = (resp[4] & 0xf0) >> 4; /* disk category, MMC-6 §6.22.3.2.1 */
939c038c065SDavid du Colombier if (vflag)
940436f307dSDavid du Colombier fprint(2, "dvd type is %s\n", dvdtype[cat]);
9418ad6bb6aSDavid du Colombier drive->dvdtype = dvdtype[cat];
942c038c065SDavid du Colombier /* write parameters mode page may suffice to compute writeok for dvd */
94360ba15acSDavid du Colombier drive->erasable = drive->recordable = No;
944436f307dSDavid du Colombier /*
945436f307dSDavid du Colombier * the layer-type field is a *bit array*,
946436f307dSDavid du Colombier * though an enumeration of types would make more sense,
947436f307dSDavid du Colombier * since the types are exclusive, not orthogonal.
948436f307dSDavid du Colombier */
949436f307dSDavid du Colombier if (resp[6] & (1<<2)) /* rewritable? */
95060ba15acSDavid du Colombier drive->erasable = Yes;
951436f307dSDavid du Colombier else if (resp[6] & (1<<1)) /* recordable once? */
95260ba15acSDavid du Colombier drive->recordable = Yes;
9537f1c8a1dSDavid du Colombier /* else it's a factory-pressed disk */
954cdf9e71cSDavid du Colombier drive->mmctype = (cat >= 8? Mmcdvdplus: Mmcdvdminus);
955c038c065SDavid du Colombier return 0;
956c038c065SDavid du Colombier }
957c038c065SDavid du Colombier
95860ba15acSDavid du Colombier /*
95960ba15acSDavid du Colombier * ugly hack to divine device type from inquiry string as last resort.
96060ba15acSDavid du Colombier * mostly for Pioneer BDR-206M.
96160ba15acSDavid du Colombier */
96260ba15acSDavid du Colombier static int
bdguess(Drive * drive)96360ba15acSDavid du Colombier bdguess(Drive *drive)
96460ba15acSDavid du Colombier {
96560ba15acSDavid du Colombier if (drive->mmctype == Mmcnone) {
96660ba15acSDavid du Colombier if (strstr(drive->Scsi.inquire, "BD") == nil)
96760ba15acSDavid du Colombier return -1;
96860ba15acSDavid du Colombier if (vflag)
96960ba15acSDavid du Colombier fprint(2, "drive probably a BD (from inquiry string)\n");
97060ba15acSDavid du Colombier drive->mmctype = Mmcbd;
97160ba15acSDavid du Colombier } else if (drive->mmctype == Mmcbd) {
97260ba15acSDavid du Colombier if (drive->erasable != Unset && drive->recordable != Unset)
97360ba15acSDavid du Colombier return 0;
97460ba15acSDavid du Colombier } else
97560ba15acSDavid du Colombier return -1;
97660ba15acSDavid du Colombier
97760ba15acSDavid du Colombier drive->recordable = drive->writeok = No;
97860ba15acSDavid du Colombier if (strstr(drive->Scsi.inquire, "RW") != nil) {
97960ba15acSDavid du Colombier if (vflag)
98060ba15acSDavid du Colombier fprint(2, "drive probably a burner (from inquiry string)\n");
98160ba15acSDavid du Colombier drive->recordable = drive->writeok = Yes;
98260ba15acSDavid du Colombier if (drive->erasable == Unset) { /* set by getdiscinfo perhaps */
98360ba15acSDavid du Colombier drive->erasable = No; /* no way to tell, alas */
98460ba15acSDavid du Colombier if (vflag)
985efd1a06fSDavid du Colombier fprint(2, "\tassuming -r not -re\n");
98660ba15acSDavid du Colombier }
98760ba15acSDavid du Colombier } else {
98860ba15acSDavid du Colombier if (drive->erasable == Unset)
98960ba15acSDavid du Colombier drive->erasable = No;
99060ba15acSDavid du Colombier }
99160ba15acSDavid du Colombier if (drive->erasable == Yes)
99260ba15acSDavid du Colombier drive->recordable = No; /* mutually exclusive */
99360ba15acSDavid du Colombier return 0;
99460ba15acSDavid du Colombier }
99560ba15acSDavid du Colombier
996c038c065SDavid du Colombier static int
getbdstruct(Drive * drive)997c038c065SDavid du Colombier getbdstruct(Drive *drive)
998c038c065SDavid du Colombier {
999c038c065SDavid du Colombier int n;
100060ba15acSDavid du Colombier uchar cmd[12], resp[4+4096];
100160ba15acSDavid du Colombier uchar *di, *body;
1002c038c065SDavid du Colombier
1003c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReadDVD); /* actually, read disc structure */
1004c038c065SDavid du Colombier cmd[1] = 1; /* media type: bd */
100560ba15acSDavid du Colombier /* cmd[6] is layer #, 0 is first */
1006c038c065SDavid du Colombier cmd[7] = 0; /* format code: disc info */
1007c038c065SDavid du Colombier cmd[8] = sizeof resp >> 8; /* allocation length */
1008c038c065SDavid du Colombier cmd[9] = sizeof resp;
1009c038c065SDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof resp, Sread);
101060ba15acSDavid du Colombier if(n < 0) {
101160ba15acSDavid du Colombier if(vflag)
101260ba15acSDavid du Colombier fprint(2, "read disc structure (bd) cmd failed\n");
101360ba15acSDavid du Colombier return -1;
101460ba15acSDavid du Colombier }
101560ba15acSDavid du Colombier
1016436f307dSDavid du Colombier /*
101760ba15acSDavid du Colombier * resp[0..1] is resp length (4100); 2 & 3 are reserved.
101860ba15acSDavid du Colombier * there may be multiple disc info structs of 112 bytes each.
101960ba15acSDavid du Colombier * disc info (di) starts at 4. di[0..7] are header, followed by body.
102060ba15acSDavid du Colombier * body[0..2] is bd type (disc type identifier):
102160ba15acSDavid du Colombier * BDO|BDW|BDR, MMC-6 §6.22.3.3.1. The above scsi command should
1022436f307dSDavid du Colombier * fail on DVD drives, but some seem to ignore media type
1023436f307dSDavid du Colombier * and return successfully, so verify that it's a BD drive.
1024436f307dSDavid du Colombier */
102560ba15acSDavid du Colombier di = resp + 4;
102660ba15acSDavid du Colombier body = di + 8;
102760ba15acSDavid du Colombier n -= 4 + 8;
102860ba15acSDavid du Colombier if (n < 3 || di[0] != 'D' || di[1] != 'I' ||
102960ba15acSDavid du Colombier body[0] != 'B' || body[1] != 'D') {
1030c038c065SDavid du Colombier if(vflag)
103160ba15acSDavid du Colombier fprint(2, "it's not a bd\n");
1032c038c065SDavid du Colombier return -1;
1033c038c065SDavid du Colombier }
103460ba15acSDavid du Colombier if (vflag)
103560ba15acSDavid du Colombier fprint(2, "read disc structure (bd) succeeded; di format %d\n",
103660ba15acSDavid du Colombier di[2]);
103760ba15acSDavid du Colombier
103860ba15acSDavid du Colombier drive->erasable = drive->recordable = No;
103960ba15acSDavid du Colombier switch (body[2]) {
104060ba15acSDavid du Colombier case 'O': /* read-Only */
104160ba15acSDavid du Colombier break;
104260ba15acSDavid du Colombier case 'R': /* Recordable */
104360ba15acSDavid du Colombier drive->recordable = Yes;
104460ba15acSDavid du Colombier break;
104560ba15acSDavid du Colombier case 'W': /* reWritable */
104660ba15acSDavid du Colombier drive->erasable = Yes;
104760ba15acSDavid du Colombier break;
104860ba15acSDavid du Colombier default:
104960ba15acSDavid du Colombier fprint(2, "%s: unknown bd type BD%c\n", argv0, body[2]);
105060ba15acSDavid du Colombier return -1;
105160ba15acSDavid du Colombier }
1052efd1a06fSDavid du Colombier /* printed getbdstruct: di bytes 98 di units/layers 32 */
1053efd1a06fSDavid du Colombier // fprint(2, "getbdstruct: di bytes %d di units/layers %d\n", di[6], di[3]);
1054cdf9e71cSDavid du Colombier drive->mmctype = Mmcbd;
1055c038c065SDavid du Colombier return 0;
1056c038c065SDavid du Colombier }
1057c038c065SDavid du Colombier
1058079e489fSDavid du Colombier /*
1059079e489fSDavid du Colombier * infer endings from the beginnings of other tracks.
1060079e489fSDavid du Colombier */
1061efd1a06fSDavid du Colombier static int
mmcinfertracks(Drive * drive,int first,int last)1062cdf9e71cSDavid du Colombier mmcinfertracks(Drive *drive, int first, int last)
1063cdf9e71cSDavid du Colombier {
1064cdf9e71cSDavid du Colombier int i;
1065cdf9e71cSDavid du Colombier uchar resp[1024];
1066cdf9e71cSDavid du Colombier ulong tot;
1067cdf9e71cSDavid du Colombier Track *t;
1068cdf9e71cSDavid du Colombier
1069079e489fSDavid du Colombier if (vflag)
1070efd1a06fSDavid du Colombier fprint(2, "inferring tracks from toc\n");
1071cdf9e71cSDavid du Colombier for(i = first; i <= last; i++) {
1072cdf9e71cSDavid du Colombier memset(resp, 0, sizeof(resp));
1073efd1a06fSDavid du Colombier if(mmcreadtoc(drive, 0, i, resp, sizeof(resp)) < 0) {
1074efd1a06fSDavid du Colombier last = i - 1;
1075efd1a06fSDavid du Colombier if (last < 1)
1076efd1a06fSDavid du Colombier last = 1;
1077cdf9e71cSDavid du Colombier break;
1078efd1a06fSDavid du Colombier }
1079cdf9e71cSDavid du Colombier t = &drive->track[i-first];
1080cdf9e71cSDavid du Colombier t->mtime = drive->changetime;
1081cdf9e71cSDavid du Colombier t->type = TypeData;
1082cdf9e71cSDavid du Colombier t->bs = BScdrom;
1083cdf9e71cSDavid du Colombier t->beg = bige(resp+8);
1084cdf9e71cSDavid du Colombier if(!(resp[5] & 4)) {
1085cdf9e71cSDavid du Colombier t->type = TypeAudio;
1086cdf9e71cSDavid du Colombier t->bs = BScdda;
1087cdf9e71cSDavid du Colombier }
1088cdf9e71cSDavid du Colombier }
1089cdf9e71cSDavid du Colombier
1090cdf9e71cSDavid du Colombier if((long)drive->track[0].beg < 0) /* i've seen negative track 0's */
1091cdf9e71cSDavid du Colombier drive->track[0].beg = 0;
1092cdf9e71cSDavid du Colombier
1093cdf9e71cSDavid du Colombier tot = 0;
1094cdf9e71cSDavid du Colombier memset(resp, 0, sizeof(resp));
1095cdf9e71cSDavid du Colombier /* 0xAA is lead-out */
1096cdf9e71cSDavid du Colombier if(mmcreadtoc(drive, 0, 0xAA, resp, sizeof(resp)) < 0)
1097efd1a06fSDavid du Colombier print("bad mmcreadtoc\n");
1098cdf9e71cSDavid du Colombier if(resp[6])
1099cdf9e71cSDavid du Colombier tot = bige(resp+8);
1100cdf9e71cSDavid du Colombier
1101efd1a06fSDavid du Colombier t = nil;
1102cdf9e71cSDavid du Colombier for(i=last; i>=first; i--) {
1103cdf9e71cSDavid du Colombier t = &drive->track[i-first];
1104cdf9e71cSDavid du Colombier t->end = tot;
1105cdf9e71cSDavid du Colombier tot = t->beg;
110673ee67a1SDavid du Colombier if(t->end <= t->beg)
110773ee67a1SDavid du Colombier t->beg = t->end = 0;
1108cdf9e71cSDavid du Colombier /* -2: skip lead out */
1109cdf9e71cSDavid du Colombier t->size = (t->end - t->beg - 2) * (vlong)t->bs;
1110cdf9e71cSDavid du Colombier }
1111efd1a06fSDavid du Colombier fprint(2, "infertracks: nwa should be %,lld; tot = %,ld\n", t->size, tot);
1112efd1a06fSDavid du Colombier return last;
1113cdf9e71cSDavid du Colombier }
1114cdf9e71cSDavid du Colombier
1115efd1a06fSDavid du Colombier static void
initdrive(Drive * drive)1116efd1a06fSDavid du Colombier initdrive(Drive *drive)
11177dd7cddfSDavid du Colombier {
1118efd1a06fSDavid du Colombier int i;
1119684b447eSDavid du Colombier Mmcaux *aux;
11207dd7cddfSDavid du Colombier
11217dd7cddfSDavid du Colombier drive->ntrack = 0;
11227dd7cddfSDavid du Colombier drive->nameok = 0;
11237dd7cddfSDavid du Colombier drive->nchange = drive->Scsi.nchange;
11247dd7cddfSDavid du Colombier drive->changetime = drive->Scsi.changetime;
112560ba15acSDavid du Colombier drive->writeok = No;
11267f1c8a1dSDavid du Colombier drive->erasable = drive->recordable = Unset;
112781f927e7SDavid du Colombier drive->laysfx = nil;
11287f1c8a1dSDavid du Colombier getinvistrack(drive);
1129684b447eSDavid du Colombier aux = drive->aux;
11307f1c8a1dSDavid du Colombier aux->mmcnwa = -1;
1131684b447eSDavid du Colombier aux->nropen = aux->nwopen = 0;
1132684b447eSDavid du Colombier aux->ntotby = aux->ntotbk = 0;
11337dd7cddfSDavid du Colombier
11349a747e4fSDavid du Colombier for(i=0; i<nelem(drive->track); i++){
11359a747e4fSDavid du Colombier memset(&drive->track[i].mbeg, 0, sizeof(Msf));
11369a747e4fSDavid du Colombier memset(&drive->track[i].mend, 0, sizeof(Msf));
11379a747e4fSDavid du Colombier }
1138efd1a06fSDavid du Colombier }
1139efd1a06fSDavid du Colombier
1140efd1a06fSDavid du Colombier static long
computenwa(Drive * drive,int first,int last)1141efd1a06fSDavid du Colombier computenwa(Drive *drive, int first, int last)
1142efd1a06fSDavid du Colombier {
1143efd1a06fSDavid du Colombier int i;
1144efd1a06fSDavid du Colombier ulong nwa;
1145efd1a06fSDavid du Colombier Track *t;
11469a747e4fSDavid du Colombier
11477dd7cddfSDavid du Colombier /*
1148efd1a06fSDavid du Colombier * work through the tracks in reverse order (last to first).
1149efd1a06fSDavid du Colombier * assume last is the to-be-written invisible track.
1150c038c065SDavid du Colombier */
1151efd1a06fSDavid du Colombier nwa = 0;
1152efd1a06fSDavid du Colombier for(i=last-1; i>=first; i--) {
1153efd1a06fSDavid du Colombier t = &drive->track[i-first];
1154efd1a06fSDavid du Colombier nwa += t->end - t->beg;
1155efd1a06fSDavid du Colombier }
1156efd1a06fSDavid du Colombier return nwa;
1157efd1a06fSDavid du Colombier }
1158c038c065SDavid du Colombier
1159efd1a06fSDavid du Colombier static int
findntracks(Drive * drive,int * firstp,int * lastp)1160efd1a06fSDavid du Colombier findntracks(Drive *drive, int *firstp, int *lastp)
1161efd1a06fSDavid du Colombier {
1162efd1a06fSDavid du Colombier int i, n, first, last;
1163efd1a06fSDavid du Colombier uchar resp[1024];
1164efd1a06fSDavid du Colombier
1165436f307dSDavid du Colombier if((n = mmcreadtoc(drive, Msfbit, 0, resp, sizeof(resp))) < 4) {
11667dd7cddfSDavid du Colombier /*
11677f1c8a1dSDavid du Colombier * it could be a blank disc. in case it's a blank disc in a
11687f1c8a1dSDavid du Colombier * cd-rw drive, use readdiscinfo to try to find the track info.
11697dd7cddfSDavid du Colombier */
1170c038c065SDavid du Colombier if(getdiscinfo(drive, resp, sizeof(resp)) < 7)
11717dd7cddfSDavid du Colombier return -1;
1172c038c065SDavid du Colombier assert((resp[2] & 0340) == 0); /* data type 0 */
11737dd7cddfSDavid du Colombier if(resp[4] != 1)
11747dd7cddfSDavid du Colombier print("multi-session disc %d\n", resp[4]);
1175efd1a06fSDavid du Colombier drive->writeok = Yes;
1176efd1a06fSDavid du Colombier
1177efd1a06fSDavid du Colombier /*
1178efd1a06fSDavid du Colombier * some drives (e.g., LG's BDs) report only 1 track on an
1179efd1a06fSDavid du Colombier * unfinalized disc, so probe every track. the nwa reported
1180efd1a06fSDavid du Colombier * by such drives may be wrong (e.g., 0), so compute that too.
1181efd1a06fSDavid du Colombier */
11827dd7cddfSDavid du Colombier first = resp[3];
11837dd7cddfSDavid du Colombier last = resp[6];
11847dd7cddfSDavid du Colombier if(vflag)
1185efd1a06fSDavid du Colombier print("rdiscinfo tracks %d-%d (last==1 means TBD)\n",
1186efd1a06fSDavid du Colombier first, last);
1187efd1a06fSDavid du Colombier if(last == 1)
1188efd1a06fSDavid du Colombier last = Maxresptracks; /* probe the tracks */
11897dd7cddfSDavid du Colombier } else {
11907dd7cddfSDavid du Colombier first = resp[2];
11917dd7cddfSDavid du Colombier last = resp[3];
1192efd1a06fSDavid du Colombier if(vflag)
1193efd1a06fSDavid du Colombier print("toc tracks %d-%d (last==1 means TBD)\n",
1194efd1a06fSDavid du Colombier first, last);
1195efd1a06fSDavid du Colombier if(last == 1)
1196efd1a06fSDavid du Colombier last = Maxresptracks; /* probe the tracks */
11979a747e4fSDavid du Colombier
11989a747e4fSDavid du Colombier if(n >= 4+8*(last-first+2)) {
1199436f307dSDavid du Colombier /* resp[4 + i*8 + 2] is track # */
1200c038c065SDavid du Colombier /* <=: track[last-first+1] = end */
1201c038c065SDavid du Colombier for(i=0; i<=last-first+1; i++)
12029a747e4fSDavid du Colombier drive->track[i].mbeg = rdmsf(resp+4+i*8+5);
12039a747e4fSDavid du Colombier for(i=0; i<last-first+1; i++)
12049a747e4fSDavid du Colombier drive->track[i].mend = drive->track[i+1].mbeg;
12059a747e4fSDavid du Colombier }
12067dd7cddfSDavid du Colombier }
1207efd1a06fSDavid du Colombier *firstp = first;
1208efd1a06fSDavid du Colombier *lastp = last;
1209efd1a06fSDavid du Colombier return 0;
1210efd1a06fSDavid du Colombier }
12117dd7cddfSDavid du Colombier
121281f927e7SDavid du Colombier /* needs drive->mmctype to be already set */
1213efd1a06fSDavid du Colombier static int
alltrackinfo(Drive * drive,int first,int last)1214efd1a06fSDavid du Colombier alltrackinfo(Drive *drive, int first, int last)
1215efd1a06fSDavid du Colombier {
1216efd1a06fSDavid du Colombier int i;
1217efd1a06fSDavid du Colombier long nwa;
1218efd1a06fSDavid du Colombier vlong cap;
121981f927e7SDavid du Colombier char *osfx;
1220efd1a06fSDavid du Colombier Mmcaux *aux;
1221efd1a06fSDavid du Colombier Track *track;
1222efd1a06fSDavid du Colombier
1223efd1a06fSDavid du Colombier for(i = first; i <= last; i++)
1224efd1a06fSDavid du Colombier if (mmctrackinfo(drive, i, i - first) < 0) {
1225efd1a06fSDavid du Colombier last = i - 1;
1226efd1a06fSDavid du Colombier if (last < 1)
1227efd1a06fSDavid du Colombier last = 1;
1228efd1a06fSDavid du Colombier break;
1229efd1a06fSDavid du Colombier }
1230efd1a06fSDavid du Colombier track = &drive->track[last - first];
1231efd1a06fSDavid du Colombier drive->end = track->end;
123281f927e7SDavid du Colombier if (drive->mmctype == Mmcbd) {
1233efd1a06fSDavid du Colombier /* allow for lower apparent capacity due to reserved spares */
1234efd1a06fSDavid du Colombier cap = (vlong)track->end * track->bs;
123581f927e7SDavid du Colombier osfx = drive->laysfx;
1236efd1a06fSDavid du Colombier if (cap >= 101*GB)
1237efd1a06fSDavid du Colombier drive->laysfx = "-ql"; /* 128GB nominal */
1238efd1a06fSDavid du Colombier else if (cap >= 51*GB)
1239efd1a06fSDavid du Colombier drive->laysfx = "-tl"; /* 100GB nominal */
1240efd1a06fSDavid du Colombier else if (cap >= 26*GB)
1241efd1a06fSDavid du Colombier drive->laysfx = "-dl"; /* 50GB nominal */
1242efd1a06fSDavid du Colombier else
1243efd1a06fSDavid du Colombier drive->laysfx = ""; /* 25GB nominal */
124481f927e7SDavid du Colombier if (vflag)
1245efd1a06fSDavid du Colombier fprint(2, "capacity %,lud sectors (%,lld bytes); bd%s\n",
1246efd1a06fSDavid du Colombier track->end, cap, drive->laysfx);
124781f927e7SDavid du Colombier if (osfx == nil || strcmp(osfx, drive->laysfx) != 0)
124881f927e7SDavid du Colombier drive->relearn = 1;
1249efd1a06fSDavid du Colombier }
1250*0c9a0762SDavid du Colombier if (drive->mmctype == Mmcnone)
1251*0c9a0762SDavid du Colombier return last; /* no disc */
1252efd1a06fSDavid du Colombier nwa = computenwa(drive, first, last);
1253efd1a06fSDavid du Colombier aux = drive->aux;
1254efd1a06fSDavid du Colombier if (vflag)
1255efd1a06fSDavid du Colombier fprint(2, "nwa from drive %,ld; computed nwa %,ld\n",
1256efd1a06fSDavid du Colombier aux->mmcnwa, nwa);
1257*0c9a0762SDavid du Colombier if (aux->mmcnwa == -1 && nwa == 0)
1258*0c9a0762SDavid du Colombier return last; /* probably a blank disc */
1259efd1a06fSDavid du Colombier if (aux->mmcnwa == -1)
1260efd1a06fSDavid du Colombier fprint(2, "%s: disc is full\n", argv0);
1261efd1a06fSDavid du Colombier /* reconcile differing nwas */
1262efd1a06fSDavid du Colombier if (aux->mmcnwa != nwa) {
1263efd1a06fSDavid du Colombier fprint(2, "%s: nwa from drive %,ld != computed nwa %,ld\n",
1264efd1a06fSDavid du Colombier argv0, aux->mmcnwa, nwa);
1265efd1a06fSDavid du Colombier fprint(2, "\tbe careful! assuming computed nwa\n");
1266efd1a06fSDavid du Colombier /* the invisible track may still start at the old nwa. */
1267efd1a06fSDavid du Colombier // aux->mmcnwa = nwa;
1268efd1a06fSDavid du Colombier }
1269efd1a06fSDavid du Colombier return last;
1270efd1a06fSDavid du Colombier }
1271efd1a06fSDavid du Colombier
1272efd1a06fSDavid du Colombier static int
findtracks(Drive * drive,int first,int last)1273efd1a06fSDavid du Colombier findtracks(Drive *drive, int first, int last)
1274efd1a06fSDavid du Colombier {
1275efd1a06fSDavid du Colombier if(first == 0 && last == 0)
1276efd1a06fSDavid du Colombier first = 1;
1277efd1a06fSDavid du Colombier
1278efd1a06fSDavid du Colombier if(first <= 0 || first >= Maxtrack) {
1279efd1a06fSDavid du Colombier werrstr("first table %d not in range", first);
1280efd1a06fSDavid du Colombier return -1;
1281efd1a06fSDavid du Colombier }
1282efd1a06fSDavid du Colombier if(last <= 0 || last >= Maxtrack) {
1283efd1a06fSDavid du Colombier werrstr("last table %d not in range", last);
1284efd1a06fSDavid du Colombier return -1;
1285efd1a06fSDavid du Colombier }
1286efd1a06fSDavid du Colombier
1287efd1a06fSDavid du Colombier if(!(drive->cap & Cwrite))
1288efd1a06fSDavid du Colombier last = mmcinfertracks(drive, first, last);
1289efd1a06fSDavid du Colombier else
1290efd1a06fSDavid du Colombier last = alltrackinfo(drive, first, last);
1291efd1a06fSDavid du Colombier drive->firsttrack = first;
1292efd1a06fSDavid du Colombier drive->ntrack = last+1-first;
1293efd1a06fSDavid du Colombier if (vflag)
1294efd1a06fSDavid du Colombier fprint(2, "this time for sure: first %d last %d\n", first, last);
1295efd1a06fSDavid du Colombier return 0;
1296efd1a06fSDavid du Colombier }
1297efd1a06fSDavid du Colombier
1298efd1a06fSDavid du Colombier static void
finddisctype(Drive * drive,int first,int last)1299efd1a06fSDavid du Colombier finddisctype(Drive *drive, int first, int last)
1300efd1a06fSDavid du Colombier {
1301586f04fdSDavid du Colombier char *ty;
1302586f04fdSDavid du Colombier
130360ba15acSDavid du Colombier /* deduce disc type */
1304cdf9e71cSDavid du Colombier drive->mmctype = Mmcnone;
13058ad6bb6aSDavid du Colombier drive->dvdtype = nil;
1306c038c065SDavid du Colombier getdvdstruct(drive);
130760ba15acSDavid du Colombier getconf(drive);
1308c038c065SDavid du Colombier getbdstruct(drive);
130960ba15acSDavid du Colombier if (Desperate)
131060ba15acSDavid du Colombier bdguess(drive);
1311cdf9e71cSDavid du Colombier if (drive->mmctype == Mmcnone)
1312cdf9e71cSDavid du Colombier drive->mmctype = Mmccd; /* by default */
131360ba15acSDavid du Colombier if (drive->recordable == Yes || drive->erasable == Yes)
131460ba15acSDavid du Colombier drive->writeok = Yes;
1315c038c065SDavid du Colombier
1316c038c065SDavid du Colombier if (vflag) {
1317c038c065SDavid du Colombier fprint(2, "writeok %d", drive->writeok);
131860ba15acSDavid du Colombier if (drive->recordable != Unset)
1319c038c065SDavid du Colombier fprint(2, " recordable %d", drive->recordable);
132060ba15acSDavid du Colombier if (drive->erasable != Unset)
1321c038c065SDavid du Colombier fprint(2, " erasable %d", drive->erasable);
1322c038c065SDavid du Colombier fprint(2, "\n");
1323efd1a06fSDavid du Colombier fprint(2, "first %d last ", first);
1324efd1a06fSDavid du Colombier if (last == Maxresptracks)
1325efd1a06fSDavid du Colombier print("TBD\n");
1326cdf9e71cSDavid du Colombier else
1327efd1a06fSDavid du Colombier print("%d\n", last);
1328586f04fdSDavid du Colombier
1329586f04fdSDavid du Colombier ty = disctype(drive);
1330586f04fdSDavid du Colombier fprint(2, "it's a %s disc.", ty);
1331586f04fdSDavid du Colombier free(ty);
1332586f04fdSDavid du Colombier if (drive->mmctype == Mmcbd && drive->laysfx == nil)
1333586f04fdSDavid du Colombier fprint(2, " (number of layers isn't known yet.)");
1334586f04fdSDavid du Colombier fprint(2, "\n\n");
1335efd1a06fSDavid du Colombier }
1336efd1a06fSDavid du Colombier }
13377dd7cddfSDavid du Colombier
1338efd1a06fSDavid du Colombier /* this gets called a lot from main.c's 9P routines */
1339efd1a06fSDavid du Colombier static int
mmcgettoc(Drive * drive)1340efd1a06fSDavid du Colombier mmcgettoc(Drive *drive)
1341efd1a06fSDavid du Colombier {
1342efd1a06fSDavid du Colombier int first, last;
1343efd1a06fSDavid du Colombier uchar resp[1024];
1344efd1a06fSDavid du Colombier
1345efd1a06fSDavid du Colombier /*
1346efd1a06fSDavid du Colombier * if someone has swapped the cd,
1347efd1a06fSDavid du Colombier * mmcreadtoc will get ``medium changed'' and the scsi routines
1348efd1a06fSDavid du Colombier * will set nchange and changetime in the scsi device.
1349efd1a06fSDavid du Colombier */
1350efd1a06fSDavid du Colombier mmcreadtoc(drive, 0, 0, resp, sizeof(resp));
1351efd1a06fSDavid du Colombier if(drive->Scsi.changetime == 0) { /* no media present */
1352efd1a06fSDavid du Colombier drive->mmctype = Mmcnone;
1353efd1a06fSDavid du Colombier drive->ntrack = 0;
13547dd7cddfSDavid du Colombier return 0;
13557dd7cddfSDavid du Colombier }
1356efd1a06fSDavid du Colombier /*
1357efd1a06fSDavid du Colombier * if the disc doesn't appear to be have been changed, and there
1358efd1a06fSDavid du Colombier * is a disc in this drive, there's nothing to do (the common case).
1359efd1a06fSDavid du Colombier */
136081f927e7SDavid du Colombier if(drive->nchange == drive->Scsi.nchange && drive->changetime != 0 &&
136181f927e7SDavid du Colombier !drive->relearn)
1362efd1a06fSDavid du Colombier return 0;
1363586f04fdSDavid du Colombier drive->relearn = 0;
1364efd1a06fSDavid du Colombier
1365efd1a06fSDavid du Colombier /*
1366efd1a06fSDavid du Colombier * the disc in the drive may have just been changed,
1367efd1a06fSDavid du Colombier * so rescan it and relearn all about it.
1368efd1a06fSDavid du Colombier */
1369586f04fdSDavid du Colombier if (vflag &&
1370586f04fdSDavid du Colombier (drive->nchange != drive->Scsi.nchange || drive->changetime == 0))
1371efd1a06fSDavid du Colombier fprint(2, "\nnew disc in drive\n");
1372efd1a06fSDavid du Colombier initdrive(drive);
1373efd1a06fSDavid du Colombier
1374efd1a06fSDavid du Colombier if (findntracks(drive, &first, &last) < 0)
1375efd1a06fSDavid du Colombier return -1;
1376efd1a06fSDavid du Colombier /*
137781f927e7SDavid du Colombier * we would like to call findtracks before finddisctype so that
1378586f04fdSDavid du Colombier * we can format bd-rs at the right capacity with explicit spares
137981f927e7SDavid du Colombier * ratio given, but findtracks needs the disc type to be already set.
1380586f04fdSDavid du Colombier * format is called from getconf from finddisctype before getbdstruct.
1381586f04fdSDavid du Colombier * luckily, FORMAT UNIT (thus format()) doesn't seem to care when we
1382586f04fdSDavid du Colombier * don't provide an explicit spares ratio.
1383efd1a06fSDavid du Colombier */
1384586f04fdSDavid du Colombier /* calls getdvdstruct, getconf and getbdstruct, in that order */
1385586f04fdSDavid du Colombier finddisctype(drive, first, last); /* formats bds at first use */
1386efd1a06fSDavid du Colombier return findtracks(drive, first, last);
1387efd1a06fSDavid du Colombier }
13887dd7cddfSDavid du Colombier
13890599ba09SDavid du Colombier static void
settrkmode(uchar * p,int mode)13900599ba09SDavid du Colombier settrkmode(uchar *p, int mode)
13910599ba09SDavid du Colombier {
13920599ba09SDavid du Colombier p[Wptrkmode] &= ~0xf;
13930599ba09SDavid du Colombier p[Wptrkmode] |= mode;
13940599ba09SDavid du Colombier }
13950599ba09SDavid du Colombier
1396e67f3b95SDavid du Colombier /*
1397e67f3b95SDavid du Colombier * this uses page 5, which is optional.
1398e67f3b95SDavid du Colombier */
13997dd7cddfSDavid du Colombier static int
mmcsetbs(Drive * drive,int bs)14007dd7cddfSDavid du Colombier mmcsetbs(Drive *drive, int bs)
14017dd7cddfSDavid du Colombier {
14027dd7cddfSDavid du Colombier uchar *p;
14037dd7cddfSDavid du Colombier Mmcaux *aux;
14047dd7cddfSDavid du Colombier
14057dd7cddfSDavid du Colombier aux = drive->aux;
1406e67f3b95SDavid du Colombier if (!aux->page05ok)
1407e67f3b95SDavid du Colombier return 0; /* harmless; assume 2k */
14087dd7cddfSDavid du Colombier
14097dd7cddfSDavid du Colombier p = aux->page05;
1410c038c065SDavid du Colombier /*
1411c038c065SDavid du Colombier * establish defaults.
1412c038c065SDavid du Colombier */
1413436f307dSDavid du Colombier p[0] &= 077; /* clear reserved bits, MMC-6 §7.2.3 */
14140599ba09SDavid du Colombier p[Wpwrtype] = Bufe | Wttrackonce;
14150599ba09SDavid du Colombier // if(testonlyflag)
14160599ba09SDavid du Colombier // p[Wpwrtype] |= 0x10; /* test-write */
14170599ba09SDavid du Colombier /* assume dvd values as defaults */
14180599ba09SDavid du Colombier settrkmode(p, Tmintr);
14190599ba09SDavid du Colombier p[Wptrkmode] |= Msnext;
14200599ba09SDavid du Colombier p[Wpdatblktype] = Db2kdata;
14210599ba09SDavid du Colombier p[Wpsessfmt] = Sfdata;
14220599ba09SDavid du Colombier
1423cdf9e71cSDavid du Colombier switch(drive->mmctype) {
14240599ba09SDavid du Colombier case Mmcdvdplus:
14250599ba09SDavid du Colombier case Mmcbd:
14267dd7cddfSDavid du Colombier break;
14270599ba09SDavid du Colombier case Mmcdvdminus:
14280599ba09SDavid du Colombier /* dvd-r can only do disc-at-once or incremental */
14290599ba09SDavid du Colombier p[Wpwrtype] = Bufe | Wtsessonce;
14300599ba09SDavid du Colombier break;
14310599ba09SDavid du Colombier case Mmccd:
14320599ba09SDavid du Colombier settrkmode(p, Tmunintr); /* data track, uninterrupted */
14330599ba09SDavid du Colombier switch(bs){
14347dd7cddfSDavid du Colombier case BScdda:
1435c038c065SDavid du Colombier /* 2 audio channels without pre-emphasis */
143660ba15acSDavid du Colombier settrkmode(p, Tmcdda); /* should be Tm2audio? */
14370599ba09SDavid du Colombier p[Wpdatblktype] = Dbraw;
14380599ba09SDavid du Colombier break;
14390599ba09SDavid du Colombier case BScdrom:
14407dd7cddfSDavid du Colombier break;
14417dd7cddfSDavid du Colombier case BScdxa:
14420599ba09SDavid du Colombier p[Wpdatblktype] = Db2336;
14430599ba09SDavid du Colombier p[Wpsessfmt] = Sfcdxa;
14447dd7cddfSDavid du Colombier break;
14457dd7cddfSDavid du Colombier default:
1446c038c065SDavid du Colombier fprint(2, "%s: unknown CD type; bs %d\n", argv0, bs);
14477dd7cddfSDavid du Colombier assert(0);
14487dd7cddfSDavid du Colombier }
1449c038c065SDavid du Colombier break;
1450c038c065SDavid du Colombier default:
1451c038c065SDavid du Colombier fprint(2, "%s: unknown disc sub-type %d\n",
1452cdf9e71cSDavid du Colombier argv0, drive->mmctype);
1453c038c065SDavid du Colombier break;
1454c038c065SDavid du Colombier }
1455c038c065SDavid du Colombier if(mmcsetpage(drive, Pagwrparams, p) < 0) {
1456c038c065SDavid du Colombier if (vflag)
1457c038c065SDavid du Colombier fprint(2, "mmcsetbs: could NOT set write parameters page\n");
14587dd7cddfSDavid du Colombier return -1;
1459c038c065SDavid du Colombier }
1460ec46fab0SDavid du Colombier return 0;
14617dd7cddfSDavid du Colombier }
14627dd7cddfSDavid du Colombier
1463efd1a06fSDavid du Colombier /*
1464efd1a06fSDavid du Colombier * `read cd' only works for CDs; for everybody else,
1465efd1a06fSDavid du Colombier * we'll try plain `read (12)'. only use read cd if it's
1466efd1a06fSDavid du Colombier * a cd drive with a cd in it and we're not reading data
1467efd1a06fSDavid du Colombier * (e.g., reading audio).
1468efd1a06fSDavid du Colombier */
1469efd1a06fSDavid du Colombier static int
fillread12cdb(Drive * drive,uchar * cmd,long nblock,ulong off,int bs)1470efd1a06fSDavid du Colombier fillread12cdb(Drive *drive, uchar *cmd, long nblock, ulong off, int bs)
1471efd1a06fSDavid du Colombier {
1472efd1a06fSDavid du Colombier if (drive->type == TypeCD && drive->mmctype == Mmccd && bs != BScdrom) {
1473efd1a06fSDavid du Colombier initcdb(cmd, 12, ScmdReadcd);
1474efd1a06fSDavid du Colombier cmd[6] = nblock>>16;
1475efd1a06fSDavid du Colombier cmd[7] = nblock>>8;
1476efd1a06fSDavid du Colombier cmd[8] = nblock>>0;
1477efd1a06fSDavid du Colombier cmd[9] = 0x10;
1478efd1a06fSDavid du Colombier switch(bs){
1479efd1a06fSDavid du Colombier case BScdda:
1480efd1a06fSDavid du Colombier cmd[1] = 0x04;
1481efd1a06fSDavid du Colombier break;
1482efd1a06fSDavid du Colombier case BScdrom:
1483efd1a06fSDavid du Colombier cmd[1] = 0x08;
1484efd1a06fSDavid du Colombier break;
1485efd1a06fSDavid du Colombier case BScdxa:
1486efd1a06fSDavid du Colombier cmd[1] = 0x0C;
1487efd1a06fSDavid du Colombier break;
1488efd1a06fSDavid du Colombier default:
1489efd1a06fSDavid du Colombier werrstr("unknown bs %d", bs);
1490efd1a06fSDavid du Colombier return -1;
1491efd1a06fSDavid du Colombier }
1492efd1a06fSDavid du Colombier } else { /* e.g., TypeDA */
1493efd1a06fSDavid du Colombier initcdb(cmd, 12, ScmdRead12);
1494efd1a06fSDavid du Colombier cmd[6] = nblock>>24;
1495efd1a06fSDavid du Colombier cmd[7] = nblock>>16;
1496efd1a06fSDavid du Colombier cmd[8] = nblock>>8;
1497efd1a06fSDavid du Colombier cmd[9] = nblock;
1498efd1a06fSDavid du Colombier // cmd[10] = 0x80; /* streaming */
1499efd1a06fSDavid du Colombier }
1500efd1a06fSDavid du Colombier cmd[2] = off>>24;
1501efd1a06fSDavid du Colombier cmd[3] = off>>16;
1502efd1a06fSDavid du Colombier cmd[4] = off>>8;
1503efd1a06fSDavid du Colombier cmd[5] = off>>0;
1504efd1a06fSDavid du Colombier return 0;
1505efd1a06fSDavid du Colombier }
1506efd1a06fSDavid du Colombier
150773ee67a1SDavid du Colombier /* off is a block number */
15087dd7cddfSDavid du Colombier static long
mmcread(Buf * buf,void * v,long nblock,ulong off)1509dd12a5c6SDavid du Colombier mmcread(Buf *buf, void *v, long nblock, ulong off)
15107dd7cddfSDavid du Colombier {
15117dd7cddfSDavid du Colombier int bs;
15127dd7cddfSDavid du Colombier long n, nn;
151373ee67a1SDavid du Colombier uchar cmd[12];
151473ee67a1SDavid du Colombier Drive *drive;
15157dd7cddfSDavid du Colombier Otrack *o;
15167dd7cddfSDavid du Colombier
15177dd7cddfSDavid du Colombier o = buf->otrack;
15187dd7cddfSDavid du Colombier drive = o->drive;
15197dd7cddfSDavid du Colombier bs = o->track->bs;
15207dd7cddfSDavid du Colombier off += o->track->beg;
15217dd7cddfSDavid du Colombier
15227dd7cddfSDavid du Colombier if(nblock >= (1<<10)) {
15237dd7cddfSDavid du Colombier werrstr("mmcread too big");
15247dd7cddfSDavid du Colombier if(vflag)
15257dd7cddfSDavid du Colombier fprint(2, "mmcread too big\n");
15267dd7cddfSDavid du Colombier return -1;
15277dd7cddfSDavid du Colombier }
15287dd7cddfSDavid du Colombier
152959cc4ca5SDavid du Colombier /* truncate nblock modulo size of track */
153059cc4ca5SDavid du Colombier if(off > o->track->end - 2) {
153159cc4ca5SDavid du Colombier werrstr("read past end of track");
153259cc4ca5SDavid du Colombier if(vflag)
1533e67f3b95SDavid du Colombier fprint(2, "end of track (%ld->%ld off %ld)",
1534e67f3b95SDavid du Colombier o->track->beg, o->track->end - 2, off);
153559cc4ca5SDavid du Colombier return -1;
153659cc4ca5SDavid du Colombier }
153759cc4ca5SDavid du Colombier if(off == o->track->end - 2)
153859cc4ca5SDavid du Colombier return 0;
153959cc4ca5SDavid du Colombier
154059cc4ca5SDavid du Colombier if(off+nblock > o->track->end - 2)
154159cc4ca5SDavid du Colombier nblock = o->track->end - 2 - off;
154259cc4ca5SDavid du Colombier
1543efd1a06fSDavid du Colombier if (fillread12cdb(drive, cmd, nblock, off, bs) < 0)
15447dd7cddfSDavid du Colombier return -1;
1545efd1a06fSDavid du Colombier
15467dd7cddfSDavid du Colombier n = nblock*bs;
15477dd7cddfSDavid du Colombier nn = scsi(drive, cmd, sizeof(cmd), v, n, Sread);
15487dd7cddfSDavid du Colombier if(nn != n) {
154964c7f6b6SDavid du Colombier if(nn != -1)
15507dd7cddfSDavid du Colombier werrstr("short read %ld/%ld", nn, n);
15517dd7cddfSDavid du Colombier if(vflag)
1552e67f3b95SDavid du Colombier print("read off %lud nblock %ld bs %d failed\n",
1553e67f3b95SDavid du Colombier off, nblock, bs);
15547dd7cddfSDavid du Colombier return -1;
15557dd7cddfSDavid du Colombier }
15567dd7cddfSDavid du Colombier return nblock;
15577dd7cddfSDavid du Colombier }
15587dd7cddfSDavid du Colombier
15597dd7cddfSDavid du Colombier static Otrack*
mmcopenrd(Drive * drive,int trackno)15607dd7cddfSDavid du Colombier mmcopenrd(Drive *drive, int trackno)
15617dd7cddfSDavid du Colombier {
15627dd7cddfSDavid du Colombier Otrack *o;
15637dd7cddfSDavid du Colombier Mmcaux *aux;
15647dd7cddfSDavid du Colombier
15657dd7cddfSDavid du Colombier if(trackno < 0 || trackno >= drive->ntrack) {
15667dd7cddfSDavid du Colombier werrstr("track number out of range");
15677dd7cddfSDavid du Colombier return nil;
15687dd7cddfSDavid du Colombier }
15697dd7cddfSDavid du Colombier
15707dd7cddfSDavid du Colombier aux = drive->aux;
15717dd7cddfSDavid du Colombier if(aux->nwopen) {
15727dd7cddfSDavid du Colombier werrstr("disk in use for writing");
15737dd7cddfSDavid du Colombier return nil;
15747dd7cddfSDavid du Colombier }
15757dd7cddfSDavid du Colombier
15767dd7cddfSDavid du Colombier o = emalloc(sizeof(Otrack));
15777dd7cddfSDavid du Colombier o->drive = drive;
15787dd7cddfSDavid du Colombier o->track = &drive->track[trackno];
15797dd7cddfSDavid du Colombier o->nchange = drive->nchange;
15807dd7cddfSDavid du Colombier o->omode = OREAD;
15817386956aSDavid du Colombier o->buf = bopen(mmcread, OREAD, o->track->bs, Readblock);
15827dd7cddfSDavid du Colombier o->buf->otrack = o;
15837dd7cddfSDavid du Colombier
15847dd7cddfSDavid du Colombier aux->nropen++;
15857dd7cddfSDavid du Colombier
15867dd7cddfSDavid du Colombier return o;
15877dd7cddfSDavid du Colombier }
15887dd7cddfSDavid du Colombier
1589efd1a06fSDavid du Colombier enum {
1590efd1a06fSDavid du Colombier Flhlen = 4, /* format list header length */
1591efd1a06fSDavid du Colombier Fdlen = 8, /* format descriptor length */
1592efd1a06fSDavid du Colombier
1593efd1a06fSDavid du Colombier /* format unit cdb[1] */
1594efd1a06fSDavid du Colombier Formatdata = 0x10,
1595efd1a06fSDavid du Colombier
1596efd1a06fSDavid du Colombier /* format list header [1] */
1597efd1a06fSDavid du Colombier Immed = 1<<1, /* immediate return, don't wait */
1598efd1a06fSDavid du Colombier
1599efd1a06fSDavid du Colombier /* format descriptor [4] */
1600efd1a06fSDavid du Colombier Fmtfull = 0,
1601efd1a06fSDavid du Colombier Fullbdrsrm = 0, /* sequential */
1602efd1a06fSDavid du Colombier Fullbdrsrmspares= 1, /* + spares for defect mgmt. */
1603efd1a06fSDavid du Colombier Fullbdrrrm = 2, /* random */
1604efd1a06fSDavid du Colombier Fmtbdrspares = 0x32 << 2,
1605efd1a06fSDavid du Colombier Bdrsparessrmplus= 0, /* srm+pow */
1606efd1a06fSDavid du Colombier Bdrsparessrmminus=1, /* srm-pow */
1607efd1a06fSDavid du Colombier Bdrsparesrrm = 2, /* random */
1608efd1a06fSDavid du Colombier };
1609efd1a06fSDavid du Colombier
1610c038c065SDavid du Colombier static int
format(Drive * drive)1611c038c065SDavid du Colombier format(Drive *drive)
1612c038c065SDavid du Colombier {
1613efd1a06fSDavid du Colombier int setblksz;
1614cdf9e71cSDavid du Colombier ulong nblks, blksize;
1615c038c065SDavid du Colombier uchar *fmtdesc;
1616efd1a06fSDavid du Colombier uchar cmd[6], parms[Flhlen+Fdlen];
1617c038c065SDavid du Colombier
161860ba15acSDavid du Colombier if (drive->recordable == Yes && drive->mmctype != Mmcbd) {
1619cdf9e71cSDavid du Colombier werrstr("don't format write-once cd or dvd media");
1620cdf9e71cSDavid du Colombier return -1;
1621cdf9e71cSDavid du Colombier }
1622c038c065SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdFormat); /* format unit */
1623efd1a06fSDavid du Colombier cmd[1] = Formatdata | 1; /* mmc format code 1 */
1624c038c065SDavid du Colombier
1625efd1a06fSDavid du Colombier /* parms is format list header & format descriptor */
1626c038c065SDavid du Colombier memset(parms, 0, sizeof parms);
1627c038c065SDavid du Colombier
1628efd1a06fSDavid du Colombier /* format list header */
1629efd1a06fSDavid du Colombier parms[1] = Immed;
1630efd1a06fSDavid du Colombier parms[3] = Fdlen;
1631efd1a06fSDavid du Colombier
1632efd1a06fSDavid du Colombier fmtdesc = parms + Flhlen;
1633efd1a06fSDavid du Colombier fmtdesc[4] = Fmtfull;
1634efd1a06fSDavid du Colombier /* 0-3 are big-endian # of blocks, 5-7 format-dependent data */
1635cdf9e71cSDavid du Colombier
1636cdf9e71cSDavid du Colombier nblks = 0;
1637cdf9e71cSDavid du Colombier blksize = BScdrom;
1638efd1a06fSDavid du Colombier setblksz = 1;
1639cdf9e71cSDavid du Colombier switch (drive->mmctype) {
1640cdf9e71cSDavid du Colombier case Mmccd:
1641cdf9e71cSDavid du Colombier nblks = 0;
1642cdf9e71cSDavid du Colombier break;
1643cdf9e71cSDavid du Colombier case Mmcdvdplus:
1644cdf9e71cSDavid du Colombier /* format type 0 is optional but equiv to format type 0x26 */
1645cdf9e71cSDavid du Colombier fmtdesc[4] = 0x26 << 2;
1646cdf9e71cSDavid du Colombier nblks = ~0ul;
1647cdf9e71cSDavid du Colombier blksize = 0;
1648cdf9e71cSDavid du Colombier break;
1649efd1a06fSDavid du Colombier case Mmcbd:
1650efd1a06fSDavid du Colombier /* enable BD-R defect mgmt. */
1651efd1a06fSDavid du Colombier if (drive->recordable == Yes && drive->erasable == No) {
1652efd1a06fSDavid du Colombier parms[1] &= ~Immed;
1653efd1a06fSDavid du Colombier fmtdesc[4] |= Fullbdrsrmspares;
1654efd1a06fSDavid du Colombier setblksz = 0;
1655efd1a06fSDavid du Colombier }
1656efd1a06fSDavid du Colombier break;
1657cdf9e71cSDavid du Colombier }
1658cdf9e71cSDavid du Colombier
1659cdf9e71cSDavid du Colombier PUTBELONG(fmtdesc, nblks);
1660efd1a06fSDavid du Colombier if (setblksz)
1661cdf9e71cSDavid du Colombier PUTBE24(fmtdesc + 5, blksize);
1662cdf9e71cSDavid du Colombier
1663cdf9e71cSDavid du Colombier // print("format parameters:\n");
1664cdf9e71cSDavid du Colombier // hexdump(parms, sizeof parms);
1665c038c065SDavid du Colombier
1666c038c065SDavid du Colombier if(vflag)
1667c038c065SDavid du Colombier print("%lld ns: format\n", nsec());
1668c038c065SDavid du Colombier return scsi(drive, cmd, sizeof(cmd), parms, sizeof parms, Swrite);
1669c038c065SDavid du Colombier }
1670c038c065SDavid du Colombier
16716397270fSDavid du Colombier static int
dvdcanverify(Drive * drive)16726397270fSDavid du Colombier dvdcanverify(Drive *drive)
16736397270fSDavid du Colombier {
16746397270fSDavid du Colombier return (drive->mmctype == Mmcdvdplus ||
16756397270fSDavid du Colombier drive->mmctype == Mmcdvdminus) &&
16766397270fSDavid du Colombier isbitset(Featrandwrite, drive->features) &&
16776397270fSDavid du Colombier isbitset(Featwriteonce, drive->features) &&
16786397270fSDavid du Colombier isbitset(Featdvdrw, drive->features);
16796397270fSDavid du Colombier }
16806397270fSDavid du Colombier
16817dd7cddfSDavid du Colombier static long
mmcxwrite(Otrack * o,void * v,long nblk)168259cc4ca5SDavid du Colombier mmcxwrite(Otrack *o, void *v, long nblk)
16837dd7cddfSDavid du Colombier {
16847f1c8a1dSDavid du Colombier int r;
168539b91b2bSDavid du Colombier long bs;
16867dd7cddfSDavid du Colombier uchar cmd[10];
168739b91b2bSDavid du Colombier Drive *drive;
16887dd7cddfSDavid du Colombier Mmcaux *aux;
16897dd7cddfSDavid du Colombier
16907dd7cddfSDavid du Colombier assert(o->omode == OWRITE);
169139b91b2bSDavid du Colombier bs = o->track->bs;
169239b91b2bSDavid du Colombier drive = o->drive;
169339b91b2bSDavid du Colombier aux = drive->aux;
169439b91b2bSDavid du Colombier if (aux->mmcnwa == -1 && scsiready(drive) < 0) {
1695684b447eSDavid du Colombier werrstr("device not ready to write");
1696684b447eSDavid du Colombier return -1;
1697684b447eSDavid du Colombier }
1698efd1a06fSDavid du Colombier if (aux->mmcnwa == -1 ||
169939b91b2bSDavid du Colombier drive->end != 0 && aux->mmcnwa + nblk > drive->end) {
1700efd1a06fSDavid du Colombier werrstr("writing past last block");
1701efd1a06fSDavid du Colombier return -1;
1702efd1a06fSDavid du Colombier }
1703efd1a06fSDavid du Colombier if (nblk <= 0)
1704efd1a06fSDavid du Colombier fprint(2, "mmcxwrite: nblk %ld <= 0\n", nblk);
170539b91b2bSDavid du Colombier aux->ntotby += nblk * bs;
17067dd7cddfSDavid du Colombier aux->ntotbk += nblk;
1707c038c065SDavid du Colombier
170839b91b2bSDavid du Colombier /*
170939b91b2bSDavid du Colombier * "write and verify" (ScmdExtwritever) only works on write-once media
171039b91b2bSDavid du Colombier * and not on CDs (mmc-6 §6.48.1).
171139b91b2bSDavid du Colombier */
17126397270fSDavid du Colombier if ((drive->mmctype == Mmcbd || dvdcanverify(drive)) &&
171339b91b2bSDavid du Colombier drive->recordable == Yes && drive->erasable == No)
1714efd1a06fSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdExtwritever);
171539b91b2bSDavid du Colombier else
171639b91b2bSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdExtwrite); /* write (10) */
17177dd7cddfSDavid du Colombier cmd[2] = aux->mmcnwa>>24;
17187dd7cddfSDavid du Colombier cmd[3] = aux->mmcnwa>>16;
17197dd7cddfSDavid du Colombier cmd[4] = aux->mmcnwa>>8;
17207dd7cddfSDavid du Colombier cmd[5] = aux->mmcnwa;
17217dd7cddfSDavid du Colombier cmd[7] = nblk>>8;
17227dd7cddfSDavid du Colombier cmd[8] = nblk>>0;
1723efd1a06fSDavid du Colombier if(vflag > 1)
1724efd1a06fSDavid du Colombier print("%lld ns: write+verify %ld at %#lux\n",
1725684b447eSDavid du Colombier nsec(), nblk, aux->mmcnwa);
1726efd1a06fSDavid du Colombier /*
1727efd1a06fSDavid du Colombier * we are using a private copy of scsi.c that doesn't retry writes
1728efd1a06fSDavid du Colombier * to ensure that the write-verify command is issued exactly once.
1729efd1a06fSDavid du Colombier * the drive may perform internal defect management on write errors,
1730efd1a06fSDavid du Colombier * but explicit attempts to rewrite a given sector on write-once
1731efd1a06fSDavid du Colombier * media are guaranteed to fail.
1732efd1a06fSDavid du Colombier */
173339b91b2bSDavid du Colombier r = scsi(drive, cmd, sizeof(cmd), v, nblk * bs, Swrite);
17347f1c8a1dSDavid du Colombier if (r < 0)
1735efd1a06fSDavid du Colombier fprint(2, "%s: write+verify error at blk offset %,ld = "
17367f1c8a1dSDavid du Colombier "offset %,lld / bs %ld: %r\n",
173739b91b2bSDavid du Colombier argv0, aux->mmcnwa, (vlong)aux->mmcnwa * bs, bs);
1738efd1a06fSDavid du Colombier else
17397dd7cddfSDavid du Colombier aux->mmcnwa += nblk;
17407f1c8a1dSDavid du Colombier return r;
17417dd7cddfSDavid du Colombier }
17427dd7cddfSDavid du Colombier
174359cc4ca5SDavid du Colombier static long
mmcwrite(Buf * buf,void * v,long nblk,ulong)1744dd12a5c6SDavid du Colombier mmcwrite(Buf *buf, void *v, long nblk, ulong)
174559cc4ca5SDavid du Colombier {
174659cc4ca5SDavid du Colombier return mmcxwrite(buf->otrack, v, nblk);
174759cc4ca5SDavid du Colombier }
174859cc4ca5SDavid du Colombier
17490599ba09SDavid du Colombier enum {
17500599ba09SDavid du Colombier Eccblk = 128, /* sectors per ecc block */
17510599ba09SDavid du Colombier Rsvslop = 0,
17520599ba09SDavid du Colombier };
17530599ba09SDavid du Colombier
17540599ba09SDavid du Colombier static int
reserve(Drive * drive,int track)17550599ba09SDavid du Colombier reserve(Drive *drive, int track)
17560599ba09SDavid du Colombier {
17570599ba09SDavid du Colombier ulong sz;
17580599ba09SDavid du Colombier uchar cmd[10];
17590599ba09SDavid du Colombier
17600599ba09SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdReserve);
17610599ba09SDavid du Colombier track -= drive->firsttrack; /* switch to zero-origin */
17620599ba09SDavid du Colombier if (track >= 0 && track < drive->ntrack)
17630599ba09SDavid du Colombier /* .end is next sector past sz */
17640599ba09SDavid du Colombier sz = drive->track[track].end - drive->track[track].beg - Rsvslop;
17650599ba09SDavid du Colombier else {
17660599ba09SDavid du Colombier sz = Eccblk;
17670599ba09SDavid du Colombier fprint(2, "%s: reserve: track #%d out of range 0-%d\n",
17680599ba09SDavid du Colombier argv0, track, drive->ntrack);
17690599ba09SDavid du Colombier }
17700599ba09SDavid du Colombier sz -= sz % Eccblk; /* round down to ecc-block multiple */
17710599ba09SDavid du Colombier if ((long)sz < 0) {
17720599ba09SDavid du Colombier fprint(2, "%s: reserve: bogus size %lud\n", argv0, sz);
17730599ba09SDavid du Colombier return -1;
17740599ba09SDavid du Colombier }
17750599ba09SDavid du Colombier cmd[1] = 0; /* no ASRV: allocate by size not lba */
17760599ba09SDavid du Colombier PUTBELONG(cmd + 2 + 3, sz);
17770599ba09SDavid du Colombier if (vflag)
17780599ba09SDavid du Colombier fprint(2, "reserving %ld sectors\n", sz);
17790599ba09SDavid du Colombier return scsi(drive, cmd, sizeof cmd, cmd, 0, Snone);
17800599ba09SDavid du Colombier }
17810599ba09SDavid du Colombier
1782684b447eSDavid du Colombier static int
getinvistrack(Drive * drive)1783684b447eSDavid du Colombier getinvistrack(Drive *drive)
1784684b447eSDavid du Colombier {
1785684b447eSDavid du Colombier int n;
1786684b447eSDavid du Colombier uchar cmd[10], resp[Pagesz];
1787684b447eSDavid du Colombier
1788684b447eSDavid du Colombier initcdb(cmd, sizeof(cmd), ScmdRtrackinfo);
1789684b447eSDavid du Colombier cmd[1] = 1<<2 | 1; /* open; address below is logical track # */
1790684b447eSDavid du Colombier PUTBELONG(cmd + 2, 1); /* find first open track */
1791684b447eSDavid du Colombier cmd[7] = sizeof(resp)>>8;
1792684b447eSDavid du Colombier cmd[8] = sizeof(resp);
1793684b447eSDavid du Colombier n = scsi(drive, cmd, sizeof(cmd), resp, sizeof(resp), Sread);
1794684b447eSDavid du Colombier if(n < 4) {
1795684b447eSDavid du Colombier if(vflag)
1796684b447eSDavid du Colombier print("trackinfo for invis track fails n=%d: %r\n", n);
1797684b447eSDavid du Colombier return -1;
1798684b447eSDavid du Colombier }
1799684b447eSDavid du Colombier
1800684b447eSDavid du Colombier if(vflag)
1801684b447eSDavid du Colombier print("getinvistrack: track #%d session #%d\n",
1802684b447eSDavid du Colombier resp[2], resp[3]);
18037f1c8a1dSDavid du Colombier drive->invistrack = resp[2];
1804684b447eSDavid du Colombier return resp[2];
1805684b447eSDavid du Colombier }
1806684b447eSDavid du Colombier
18077dd7cddfSDavid du Colombier static Otrack*
mmccreate(Drive * drive,int type)18087dd7cddfSDavid du Colombier mmccreate(Drive *drive, int type)
18097dd7cddfSDavid du Colombier {
18107f1c8a1dSDavid du Colombier int bs;
18117dd7cddfSDavid du Colombier Mmcaux *aux;
18127dd7cddfSDavid du Colombier Track *t;
18137dd7cddfSDavid du Colombier Otrack *o;
18147dd7cddfSDavid du Colombier
18157dd7cddfSDavid du Colombier aux = drive->aux;
18167dd7cddfSDavid du Colombier
18177dd7cddfSDavid du Colombier if(aux->nropen || aux->nwopen) {
18187dd7cddfSDavid du Colombier werrstr("drive in use");
18197dd7cddfSDavid du Colombier return nil;
18207dd7cddfSDavid du Colombier }
18217dd7cddfSDavid du Colombier
18227dd7cddfSDavid du Colombier switch(type){
18237dd7cddfSDavid du Colombier case TypeAudio:
18247dd7cddfSDavid du Colombier bs = BScdda;
18257dd7cddfSDavid du Colombier break;
18267dd7cddfSDavid du Colombier case TypeData:
18277dd7cddfSDavid du Colombier bs = BScdrom;
18287dd7cddfSDavid du Colombier break;
18297dd7cddfSDavid du Colombier default:
18307dd7cddfSDavid du Colombier werrstr("bad type %d", type);
18317dd7cddfSDavid du Colombier return nil;
18327dd7cddfSDavid du Colombier }
18337dd7cddfSDavid du Colombier
1834c038c065SDavid du Colombier /* comment out the returns for now; it should be no big deal - geoff */
18357f1c8a1dSDavid du Colombier if(mmctrackinfo(drive, drive->invistrack, Maxtrack)) {
1836684b447eSDavid du Colombier if (vflag)
1837684b447eSDavid du Colombier fprint(2, "mmccreate: mmctrackinfo for invis track %d"
18387f1c8a1dSDavid du Colombier " failed: %r\n", drive->invistrack);
18390599ba09SDavid du Colombier werrstr("disc not writable");
1840c038c065SDavid du Colombier // return nil;
18417dd7cddfSDavid du Colombier }
18427dd7cddfSDavid du Colombier if(mmcsetbs(drive, bs) < 0) {
18437dd7cddfSDavid du Colombier werrstr("cannot set bs mode");
1844c038c065SDavid du Colombier // return nil;
18457dd7cddfSDavid du Colombier }
18467f1c8a1dSDavid du Colombier if(mmctrackinfo(drive, drive->invistrack, Maxtrack)) {
1847684b447eSDavid du Colombier if (vflag)
1848684b447eSDavid du Colombier fprint(2, "mmccreate: mmctrackinfo for invis track %d"
18497f1c8a1dSDavid du Colombier " (2) failed: %r\n", drive->invistrack);
18500599ba09SDavid du Colombier werrstr("disc not writable 2");
1851c038c065SDavid du Colombier // return nil;
18527dd7cddfSDavid du Colombier }
18537dd7cddfSDavid du Colombier
18540599ba09SDavid du Colombier /* special hack for dvd-r: reserve the invisible track */
18550599ba09SDavid du Colombier if (drive->mmctype == Mmcdvdminus && drive->writeok &&
18567f1c8a1dSDavid du Colombier drive->recordable == Yes && reserve(drive, drive->invistrack) < 0) {
18570599ba09SDavid du Colombier if (vflag)
18580599ba09SDavid du Colombier fprint(2, "mmcreate: reserving track %d for dvd-r "
18597f1c8a1dSDavid du Colombier "failed: %r\n", drive->invistrack);
18600599ba09SDavid du Colombier return nil;
18610599ba09SDavid du Colombier }
18620599ba09SDavid du Colombier
18637dd7cddfSDavid du Colombier aux->ntotby = 0;
18647dd7cddfSDavid du Colombier aux->ntotbk = 0;
18657dd7cddfSDavid du Colombier
18667dd7cddfSDavid du Colombier t = &drive->track[drive->ntrack++];
18677dd7cddfSDavid du Colombier t->size = 0;
18687dd7cddfSDavid du Colombier t->bs = bs;
18697dd7cddfSDavid du Colombier t->beg = aux->mmcnwa;
18707dd7cddfSDavid du Colombier t->end = 0;
18717dd7cddfSDavid du Colombier t->type = type;
18727dd7cddfSDavid du Colombier drive->nameok = 0;
18737dd7cddfSDavid du Colombier
18747dd7cddfSDavid du Colombier o = emalloc(sizeof(Otrack));
18757dd7cddfSDavid du Colombier o->drive = drive;
18767dd7cddfSDavid du Colombier o->nchange = drive->nchange;
18777dd7cddfSDavid du Colombier o->omode = OWRITE;
18787dd7cddfSDavid du Colombier o->track = t;
18797386956aSDavid du Colombier o->buf = bopen(mmcwrite, OWRITE, bs, Readblock);
18807dd7cddfSDavid du Colombier o->buf->otrack = o;
18817dd7cddfSDavid du Colombier
18827dd7cddfSDavid du Colombier aux->nwopen++;
18837dd7cddfSDavid du Colombier
18847dd7cddfSDavid du Colombier if(vflag)
1885efd1a06fSDavid du Colombier print("mmcinit: nwa = %#luX\n", aux->mmcnwa);
18867dd7cddfSDavid du Colombier return o;
18877dd7cddfSDavid du Colombier }
18887dd7cddfSDavid du Colombier
1889436f307dSDavid du Colombier /*
1890436f307dSDavid du Colombier * issue some form of close track, close session or finalize disc command.
18918ad6bb6aSDavid du Colombier * see The Matrix, table 252 in MMC-6 §6.3.2.3.
1892436f307dSDavid du Colombier */
1893436f307dSDavid du Colombier static int
mmcxclose(Drive * drive,int clf,int trackno)1894436f307dSDavid du Colombier mmcxclose(Drive *drive, int clf, int trackno)
1895436f307dSDavid du Colombier {
1896436f307dSDavid du Colombier uchar cmd[10];
1897436f307dSDavid du Colombier
189860ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdClosetracksess);
1899cdf9e71cSDavid du Colombier /* cmd[1] & 1 is the immediate bit */
1900436f307dSDavid du Colombier cmd[2] = clf; /* close function */
1901436f307dSDavid du Colombier if(clf == Closetrack)
1902436f307dSDavid du Colombier cmd[5] = trackno;
1903436f307dSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone);
1904436f307dSDavid du Colombier }
1905436f307dSDavid du Colombier
1906079e489fSDavid du Colombier /* flush drive cache, close current track */
19077dd7cddfSDavid du Colombier void
mmcsynccache(Drive * drive)19087dd7cddfSDavid du Colombier mmcsynccache(Drive *drive)
19097dd7cddfSDavid du Colombier {
19107dd7cddfSDavid du Colombier uchar cmd[10];
19117dd7cddfSDavid du Colombier Mmcaux *aux;
19127dd7cddfSDavid du Colombier
19130599ba09SDavid du Colombier initcdb(cmd, sizeof cmd, ScmdSynccache);
19140599ba09SDavid du Colombier /*
19150599ba09SDavid du Colombier * this will take a long time to burn the remainder of a dvd-r
19160599ba09SDavid du Colombier * with a reserved track covering the whole disc.
19170599ba09SDavid du Colombier */
19180599ba09SDavid du Colombier if (vflag) {
19190599ba09SDavid du Colombier fprint(2, "syncing cache");
19200599ba09SDavid du Colombier if (drive->mmctype == Mmcdvdminus && drive->writeok &&
192160ba15acSDavid du Colombier drive->recordable == Yes)
19220599ba09SDavid du Colombier fprint(2, "; dvd-r burning rest of track reservation, "
19230599ba09SDavid du Colombier "will be slow");
19240599ba09SDavid du Colombier fprint(2, "\n");
19250599ba09SDavid du Colombier }
19267dd7cddfSDavid du Colombier scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone);
19277dd7cddfSDavid du Colombier if(vflag) {
19287dd7cddfSDavid du Colombier aux = drive->aux;
1929efd1a06fSDavid du Colombier print("mmcsynccache: bytes = %lld blocks = %ld, mmcnwa %#luX\n",
19307dd7cddfSDavid du Colombier aux->ntotby, aux->ntotbk, aux->mmcnwa);
19317dd7cddfSDavid du Colombier }
1932079e489fSDavid du Colombier
1933684b447eSDavid du Colombier /*
1934684b447eSDavid du Colombier * rsc: seems not to work on some drives.
1935684b447eSDavid du Colombier * so ignore return code & don't issue on dvd+rw.
1936684b447eSDavid du Colombier */
193760ba15acSDavid du Colombier if(drive->mmctype != Mmcdvdplus || drive->erasable == No) {
1938684b447eSDavid du Colombier if (vflag)
1939684b447eSDavid du Colombier fprint(2, "closing invisible track %d (not dvd+rw)...\n",
19407f1c8a1dSDavid du Colombier drive->invistrack);
19417f1c8a1dSDavid du Colombier mmcxclose(drive, Closetrack, drive->invistrack);
1942684b447eSDavid du Colombier if (vflag)
1943cdf9e71cSDavid du Colombier fprint(2, "... done.\n");
1944cdf9e71cSDavid du Colombier }
19457f1c8a1dSDavid du Colombier getinvistrack(drive); /* track # has probably changed */
19467dd7cddfSDavid du Colombier }
19477dd7cddfSDavid du Colombier
1948436f307dSDavid du Colombier /*
1949436f307dSDavid du Colombier * close the open track `o'.
1950436f307dSDavid du Colombier */
19517dd7cddfSDavid du Colombier static void
mmcclose(Otrack * o)19527dd7cddfSDavid du Colombier mmcclose(Otrack *o)
19537dd7cddfSDavid du Colombier {
19547dd7cddfSDavid du Colombier Mmcaux *aux;
195559cc4ca5SDavid du Colombier static uchar zero[2*BSmax];
19567dd7cddfSDavid du Colombier
19577dd7cddfSDavid du Colombier aux = o->drive->aux;
19587dd7cddfSDavid du Colombier if(o->omode == OREAD)
19597dd7cddfSDavid du Colombier aux->nropen--;
19607dd7cddfSDavid du Colombier else if(o->omode == OWRITE) {
19617dd7cddfSDavid du Colombier aux->nwopen--;
196259cc4ca5SDavid du Colombier mmcxwrite(o, zero, 2); /* write lead out */
19637dd7cddfSDavid du Colombier mmcsynccache(o->drive);
19647dd7cddfSDavid du Colombier o->drive->nchange = -1; /* force reread toc */
19657dd7cddfSDavid du Colombier }
19667dd7cddfSDavid du Colombier free(o);
19677dd7cddfSDavid du Colombier }
19687dd7cddfSDavid du Colombier
19698ad6bb6aSDavid du Colombier static int
setonesess(Drive * drive)1970079e489fSDavid du Colombier setonesess(Drive *drive)
19718ad6bb6aSDavid du Colombier {
19728ad6bb6aSDavid du Colombier uchar *p;
19738ad6bb6aSDavid du Colombier Mmcaux *aux;
19748ad6bb6aSDavid du Colombier
19758ad6bb6aSDavid du Colombier /* page 5 is legacy and now read-only; see MMC-6 §7.5.4.1 */
19768ad6bb6aSDavid du Colombier aux = drive->aux;
1977efd1a06fSDavid du Colombier if (!aux->page05ok)
1978efd1a06fSDavid du Colombier return -1;
19798ad6bb6aSDavid du Colombier p = aux->page05;
19800599ba09SDavid du Colombier p[Wptrkmode] &= ~Msbits;
19810599ba09SDavid du Colombier p[Wptrkmode] |= Msnonext;
1982079e489fSDavid du Colombier return mmcsetpage(drive, Pagwrparams, p);
19838ad6bb6aSDavid du Colombier }
19848ad6bb6aSDavid du Colombier
19858ad6bb6aSDavid du Colombier /*
1986436f307dSDavid du Colombier * close the current session, then finalize the disc.
19877dd7cddfSDavid du Colombier */
19887dd7cddfSDavid du Colombier static int
mmcfixate(Drive * drive)19897dd7cddfSDavid du Colombier mmcfixate(Drive *drive)
19907dd7cddfSDavid du Colombier {
1991436f307dSDavid du Colombier int r;
19927dd7cddfSDavid du Colombier
19937dd7cddfSDavid du Colombier if((drive->cap & Cwrite) == 0) {
19947dd7cddfSDavid du Colombier werrstr("not a writer");
19957dd7cddfSDavid du Colombier return -1;
19967dd7cddfSDavid du Colombier }
19977dd7cddfSDavid du Colombier drive->nchange = -1; /* force reread toc */
19987dd7cddfSDavid du Colombier
1999079e489fSDavid du Colombier setonesess(drive);
20007dd7cddfSDavid du Colombier
2001684b447eSDavid du Colombier /* skip explicit close session on bd-r */
200260ba15acSDavid du Colombier if (drive->mmctype != Mmcbd || drive->erasable == Yes) {
2003684b447eSDavid du Colombier if (vflag)
2004cdf9e71cSDavid du Colombier fprint(2, "closing session and maybe finalizing...\n");
2005436f307dSDavid du Colombier r = mmcxclose(drive, Closesessfinal, 0);
2006684b447eSDavid du Colombier if (vflag)
2007cdf9e71cSDavid du Colombier fprint(2, "... done.\n");
2008cdf9e71cSDavid du Colombier if (r < 0)
2009cdf9e71cSDavid du Colombier return r;
2010684b447eSDavid du Colombier }
2011436f307dSDavid du Colombier /*
2012436f307dSDavid du Colombier * Closesessfinal only closes & doesn't finalize on dvd+r and bd-r.
2013079e489fSDavid du Colombier * Closedvdrbdfinal closes & finalizes dvd+r and bd-r.
2014436f307dSDavid du Colombier */
2015cdf9e71cSDavid du Colombier if ((drive->mmctype == Mmcdvdplus || drive->mmctype == Mmcbd) &&
201660ba15acSDavid du Colombier drive->erasable == No) {
2017684b447eSDavid du Colombier if (vflag)
2018684b447eSDavid du Colombier fprint(2, "finalizing dvd+r or bd-r... "
2019684b447eSDavid du Colombier "(won't print `done').\n");
2020436f307dSDavid du Colombier return mmcxclose(drive, Closedvdrbdfinal, 0);
2021cdf9e71cSDavid du Colombier }
2022cdf9e71cSDavid du Colombier return 0;
20237dd7cddfSDavid du Colombier }
20247dd7cddfSDavid du Colombier
20257dd7cddfSDavid du Colombier static int
mmcblank(Drive * drive,int quick)20267dd7cddfSDavid du Colombier mmcblank(Drive *drive, int quick)
20277dd7cddfSDavid du Colombier {
20287dd7cddfSDavid du Colombier uchar cmd[12];
20297dd7cddfSDavid du Colombier
20307dd7cddfSDavid du Colombier drive->nchange = -1; /* force reread toc */
20317dd7cddfSDavid du Colombier
203260ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdBlank); /* blank cd-rw media */
2033cdf9e71cSDavid du Colombier /* immediate bit is 0x10 */
20347dd7cddfSDavid du Colombier /* cmd[1] = 0 means blank the whole disc; = 1 just the header */
20357dd7cddfSDavid du Colombier cmd[1] = quick ? 0x01 : 0x00;
20367dd7cddfSDavid du Colombier
20377dd7cddfSDavid du Colombier return scsi(drive, cmd, sizeof(cmd), cmd, 0, Snone);
20387dd7cddfSDavid du Colombier }
20397dd7cddfSDavid du Colombier
20407dd7cddfSDavid du Colombier static char*
e(int status)20417dd7cddfSDavid du Colombier e(int status)
20427dd7cddfSDavid du Colombier {
20437dd7cddfSDavid du Colombier if(status < 0)
20447dd7cddfSDavid du Colombier return geterrstr();
20457dd7cddfSDavid du Colombier return nil;
20467dd7cddfSDavid du Colombier }
20477dd7cddfSDavid du Colombier
20487dd7cddfSDavid du Colombier static char*
mmcctl(Drive * drive,int argc,char ** argv)20497dd7cddfSDavid du Colombier mmcctl(Drive *drive, int argc, char **argv)
20507dd7cddfSDavid du Colombier {
20518ad6bb6aSDavid du Colombier char *cmd;
20528ad6bb6aSDavid du Colombier
20537dd7cddfSDavid du Colombier if(argc < 1)
20547dd7cddfSDavid du Colombier return nil;
20558ad6bb6aSDavid du Colombier cmd = argv[0];
20568ad6bb6aSDavid du Colombier if(strcmp(cmd, "format") == 0)
2057c038c065SDavid du Colombier return e(format(drive));
20588ad6bb6aSDavid du Colombier if(strcmp(cmd, "blank") == 0)
20597dd7cddfSDavid du Colombier return e(mmcblank(drive, 0));
20608ad6bb6aSDavid du Colombier if(strcmp(cmd, "quickblank") == 0)
20617dd7cddfSDavid du Colombier return e(mmcblank(drive, 1));
20628ad6bb6aSDavid du Colombier if(strcmp(cmd, "eject") == 0)
20637dd7cddfSDavid du Colombier return e(start(drive, 2));
20648ad6bb6aSDavid du Colombier if(strcmp(cmd, "ingest") == 0)
20657dd7cddfSDavid du Colombier return e(start(drive, 3));
20667dd7cddfSDavid du Colombier return "bad arg";
20677dd7cddfSDavid du Colombier }
20687dd7cddfSDavid du Colombier
20699a747e4fSDavid du Colombier static char*
mmcsetspeed(Drive * drive,int r,int w)20709a747e4fSDavid du Colombier mmcsetspeed(Drive *drive, int r, int w)
20719a747e4fSDavid du Colombier {
20729a747e4fSDavid du Colombier char *rv;
20739a747e4fSDavid du Colombier uchar cmd[12];
20749a747e4fSDavid du Colombier
207560ba15acSDavid du Colombier initcdb(cmd, sizeof cmd, ScmdSetcdspeed);
20769a747e4fSDavid du Colombier cmd[2] = r>>8;
20779a747e4fSDavid du Colombier cmd[3] = r;
20789a747e4fSDavid du Colombier cmd[4] = w>>8;
20799a747e4fSDavid du Colombier cmd[5] = w;
20809a747e4fSDavid du Colombier rv = e(scsi(drive, cmd, sizeof(cmd), nil, 0, Snone));
20819a747e4fSDavid du Colombier mmcgetspeed(drive);
20829a747e4fSDavid du Colombier return rv;
20839a747e4fSDavid du Colombier }
20849a747e4fSDavid du Colombier
20857dd7cddfSDavid du Colombier static Dev mmcdev = {
20867dd7cddfSDavid du Colombier mmcopenrd,
20877dd7cddfSDavid du Colombier mmccreate,
20887dd7cddfSDavid du Colombier bufread,
20897dd7cddfSDavid du Colombier bufwrite,
20907dd7cddfSDavid du Colombier mmcclose,
20917dd7cddfSDavid du Colombier mmcgettoc,
20927dd7cddfSDavid du Colombier mmcfixate,
20937dd7cddfSDavid du Colombier mmcctl,
20949a747e4fSDavid du Colombier mmcsetspeed,
20957dd7cddfSDavid du Colombier };
2096