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