xref: /openbsd-src/usr.bin/cdio/mmc.c (revision 6e8568b90610b9556935b5a812695e4f664c6722)
1*6e8568b9Skrw /*	$OpenBSD: mmc.c,v 1.34 2022/10/10 15:04:09 krw Exp $	*/
23679886aSmjc /*
33679886aSmjc  * Copyright (c) 2006 Michael Coulter <mjc@openbsd.org>
43679886aSmjc  *
53679886aSmjc  * Permission to use, copy, modify, and distribute this software for any
63679886aSmjc  * purpose with or without fee is hereby granted, provided that the above
73679886aSmjc  * copyright notice and this permission notice appear in all copies.
83679886aSmjc  *
93679886aSmjc  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103679886aSmjc  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113679886aSmjc  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123679886aSmjc  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133679886aSmjc  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143679886aSmjc  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153679886aSmjc  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163679886aSmjc  */
173679886aSmjc 
181a0afcdeSderaadt #include <sys/types.h>
197993cdedSmjc #include <sys/limits.h>
205eb87a29Scheloha #include <sys/time.h>
217993cdedSmjc #include <sys/scsiio.h>
228979bd90Smjc #include <scsi/cd.h>
238979bd90Smjc #include <scsi/scsi_all.h>
248979bd90Smjc #include <scsi/scsi_disk.h>
257993cdedSmjc #include <err.h>
260cf923d6Smjc #include <errno.h>
277993cdedSmjc #include <fcntl.h>
287993cdedSmjc #include <stdio.h>
297993cdedSmjc #include <string.h>
305eb87a29Scheloha #include <time.h>
317993cdedSmjc #include <unistd.h>
327993cdedSmjc #include "extern.h"
337993cdedSmjc 
347993cdedSmjc extern int fd;
35dc4cd921Skrw extern u_int8_t mediacap[];
360cf923d6Smjc extern char *cdname;
37dc4cd921Skrw extern int verbose;
387993cdedSmjc 
39c7402b7cSav #define SCSI_GET_CONFIGURATION		0x46
40c7402b7cSav 
417299f7a2Sfgsch #define MMC_FEATURE_HDR_LEN		8
42c7402b7cSav 
43dc4cd921Skrw static const struct {
44dc4cd921Skrw 	u_int16_t id;
45dc4cd921Skrw 	char *name;
46dc4cd921Skrw } mmc_feature[] = {
47dc4cd921Skrw 	{ 0x0000, "Profile List" },
48dc4cd921Skrw 	{ 0x0001, "Core" },
49dc4cd921Skrw 	{ 0x0002, "Morphing" },
50dc4cd921Skrw 	{ 0x0003, "Removable Medium" },
51dc4cd921Skrw 	{ 0x0004, "Write Protect" },
52dc4cd921Skrw 	{ 0x0010, "Random Readable" },
53dc4cd921Skrw 	{ 0x001d, "Multi-Read" },
54dc4cd921Skrw 	{ 0x001e, "CD Read" },
55dc4cd921Skrw 	{ 0x001f, "DVD Read" },
56dc4cd921Skrw 	{ 0x0020, "Random Writable" },
57dc4cd921Skrw 	{ 0x0021, "Incremental Streaming Writable" },
58dc4cd921Skrw 	{ 0x0022, "Sector Erasable" },
59dc4cd921Skrw 	{ 0x0023, "Formattable" },
60dc4cd921Skrw 	{ 0x0024, "Hardware Defect Management" },
61dc4cd921Skrw 	{ 0x0025, "Write Once" },
62dc4cd921Skrw 	{ 0x0026, "Restricted Overwrite" },
63dc4cd921Skrw 	{ 0x0027, "CD-RW CAV Write" },
64dc4cd921Skrw 	{ 0x0028, "MRW" },
65dc4cd921Skrw 	{ 0x0029, "Enhanced Defect Reporting" },
66dc4cd921Skrw 	{ 0x002a, "DVD+RW" },
67dc4cd921Skrw 	{ 0x002b, "DVD+R" },
68dc4cd921Skrw 	{ 0x002c, "Rigid Restricted Overwrite" },
69dc4cd921Skrw 	{ 0x002d, "CD Track at Once (TAO)" },
70dc4cd921Skrw 	{ 0x002e, "CD Mastering (Session at Once)" },
71dc4cd921Skrw 	{ 0x002f, "DVD-RW Write" },
72dc4cd921Skrw 	{ 0x0030, "DDCD-ROM (Legacy)" },
73dc4cd921Skrw 	{ 0x0031, "DDCD-R (Legacy)" },
74dc4cd921Skrw 	{ 0x0032, "DDCD-RW (Legacy)" },
75dc4cd921Skrw 	{ 0x0033, "Layer Jump Recording" },
76dc4cd921Skrw 	{ 0x0037, "CD-RW Media Write Support" },
77dc4cd921Skrw 	{ 0x0038, "BD-R Pseudo-Overwrite (POW)" },
78dc4cd921Skrw 	{ 0x003a, "DVD+RW Dual Layer" },
79dc4cd921Skrw 	{ 0x003b, "DVD+R Dual Layer" },
80dc4cd921Skrw 	{ 0x0040, "BD Read" },
81dc4cd921Skrw 	{ 0x0041, "BD Write" },
82dc4cd921Skrw 	{ 0x0042, "Timely Safe Recording (TSR)" },
83dc4cd921Skrw 	{ 0x0050, "HD DVD Read" },
84dc4cd921Skrw 	{ 0x0051, "HD DVD Write" },
85dc4cd921Skrw 	{ 0x0080, "Hybrid Disc" },
86dc4cd921Skrw 	{ 0x0100, "Power Management" },
87dc4cd921Skrw 	{ 0x0101, "S.M.A.R.T." },
88dc4cd921Skrw 	{ 0x0102, "Embedded Changer" },
89dc4cd921Skrw 	{ 0x0103, "CD Audio External Play (Legacy)" },
90dc4cd921Skrw 	{ 0x0104, "Microcode Upgrade" },
91dc4cd921Skrw 	{ 0x0105, "Timeout" },
92dc4cd921Skrw 	{ 0x0106, "DVD CSS" },
93dc4cd921Skrw 	{ 0x0107, "Real Time Streaming" },
94dc4cd921Skrw 	{ 0x0108, "Drive Serial Number" },
95dc4cd921Skrw 	{ 0x0109, "Media Serial Number" },
96dc4cd921Skrw 	{ 0x010a, "Disc Control Blocks (DCBs)" },
97dc4cd921Skrw 	{ 0x010b, "DVD CPRM" },
98dc4cd921Skrw 	{ 0x010c, "Firmware Information" },
99dc4cd921Skrw 	{ 0x010d, "AACS" },
100dc4cd921Skrw 	{ 0x0110, "VCPS" },
101dc4cd921Skrw 	{ 0, NULL }
102dc4cd921Skrw };
103dc4cd921Skrw 
104dc4cd921Skrw static const struct {
105dc4cd921Skrw 	u_int16_t id;
106dc4cd921Skrw 	char *name;
107dc4cd921Skrw } mmc_profile[] = {
108dc4cd921Skrw 	{ 0x0001, "Re-writable disk, capable of changing behaviour" },
109dc4cd921Skrw 	{ 0x0002, "Re-writable, with removable media" },
110dc4cd921Skrw 	{ 0x0003, "Magneto-Optical disk with sector erase capability" },
111dc4cd921Skrw 	{ 0x0004, "Optical write once" },
112dc4cd921Skrw 	{ 0x0005, "Advance Storage -- Magneto-Optical" },
113dc4cd921Skrw 	{ 0x0008, "Read only Compact Disc" },
114dc4cd921Skrw 	{ 0x0009, "Write once Compact Disc" },
115dc4cd921Skrw 	{ 0x000a, "Re-writable Compact Disc" },
116dc4cd921Skrw 	{ 0x0010, "Read only DVD" },
117dc4cd921Skrw 	{ 0x0011, "Write once DVD using Sequential recording" },
118dc4cd921Skrw 	{ 0x0012, "Re-writable DVD" },
119dc4cd921Skrw 	{ 0x0013, "Re-recordable DVD using Restricted Overwrite" },
120dc4cd921Skrw 	{ 0x0014, "Re-recordable DVD using Sequential recording" },
121dc4cd921Skrw 	{ 0x0015, "Dual Layer DVD-R using Sequential recording" },
122dc4cd921Skrw 	{ 0x0016, "Dual Layer DVD-R using Layer Jump recording" },
123dc4cd921Skrw 	{ 0x001a, "DVD+ReWritable" },
124dc4cd921Skrw 	{ 0x001b, "DVD+Recordable" },
125dc4cd921Skrw 	{ 0x0020, "DDCD-ROM" },
126dc4cd921Skrw 	{ 0x0021, "DDCD-R" },
127dc4cd921Skrw 	{ 0x0022, "DDCD-RW" },
128dc4cd921Skrw 	{ 0x002a, "DVD+Rewritable Dual Layer" },
129dc4cd921Skrw 	{ 0x002b, "DVD+Recordable Dual Layer" },
130dc4cd921Skrw 	{ 0x003e, "Blu-ray Disc ROM" },
131dc4cd921Skrw 	{ 0x003f, "Blu-ray Disc Recordable -- Sequential Recording Mode" },
132dc4cd921Skrw 	{ 0x0040, "Blu-ray Disc Recordable -- Random Recording Mode" },
133dc4cd921Skrw 	{ 0x0041, "Blu-ray Disc Rewritable" },
134dc4cd921Skrw 	{ 0x004e, "Read-only HD DVD" },
135dc4cd921Skrw 	{ 0x004f, "Write-once HD DVD" },
136dc4cd921Skrw 	{ 0x0050, "Rewritable HD DVD" },
137dc4cd921Skrw 	{ 0, NULL }
138dc4cd921Skrw };
139dc4cd921Skrw 
140c7402b7cSav int
get_media_type(void)141f560e737Sav get_media_type(void)
142f560e737Sav {
143f560e737Sav 	scsireq_t scr;
144f560e737Sav 	char buf[32];
145f560e737Sav 	u_char disctype;
146f560e737Sav 	int rv, error;
147f560e737Sav 
148f560e737Sav 	rv = MEDIATYPE_UNKNOWN;
149f560e737Sav 	memset(buf, 0, sizeof(buf));
150f560e737Sav 	memset(&scr, 0, sizeof(scr));
151f560e737Sav 
152f560e737Sav 	scr.cmd[0] = READ_TOC;
153f560e737Sav 	scr.cmd[1] = 0x2;	/* MSF */
154f560e737Sav 	scr.cmd[2] = 0x4;	/* ATIP */
155f560e737Sav 	scr.cmd[8] = 0x20;
156f560e737Sav 
157f560e737Sav 	scr.flags = SCCMD_ESCAPE | SCCMD_READ;
158f560e737Sav 	scr.databuf = buf;
159f560e737Sav 	scr.datalen = sizeof(buf);
160f560e737Sav 	scr.cmdlen = 10;
161f560e737Sav 	scr.timeout = 120000;
162f560e737Sav 	scr.senselen = SENSEBUFLEN;
163f560e737Sav 
164f560e737Sav 	error = ioctl(fd, SCIOCCOMMAND, &scr);
165dc4cd921Skrw 	if (error != -1 && scr.retsts == SCCMD_OK && scr.datalen_used > 7) {
166f560e737Sav 		disctype = (buf[6] >> 6) & 0x1;
167f560e737Sav 		if (disctype == 0)
168f560e737Sav 			rv = MEDIATYPE_CDR;
169f560e737Sav 		else if (disctype == 1)
170f560e737Sav 			rv = MEDIATYPE_CDRW;
171f560e737Sav 	}
172f560e737Sav 
173f560e737Sav 	return (rv);
174f560e737Sav }
175f560e737Sav 
176f560e737Sav int
get_media_capabilities(u_int8_t * cap,int rt)177dc4cd921Skrw get_media_capabilities(u_int8_t *cap, int rt)
178c7402b7cSav {
179c7402b7cSav 	scsireq_t scr;
180fa3e22b8Sav 	u_char buf[4096];
1817299f7a2Sfgsch 	u_int32_t i, dlen;
182dc4cd921Skrw 	u_int16_t feature, profile, tmp;
1837299f7a2Sfgsch 	u_int8_t feature_len;
184dc4cd921Skrw 	int current, error, j, k;
185c7402b7cSav 
186dc4cd921Skrw 	memset(cap, 0, MMC_FEATURE_MAX / NBBY);
187c7402b7cSav 	memset(buf, 0, sizeof(buf));
188c7402b7cSav 	memset(&scr, 0, sizeof(scr));
189c7402b7cSav 
190c7402b7cSav 	scr.cmd[0] = SCSI_GET_CONFIGURATION;
1917299f7a2Sfgsch 	scr.cmd[1] = rt;
192475b0937Sclaudio 	tmp = htobe16(sizeof(buf));
193475b0937Sclaudio 	memcpy(scr.cmd + 7, &tmp, sizeof(u_int16_t));
194c7402b7cSav 
195c7402b7cSav 	scr.flags = SCCMD_ESCAPE | SCCMD_READ;
196c7402b7cSav 	scr.databuf = buf;
197c7402b7cSav 	scr.datalen = sizeof(buf);
198c7402b7cSav 	scr.cmdlen = 10;
199c7402b7cSav 	scr.timeout = 120000;
200c7402b7cSav 	scr.senselen = SENSEBUFLEN;
201c7402b7cSav 
202c7402b7cSav 	error = ioctl(fd, SCIOCCOMMAND, &scr);
203dc4cd921Skrw 	if (error == -1 || scr.retsts != SCCMD_OK)
204c7402b7cSav 		return (-1);
2057299f7a2Sfgsch 	if (scr.datalen_used < MMC_FEATURE_HDR_LEN)
2067299f7a2Sfgsch 		return (-1);	/* Can't get the header. */
207c7402b7cSav 
2087299f7a2Sfgsch 	/* Include the whole header in the length. */
2097299f7a2Sfgsch 	dlen = betoh32(*(u_int32_t *)buf) + 4;
2107299f7a2Sfgsch 	if (dlen > scr.datalen_used)
2117299f7a2Sfgsch 		dlen = scr.datalen_used;
212c7402b7cSav 
213dc4cd921Skrw 	if (verbose > 1)
214dc4cd921Skrw 		printf("Features:\n");
2157299f7a2Sfgsch 	for (i = MMC_FEATURE_HDR_LEN; i + 3 < dlen; i += feature_len) {
2167299f7a2Sfgsch 		feature_len = buf[i + 3] + 4;
2177299f7a2Sfgsch 		if (feature_len + i > dlen)
2187299f7a2Sfgsch 			break;
2197299f7a2Sfgsch 
220c7402b7cSav 		feature = betoh16(*(u_int16_t *)(buf + i));
2217299f7a2Sfgsch 		if (feature >= MMC_FEATURE_MAX)
2227299f7a2Sfgsch 			break;
223c7402b7cSav 
224dc4cd921Skrw 		if (verbose > 1) {
225dc4cd921Skrw 			printf("0x%04x", feature);
226dc4cd921Skrw 			for (j = 0; mmc_feature[j].name != NULL; j++)
227dc4cd921Skrw 				if (feature == mmc_feature[j].id)
228dc4cd921Skrw 					break;
229dc4cd921Skrw 			if (mmc_feature[j].name == NULL)
230dc4cd921Skrw 				printf(" <Undocumented>");
231dc4cd921Skrw 			else
232dc4cd921Skrw 				printf(" %s", mmc_feature[j].name);
233dc4cd921Skrw 			if (feature_len > 4)
234dc4cd921Skrw 				printf(" (%d bytes of data)", feature_len - 4);
235dc4cd921Skrw 			printf("\n");
236dc4cd921Skrw 			if (verbose > 2) {
237dc4cd921Skrw 				printf("    ");
238dc4cd921Skrw 				for (j = i; j < i + feature_len; j++) {
239dc4cd921Skrw 					printf("%02x", buf[j]);
240dc4cd921Skrw 					if ((j + 1) == (i + feature_len))
241dc4cd921Skrw 						printf("\n");
242dc4cd921Skrw 					else if ((j > i) && ((j - i + 1) % 16
243dc4cd921Skrw 					    == 0))
244dc4cd921Skrw 						printf("\n    ");
245dc4cd921Skrw 					else if ((j - i) == 3)
246dc4cd921Skrw 						printf("|");
247dc4cd921Skrw 					else
248dc4cd921Skrw 						printf(" ");
249dc4cd921Skrw 				}
250dc4cd921Skrw 			}
251dc4cd921Skrw 		}
252dc4cd921Skrw 		if (feature == 0 && verbose > 1) {
253dc4cd921Skrw 			if (verbose > 2)
254dc4cd921Skrw 				printf("    Profiles:\n");
255dc4cd921Skrw 			for (j = i + 4; j < i + feature_len; j += 4) {
256dc4cd921Skrw 				profile = betoh16(*(u_int16_t *)(buf+j));
257dc4cd921Skrw 				current = buf[j+2] == 1;
258dc4cd921Skrw 				if (verbose < 3 && !current)
259dc4cd921Skrw 					continue;
260dc4cd921Skrw 				if (current)
261dc4cd921Skrw 					printf("  * ");
262dc4cd921Skrw 				else
263dc4cd921Skrw 					printf("    ");
264dc4cd921Skrw 				printf("0x%04x", profile);
265dc4cd921Skrw 				for (k = 0; mmc_profile[k].name != NULL; k++)
266dc4cd921Skrw 					if (profile == mmc_profile[k].id)
267dc4cd921Skrw 						break;
268dc4cd921Skrw 				if (mmc_profile[k].name == NULL)
269dc4cd921Skrw 					printf(" <Undocumented>");
270dc4cd921Skrw 				else
271dc4cd921Skrw 					printf(" %s", mmc_profile[k].name);
272dc4cd921Skrw 				printf(" %s\n", current ? "[Current Profile]" :
273dc4cd921Skrw 				    "" );
274dc4cd921Skrw 			}
275dc4cd921Skrw 		}
2761a0afcdeSderaadt 		cdio_setbit(cap, feature);
277c7402b7cSav 	}
278c7402b7cSav 
279c7402b7cSav 	return (0);
280c7402b7cSav }
281c7402b7cSav 
2823af8cbcbSderaadt static int
set_speed(int wspeed)2837b34dc5fSav set_speed(int wspeed)
2847b34dc5fSav {
2857b34dc5fSav 	scsireq_t scr;
2867b34dc5fSav 	int r;
2877b34dc5fSav 
2887b34dc5fSav 	memset(&scr, 0, sizeof(scr));
2893ec8b627Sfgsch 	scr.cmd[0] = SET_CD_SPEED;
2901a0afcdeSderaadt 	scr.cmd[1] = (cdio_isset(mediacap, MMC_FEATURE_CDRW_CAV)) != 0;
2917b34dc5fSav 	*(u_int16_t *)(scr.cmd + 2) = htobe16(DRIVE_SPEED_OPTIMAL);
2927b34dc5fSav 	*(u_int16_t *)(scr.cmd + 4) = htobe16(wspeed);
2937b34dc5fSav 
2947b34dc5fSav 	scr.cmdlen = 12;
2957b34dc5fSav 	scr.datalen = 0;
2967b34dc5fSav 	scr.timeout = 120000;
2977b34dc5fSav 	scr.flags = SCCMD_ESCAPE;
2987b34dc5fSav 	scr.senselen = SENSEBUFLEN;
2997b34dc5fSav 
3007b34dc5fSav 	r = ioctl(fd, SCIOCCOMMAND, &scr);
3017b34dc5fSav 	return (r == 0 ? scr.retsts : -1);
3027b34dc5fSav }
3037b34dc5fSav 
3047b34dc5fSav int
blank(void)3057993cdedSmjc blank(void)
3067993cdedSmjc {
3078979bd90Smjc 	struct scsi_blank *scb;
3087993cdedSmjc 	scsireq_t scr;
3097993cdedSmjc 	int r;
3107993cdedSmjc 
3117993cdedSmjc 	bzero(&scr, sizeof(scr));
3128979bd90Smjc 	scb = (struct scsi_blank *)scr.cmd;
3138979bd90Smjc 	scb->opcode = BLANK;
3148979bd90Smjc 	scb->byte2 |= BLANK_MINIMAL;
3158979bd90Smjc 	scr.cmdlen = sizeof(*scb);
3167993cdedSmjc 	scr.datalen = 0;
3177993cdedSmjc 	scr.timeout = 120000;
3187993cdedSmjc 	scr.flags = SCCMD_ESCAPE;
3197993cdedSmjc 	scr.senselen = SENSEBUFLEN;
3207993cdedSmjc 
3217993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
3227993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
3237993cdedSmjc }
3247993cdedSmjc 
3257993cdedSmjc int
unit_ready(void)3267993cdedSmjc unit_ready(void)
3277993cdedSmjc {
3288979bd90Smjc 	struct scsi_test_unit_ready *scb;
3297993cdedSmjc 	scsireq_t scr;
3307993cdedSmjc 	int r;
3317993cdedSmjc 
3327993cdedSmjc 	bzero(&scr, sizeof(scr));
3338979bd90Smjc 	scb = (struct scsi_test_unit_ready *)scr.cmd;
3348979bd90Smjc 	scb->opcode = TEST_UNIT_READY;
3358979bd90Smjc 	scr.cmdlen = sizeof(*scb);
3367993cdedSmjc 	scr.datalen = 0;
3377993cdedSmjc 	scr.timeout = 120000;
3387993cdedSmjc 	scr.flags = SCCMD_ESCAPE;
3397993cdedSmjc 	scr.senselen = SENSEBUFLEN;
3407993cdedSmjc 
3417993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
3427993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
3437993cdedSmjc }
3447993cdedSmjc 
3457993cdedSmjc int
synchronize_cache(void)3467993cdedSmjc synchronize_cache(void)
3477993cdedSmjc {
3488979bd90Smjc 	struct scsi_synchronize_cache *scb;
3497993cdedSmjc 	scsireq_t scr;
3507993cdedSmjc 	int r;
3517993cdedSmjc 
3527993cdedSmjc 	bzero(&scr, sizeof(scr));
3538979bd90Smjc 	scb = (struct scsi_synchronize_cache *)scr.cmd;
3548979bd90Smjc 	scb->opcode = SYNCHRONIZE_CACHE;
3558979bd90Smjc 	scr.cmdlen = sizeof(*scb);
3567993cdedSmjc 	scr.datalen = 0;
3577993cdedSmjc 	scr.timeout = 120000;
3587993cdedSmjc 	scr.flags = SCCMD_ESCAPE;
3597993cdedSmjc 	scr.senselen = SENSEBUFLEN;
3607993cdedSmjc 
3617993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
3627993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
3637993cdedSmjc }
3647993cdedSmjc 
3657993cdedSmjc int
close_session(void)3667993cdedSmjc close_session(void)
3677993cdedSmjc {
3688979bd90Smjc 	struct scsi_close_track *scb;
3697993cdedSmjc 	scsireq_t scr;
3707993cdedSmjc 	int r;
3717993cdedSmjc 
3727993cdedSmjc 	bzero(&scr, sizeof(scr));
3738979bd90Smjc 	scb = (struct scsi_close_track *)scr.cmd;
3748979bd90Smjc 	scb->opcode = CLOSE_TRACK;
3758979bd90Smjc 	scb->closefunc = CT_CLOSE_SESS;
3768979bd90Smjc 	scr.cmdlen = sizeof(*scb);
3777993cdedSmjc 	scr.datalen = 0;
3787993cdedSmjc 	scr.timeout = 120000;
3797993cdedSmjc 	scr.flags = SCCMD_ESCAPE;
3807993cdedSmjc 	scr.senselen = SENSEBUFLEN;
3817993cdedSmjc 
3827993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
3837993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
3847993cdedSmjc }
3857993cdedSmjc 
3867993cdedSmjc int
writetao(struct track_head * thp)387cf22b6b5Smjc writetao(struct track_head *thp)
3887993cdedSmjc {
389618f50a1Sderaadt 	u_char modebuf[70], bdlen;
390cf22b6b5Smjc 	struct track_info *tr;
391618f50a1Sderaadt 	int r, track = 0;
3927993cdedSmjc 
3937993cdedSmjc 	if ((r = mode_sense_write(modebuf)) != SCCMD_OK) {
3947993cdedSmjc 		warnx("mode sense failed: %d", r);
3957993cdedSmjc 		return (r);
3967993cdedSmjc 	}
3977993cdedSmjc 	bdlen = modebuf[7];
3987993cdedSmjc 	modebuf[2+8+bdlen] |= 0x40; /* Buffer Underrun Free Enable */
3997993cdedSmjc 	modebuf[2+8+bdlen] |= 0x01; /* change write type to TAO */
4007993cdedSmjc 
401cf22b6b5Smjc 	SLIST_FOREACH(tr, thp, track_list) {
402618f50a1Sderaadt 		track++;
403cf22b6b5Smjc 		switch (tr->type) {
404cf22b6b5Smjc 		case 'd':
4057993cdedSmjc 			modebuf[3+8+bdlen] = 0x04; /* track mode = data */
4067993cdedSmjc 			modebuf[4+8+bdlen] = 0x08; /* 2048 block track mode */
4077993cdedSmjc 			modebuf[8+8+bdlen] = 0x00; /* turn off XA */
408cf22b6b5Smjc 			break;
409cf22b6b5Smjc 		case 'a':
4107993cdedSmjc 			modebuf[3+8+bdlen] = 0x00; /* track mode = audio */
4117993cdedSmjc 			modebuf[4+8+bdlen] = 0x00; /* 2352 block track mode */
4127993cdedSmjc 			modebuf[8+8+bdlen] = 0x00; /* turn off XA */
413cf22b6b5Smjc 			break;
414cf22b6b5Smjc 		default:
415*6e8568b9Skrw 			warnx("impossible tracktype detected");
416cf22b6b5Smjc 			break;
4177993cdedSmjc 		}
4187993cdedSmjc 		while (unit_ready() != SCCMD_OK)
4197993cdedSmjc 			continue;
4207993cdedSmjc 		if ((r = mode_select_write(modebuf)) != SCCMD_OK) {
4217993cdedSmjc 			warnx("mode select failed: %d", r);
4227993cdedSmjc 			return (r);
4237993cdedSmjc 		}
4247b34dc5fSav 
4257b34dc5fSav 		set_speed(tr->speed);
426618f50a1Sderaadt 		writetrack(tr, track);
4277993cdedSmjc 		synchronize_cache();
4287993cdedSmjc 	}
4297c204c3eSmjc 	fprintf(stderr, "Closing session.\n");
4307993cdedSmjc 	close_session();
4317993cdedSmjc 	return (0);
4327993cdedSmjc }
4337993cdedSmjc 
4347993cdedSmjc int
writetrack(struct track_info * tr,int track)435618f50a1Sderaadt writetrack(struct track_info *tr, int track)
4367993cdedSmjc {
4375eb87a29Scheloha 	struct timespec ts, ots, ats;
438618f50a1Sderaadt 	u_char databuf[65536], nblk;
439618f50a1Sderaadt 	u_int end_lba, lba, tmp;
4407993cdedSmjc 	scsireq_t scr;
441e6deffc0Smjc 	int r;
4427993cdedSmjc 
443cf22b6b5Smjc 	nblk = 65535/tr->blklen;
4447993cdedSmjc 	bzero(&scr, sizeof(scr));
4457993cdedSmjc 	scr.timeout = 300000;
446b9a0c77eSkrw 	scr.cmd[0] = WRITE_10;
4477993cdedSmjc 	scr.cmd[1] = 0x00;
4487993cdedSmjc 	scr.cmd[8] = nblk; /* Transfer length in blocks (LSB) */
4497993cdedSmjc 	scr.cmdlen = 10;
4507993cdedSmjc 	scr.databuf = (caddr_t)databuf;
451cf22b6b5Smjc 	scr.datalen = nblk * tr->blklen;
4527993cdedSmjc 	scr.senselen = SENSEBUFLEN;
4537993cdedSmjc 	scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
4547993cdedSmjc 
4555eb87a29Scheloha 	timespecclear(&ots);
4565eb87a29Scheloha 	ats.tv_sec = 1;
4575eb87a29Scheloha 	ats.tv_nsec = 0;
458618f50a1Sderaadt 
4597993cdedSmjc 	if (get_nwa(&lba) != SCCMD_OK) {
4607993cdedSmjc 		warnx("cannot get next writable address");
4617993cdedSmjc 		return (-1);
4627993cdedSmjc 	}
4637993cdedSmjc 	tmp = htobe32(lba); /* update lba in cdb */
4647993cdedSmjc 	memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
4657993cdedSmjc 
466cf22b6b5Smjc 	if (tr->sz / tr->blklen + 1 > UINT_MAX || tr->sz < tr->blklen) {
467cf22b6b5Smjc 		warnx("file %s has invalid size", tr->file);
4687993cdedSmjc 		return (-1);
4697993cdedSmjc 	}
470cf22b6b5Smjc 	if (tr->sz % tr->blklen) {
471765d0372Sderaadt 		warnx("file %s is not multiple of block length %d",
472765d0372Sderaadt 		    tr->file, tr->blklen);
473cf22b6b5Smjc 		end_lba = tr->sz / tr->blklen + lba + 1;
4747993cdedSmjc 	} else {
475cf22b6b5Smjc 		end_lba = tr->sz / tr->blklen + lba;
4767993cdedSmjc 	}
477f7dfecd4Sav 	if (lseek(tr->fd, tr->off, SEEK_SET) == -1)
478e6deffc0Smjc 		err(1, "seek failed for file %s", tr->file);
4792220c7d5Sderaadt 	while (lba < end_lba && nblk != 0) {
4807993cdedSmjc 		while (lba + nblk <= end_lba) {
481e6deffc0Smjc 			read(tr->fd, databuf, nblk * tr->blklen);
4827993cdedSmjc 			scr.cmd[8] = nblk;
483cf22b6b5Smjc 			scr.datalen = nblk * tr->blklen;
48463d334f7Smjc again:
4857993cdedSmjc 			r = ioctl(fd, SCIOCCOMMAND, &scr);
4867993cdedSmjc 			if (r != 0) {
4874aec353bSderaadt 				printf("\r%60s", "");
4887993cdedSmjc 				warn("ioctl failed while attempting to write");
4897993cdedSmjc 				return (-1);
4907993cdedSmjc 			}
49163d334f7Smjc 			if (scr.retsts == SCCMD_SENSE && scr.sense[2] == 0x2) {
49263d334f7Smjc 				usleep(1000);
49363d334f7Smjc 				goto again;
49463d334f7Smjc 			}
4957993cdedSmjc 			if (scr.retsts != SCCMD_OK) {
4964aec353bSderaadt 				printf("\r%60s", "");
497765d0372Sderaadt 				warnx("ioctl returned bad status while "
498765d0372Sderaadt 				    "attempting to write: %d",
499765d0372Sderaadt 				    scr.retsts);
5007993cdedSmjc 				return (r);
5017993cdedSmjc 			}
5027993cdedSmjc 			lba += nblk;
503618f50a1Sderaadt 
5045eb87a29Scheloha 			clock_gettime(CLOCK_MONOTONIC, &ts);
5055eb87a29Scheloha 			if (lba == end_lba || timespeccmp(&ts, &ots, >)) {
506618f50a1Sderaadt 				fprintf(stderr,
5074aec353bSderaadt 				    "\rtrack %02d '%c' %08u/%08u %3d%%",
508618f50a1Sderaadt 				    track, tr->type,
509618f50a1Sderaadt 				    lba, end_lba, 100 * lba / end_lba);
5105eb87a29Scheloha 				timespecadd(&ts, &ats, &ots);
511618f50a1Sderaadt 			}
5127993cdedSmjc 			tmp = htobe32(lba); /* update lba in cdb */
5137993cdedSmjc 			memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
5147993cdedSmjc 		}
5157993cdedSmjc 		nblk--;
5167993cdedSmjc 	}
5172220c7d5Sderaadt 	printf("\n");
518e6deffc0Smjc 	close(tr->fd);
5197993cdedSmjc 	return (0);
5207993cdedSmjc }
5217993cdedSmjc 
5227993cdedSmjc int
mode_sense_write(unsigned char buf[])5237993cdedSmjc mode_sense_write(unsigned char buf[])
5247993cdedSmjc {
5258979bd90Smjc 	struct scsi_mode_sense_big *scb;
5267993cdedSmjc 	scsireq_t scr;
5277993cdedSmjc 	int r;
5287993cdedSmjc 
5297993cdedSmjc 	bzero(&scr, sizeof(scr));
5308979bd90Smjc 	scb = (struct scsi_mode_sense_big *)scr.cmd;
5318979bd90Smjc 	scb->opcode = MODE_SENSE_BIG;
5328979bd90Smjc 	/* XXX: need to set disable block descriptors and check SCSI drive */
5338979bd90Smjc 	scb->page = WRITE_PARAM_PAGE;
5348979bd90Smjc 	scb->length[1] = 0x46; /* 16 for the header + size from pg. 89 mmc-r10a.pdf */
5358979bd90Smjc 	scr.cmdlen = sizeof(*scb);
5367993cdedSmjc 	scr.timeout = 4000;
5377993cdedSmjc 	scr.senselen = SENSEBUFLEN;
5387993cdedSmjc 	scr.datalen= 0x46;
5397993cdedSmjc 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
5407993cdedSmjc 	scr.databuf = (caddr_t)buf;
5417993cdedSmjc 
5427993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
5437993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
5447993cdedSmjc }
5457993cdedSmjc 
5467993cdedSmjc int
mode_select_write(unsigned char buf[])5477993cdedSmjc mode_select_write(unsigned char buf[])
5487993cdedSmjc {
5498979bd90Smjc 	struct scsi_mode_select_big *scb;
5507993cdedSmjc 	scsireq_t scr;
5517993cdedSmjc 	int r;
5527993cdedSmjc 
5537993cdedSmjc 	bzero(&scr, sizeof(scr));
5548979bd90Smjc 	scb = (struct scsi_mode_select_big *)scr.cmd;
5558979bd90Smjc 	scb->opcode = MODE_SELECT_BIG;
556741934beSderaadt 
5578979bd90Smjc 	/*
5588979bd90Smjc 	 * INF-8020 says bit 4 in byte 2 is '1'
5598979bd90Smjc 	 * INF-8090 refers to it as 'PF(1)' then doesn't
5608979bd90Smjc 	 * describe it.
5618979bd90Smjc 	 */
5628979bd90Smjc 	scb->byte2 = 0x10;
5638979bd90Smjc 	scb->length[1] = 2 + buf[1] + 256 * buf[0];
5647993cdedSmjc 	scr.timeout = 4000;
5657993cdedSmjc 	scr.senselen = SENSEBUFLEN;
5668979bd90Smjc 	scr.cmdlen = sizeof(*scb);
5677993cdedSmjc 	scr.datalen = 2 + buf[1] + 256 * buf[0];
5687993cdedSmjc 	scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
5697993cdedSmjc 	scr.databuf = (caddr_t)buf;
5707993cdedSmjc 
5717993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
5727993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
5737993cdedSmjc }
5747993cdedSmjc 
5757993cdedSmjc int
get_disc_size(off_t * availblk)576dca2c108Smjc get_disc_size(off_t *availblk)
577dca2c108Smjc {
578dca2c108Smjc 	u_char databuf[28];
5798979bd90Smjc 	struct scsi_read_track_info *scb;
580dca2c108Smjc 	scsireq_t scr;
581dca2c108Smjc 	int r, tmp;
582dca2c108Smjc 
583dca2c108Smjc 	bzero(&scr, sizeof(scr));
5848979bd90Smjc 	scb = (struct scsi_read_track_info *)scr.cmd;
585dca2c108Smjc 	scr.timeout = 4000;
586dca2c108Smjc 	scr.senselen = SENSEBUFLEN;
5878979bd90Smjc 	scb->opcode = READ_TRACK_INFO;
5888979bd90Smjc 	scb->addrtype = RTI_TRACK;
589752e29b5Smjc 	scb->addr[3] = 1;
590752e29b5Smjc 	scb->data_len[1] = 0x1c;
5918979bd90Smjc 	scr.cmdlen = sizeof(*scb);
592dca2c108Smjc 	scr.datalen= 0x1c;
593dca2c108Smjc 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
594dca2c108Smjc 	scr.databuf = (caddr_t)databuf;
595dca2c108Smjc 
596dca2c108Smjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
597dca2c108Smjc 	memcpy(&tmp, &databuf[16], sizeof(tmp));
598dca2c108Smjc 	*availblk = betoh32(tmp);
599dca2c108Smjc 	return (r == 0 ? scr.retsts : -1);
600dca2c108Smjc }
601dca2c108Smjc 
602dca2c108Smjc int
get_nwa(int * nwa)6037993cdedSmjc get_nwa(int *nwa)
6047993cdedSmjc {
6057993cdedSmjc 	u_char databuf[28];
6067993cdedSmjc 	scsireq_t scr;
6077993cdedSmjc 	int r, tmp;
6087993cdedSmjc 
6097993cdedSmjc 	bzero(&scr, sizeof(scr));
6107993cdedSmjc 	scr.timeout = 4000;
6117993cdedSmjc 	scr.senselen = SENSEBUFLEN;
6123ec8b627Sfgsch 	scr.cmd[0] = READ_TRACK_INFO;
6137993cdedSmjc 	scr.cmd[1] = 0x01;
6147993cdedSmjc 	scr.cmd[5] = 0xff; /* Invisible Track */
6157993cdedSmjc 	scr.cmd[7] = 0x00;
6167993cdedSmjc 	scr.cmd[8] = 0x1c;
6177993cdedSmjc 	scr.cmdlen = 10;
6187993cdedSmjc 	scr.datalen= 0x1c;
6197993cdedSmjc 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
6207993cdedSmjc 	scr.databuf = (caddr_t)databuf;
6217993cdedSmjc 
6227993cdedSmjc 	r = ioctl(fd, SCIOCCOMMAND, &scr);
6237993cdedSmjc 	memcpy(&tmp, &databuf[12], sizeof(tmp));
6247993cdedSmjc 	*nwa = betoh32(tmp);
6257993cdedSmjc 	return (r == 0 ? scr.retsts : -1);
6267993cdedSmjc }
627