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