xref: /openbsd/usr.bin/cdio/mmc.c (revision 905646f0)
1 /*	$OpenBSD: mmc.c,v 1.32 2020/09/01 17:20:02 krw Exp $	*/
2 /*
3  * Copyright (c) 2006 Michael Coulter <mjc@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/limits.h>
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <sys/scsiio.h>
22 #include <sys/param.h>	/* setbit, isset */
23 #include <scsi/cd.h>
24 #include <scsi/scsi_all.h>
25 #include <scsi/scsi_disk.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 #include "extern.h"
34 
35 extern int fd;
36 extern u_int8_t mediacap[];
37 extern char *cdname;
38 extern int verbose;
39 
40 #define SCSI_GET_CONFIGURATION		0x46
41 
42 #define MMC_FEATURE_HDR_LEN		8
43 
44 static const struct {
45 	u_int16_t id;
46 	char *name;
47 } mmc_feature[] = {
48 	{ 0x0000, "Profile List" },
49 	{ 0x0001, "Core" },
50 	{ 0x0002, "Morphing" },
51 	{ 0x0003, "Removable Medium" },
52 	{ 0x0004, "Write Protect" },
53 	{ 0x0010, "Random Readable" },
54 	{ 0x001d, "Multi-Read" },
55 	{ 0x001e, "CD Read" },
56 	{ 0x001f, "DVD Read" },
57 	{ 0x0020, "Random Writable" },
58 	{ 0x0021, "Incremental Streaming Writable" },
59 	{ 0x0022, "Sector Erasable" },
60 	{ 0x0023, "Formattable" },
61 	{ 0x0024, "Hardware Defect Management" },
62 	{ 0x0025, "Write Once" },
63 	{ 0x0026, "Restricted Overwrite" },
64 	{ 0x0027, "CD-RW CAV Write" },
65 	{ 0x0028, "MRW" },
66 	{ 0x0029, "Enhanced Defect Reporting" },
67 	{ 0x002a, "DVD+RW" },
68 	{ 0x002b, "DVD+R" },
69 	{ 0x002c, "Rigid Restricted Overwrite" },
70 	{ 0x002d, "CD Track at Once (TAO)" },
71 	{ 0x002e, "CD Mastering (Session at Once)" },
72 	{ 0x002f, "DVD-RW Write" },
73 	{ 0x0030, "DDCD-ROM (Legacy)" },
74 	{ 0x0031, "DDCD-R (Legacy)" },
75 	{ 0x0032, "DDCD-RW (Legacy)" },
76 	{ 0x0033, "Layer Jump Recording" },
77 	{ 0x0037, "CD-RW Media Write Support" },
78 	{ 0x0038, "BD-R Pseudo-Overwrite (POW)" },
79 	{ 0x003a, "DVD+RW Dual Layer" },
80 	{ 0x003b, "DVD+R Dual Layer" },
81 	{ 0x0040, "BD Read" },
82 	{ 0x0041, "BD Write" },
83 	{ 0x0042, "Timely Safe Recording (TSR)" },
84 	{ 0x0050, "HD DVD Read" },
85 	{ 0x0051, "HD DVD Write" },
86 	{ 0x0080, "Hybrid Disc" },
87 	{ 0x0100, "Power Management" },
88 	{ 0x0101, "S.M.A.R.T." },
89 	{ 0x0102, "Embedded Changer" },
90 	{ 0x0103, "CD Audio External Play (Legacy)" },
91 	{ 0x0104, "Microcode Upgrade" },
92 	{ 0x0105, "Timeout" },
93 	{ 0x0106, "DVD CSS" },
94 	{ 0x0107, "Real Time Streaming" },
95 	{ 0x0108, "Drive Serial Number" },
96 	{ 0x0109, "Media Serial Number" },
97 	{ 0x010a, "Disc Control Blocks (DCBs)" },
98 	{ 0x010b, "DVD CPRM" },
99 	{ 0x010c, "Firmware Information" },
100 	{ 0x010d, "AACS" },
101 	{ 0x0110, "VCPS" },
102 	{ 0, NULL }
103 };
104 
105 static const struct {
106 	u_int16_t id;
107 	char *name;
108 } mmc_profile[] = {
109 	{ 0x0001, "Re-writable disk, capable of changing behaviour" },
110 	{ 0x0002, "Re-writable, with removable media" },
111 	{ 0x0003, "Magneto-Optical disk with sector erase capability" },
112 	{ 0x0004, "Optical write once" },
113 	{ 0x0005, "Advance Storage -- Magneto-Optical" },
114 	{ 0x0008, "Read only Compact Disc" },
115 	{ 0x0009, "Write once Compact Disc" },
116 	{ 0x000a, "Re-writable Compact Disc" },
117 	{ 0x0010, "Read only DVD" },
118 	{ 0x0011, "Write once DVD using Sequential recording" },
119 	{ 0x0012, "Re-writable DVD" },
120 	{ 0x0013, "Re-recordable DVD using Restricted Overwrite" },
121 	{ 0x0014, "Re-recordable DVD using Sequential recording" },
122 	{ 0x0015, "Dual Layer DVD-R using Sequential recording" },
123 	{ 0x0016, "Dual Layer DVD-R using Layer Jump recording" },
124 	{ 0x001a, "DVD+ReWritable" },
125 	{ 0x001b, "DVD+Recordable" },
126 	{ 0x0020, "DDCD-ROM" },
127 	{ 0x0021, "DDCD-R" },
128 	{ 0x0022, "DDCD-RW" },
129 	{ 0x002a, "DVD+Rewritable Dual Layer" },
130 	{ 0x002b, "DVD+Recordable Dual Layer" },
131 	{ 0x003e, "Blu-ray Disc ROM" },
132 	{ 0x003f, "Blu-ray Disc Recordable -- Sequential Recording Mode" },
133 	{ 0x0040, "Blu-ray Disc Recordable -- Random Recording Mode" },
134 	{ 0x0041, "Blu-ray Disc Rewritable" },
135 	{ 0x004e, "Read-only HD DVD" },
136 	{ 0x004f, "Write-once HD DVD" },
137 	{ 0x0050, "Rewritable HD DVD" },
138 	{ 0, NULL }
139 };
140 
141 int
142 get_media_type(void)
143 {
144 	scsireq_t scr;
145 	char buf[32];
146 	u_char disctype;
147 	int rv, error;
148 
149 	rv = MEDIATYPE_UNKNOWN;
150 	memset(buf, 0, sizeof(buf));
151 	memset(&scr, 0, sizeof(scr));
152 
153 	scr.cmd[0] = READ_TOC;
154 	scr.cmd[1] = 0x2;	/* MSF */
155 	scr.cmd[2] = 0x4;	/* ATIP */
156 	scr.cmd[8] = 0x20;
157 
158 	scr.flags = SCCMD_ESCAPE | SCCMD_READ;
159 	scr.databuf = buf;
160 	scr.datalen = sizeof(buf);
161 	scr.cmdlen = 10;
162 	scr.timeout = 120000;
163 	scr.senselen = SENSEBUFLEN;
164 
165 	error = ioctl(fd, SCIOCCOMMAND, &scr);
166 	if (error != -1 && scr.retsts == SCCMD_OK && scr.datalen_used > 7) {
167 		disctype = (buf[6] >> 6) & 0x1;
168 		if (disctype == 0)
169 			rv = MEDIATYPE_CDR;
170 		else if (disctype == 1)
171 			rv = MEDIATYPE_CDRW;
172 	}
173 
174 	return (rv);
175 }
176 
177 int
178 get_media_capabilities(u_int8_t *cap, int rt)
179 {
180 	scsireq_t scr;
181 	u_char buf[4096];
182 	u_int32_t i, dlen;
183 	u_int16_t feature, profile, tmp;
184 	u_int8_t feature_len;
185 	int current, error, j, k;
186 
187 	memset(cap, 0, MMC_FEATURE_MAX / NBBY);
188 	memset(buf, 0, sizeof(buf));
189 	memset(&scr, 0, sizeof(scr));
190 
191 	scr.cmd[0] = SCSI_GET_CONFIGURATION;
192 	scr.cmd[1] = rt;
193 	tmp = htobe16(sizeof(buf));
194 	memcpy(scr.cmd + 7, &tmp, sizeof(u_int16_t));
195 
196 	scr.flags = SCCMD_ESCAPE | SCCMD_READ;
197 	scr.databuf = buf;
198 	scr.datalen = sizeof(buf);
199 	scr.cmdlen = 10;
200 	scr.timeout = 120000;
201 	scr.senselen = SENSEBUFLEN;
202 
203 	error = ioctl(fd, SCIOCCOMMAND, &scr);
204 	if (error == -1 || scr.retsts != SCCMD_OK)
205 		return (-1);
206 	if (scr.datalen_used < MMC_FEATURE_HDR_LEN)
207 		return (-1);	/* Can't get the header. */
208 
209 	/* Include the whole header in the length. */
210 	dlen = betoh32(*(u_int32_t *)buf) + 4;
211 	if (dlen > scr.datalen_used)
212 		dlen = scr.datalen_used;
213 
214 	if (verbose > 1)
215 		printf("Features:\n");
216 	for (i = MMC_FEATURE_HDR_LEN; i + 3 < dlen; i += feature_len) {
217 		feature_len = buf[i + 3] + 4;
218 		if (feature_len + i > dlen)
219 			break;
220 
221 		feature = betoh16(*(u_int16_t *)(buf + i));
222 		if (feature >= MMC_FEATURE_MAX)
223 			break;
224 
225 		if (verbose > 1) {
226 			printf("0x%04x", feature);
227 			for (j = 0; mmc_feature[j].name != NULL; j++)
228 				if (feature == mmc_feature[j].id)
229 					break;
230 			if (mmc_feature[j].name == NULL)
231 				printf(" <Undocumented>");
232 			else
233 				printf(" %s", mmc_feature[j].name);
234 			if (feature_len > 4)
235 				printf(" (%d bytes of data)", feature_len - 4);
236 			printf("\n");
237 			if (verbose > 2) {
238 				printf("    ");
239 				for (j = i; j < i + feature_len; j++) {
240 					printf("%02x", buf[j]);
241 					if ((j + 1) == (i + feature_len))
242 						printf("\n");
243 					else if ((j > i) && ((j - i + 1) % 16
244 					    == 0))
245 						printf("\n    ");
246 					else if ((j - i) == 3)
247 						printf("|");
248 					else
249 						printf(" ");
250 				}
251 			}
252 		}
253 		if (feature == 0 && verbose > 1) {
254 			if (verbose > 2)
255 				printf("    Profiles:\n");
256 			for (j = i + 4; j < i + feature_len; j += 4) {
257 				profile = betoh16(*(u_int16_t *)(buf+j));
258 				current = buf[j+2] == 1;
259 				if (verbose < 3 && !current)
260 					continue;
261 				if (current)
262 					printf("  * ");
263 				else
264 					printf("    ");
265 				printf("0x%04x", profile);
266 				for (k = 0; mmc_profile[k].name != NULL; k++)
267 					if (profile == mmc_profile[k].id)
268 						break;
269 				if (mmc_profile[k].name == NULL)
270 					printf(" <Undocumented>");
271 				else
272 					printf(" %s", mmc_profile[k].name);
273 				printf(" %s\n", current ? "[Current Profile]" :
274 				    "" );
275 			}
276 		}
277 		setbit(cap, feature);
278 	}
279 
280 	return (0);
281 }
282 
283 static int
284 set_speed(int wspeed)
285 {
286 	scsireq_t scr;
287 	int r;
288 
289 	memset(&scr, 0, sizeof(scr));
290 	scr.cmd[0] = SET_CD_SPEED;
291 	scr.cmd[1] = (isset(mediacap, MMC_FEATURE_CDRW_CAV)) != 0;
292 	*(u_int16_t *)(scr.cmd + 2) = htobe16(DRIVE_SPEED_OPTIMAL);
293 	*(u_int16_t *)(scr.cmd + 4) = htobe16(wspeed);
294 
295 	scr.cmdlen = 12;
296 	scr.datalen = 0;
297 	scr.timeout = 120000;
298 	scr.flags = SCCMD_ESCAPE;
299 	scr.senselen = SENSEBUFLEN;
300 
301 	r = ioctl(fd, SCIOCCOMMAND, &scr);
302 	return (r == 0 ? scr.retsts : -1);
303 }
304 
305 int
306 blank(void)
307 {
308 	struct scsi_blank *scb;
309 	scsireq_t scr;
310 	int r;
311 
312 	bzero(&scr, sizeof(scr));
313 	scb = (struct scsi_blank *)scr.cmd;
314 	scb->opcode = BLANK;
315 	scb->byte2 |= BLANK_MINIMAL;
316 	scr.cmdlen = sizeof(*scb);
317 	scr.datalen = 0;
318 	scr.timeout = 120000;
319 	scr.flags = SCCMD_ESCAPE;
320 	scr.senselen = SENSEBUFLEN;
321 
322 	r = ioctl(fd, SCIOCCOMMAND, &scr);
323 	return (r == 0 ? scr.retsts : -1);
324 }
325 
326 int
327 unit_ready(void)
328 {
329 	struct scsi_test_unit_ready *scb;
330 	scsireq_t scr;
331 	int r;
332 
333 	bzero(&scr, sizeof(scr));
334 	scb = (struct scsi_test_unit_ready *)scr.cmd;
335 	scb->opcode = TEST_UNIT_READY;
336 	scr.cmdlen = sizeof(*scb);
337 	scr.datalen = 0;
338 	scr.timeout = 120000;
339 	scr.flags = SCCMD_ESCAPE;
340 	scr.senselen = SENSEBUFLEN;
341 
342 	r = ioctl(fd, SCIOCCOMMAND, &scr);
343 	return (r == 0 ? scr.retsts : -1);
344 }
345 
346 int
347 synchronize_cache(void)
348 {
349 	struct scsi_synchronize_cache *scb;
350 	scsireq_t scr;
351 	int r;
352 
353 	bzero(&scr, sizeof(scr));
354 	scb = (struct scsi_synchronize_cache *)scr.cmd;
355 	scb->opcode = SYNCHRONIZE_CACHE;
356 	scr.cmdlen = sizeof(*scb);
357 	scr.datalen = 0;
358 	scr.timeout = 120000;
359 	scr.flags = SCCMD_ESCAPE;
360 	scr.senselen = SENSEBUFLEN;
361 
362 	r = ioctl(fd, SCIOCCOMMAND, &scr);
363 	return (r == 0 ? scr.retsts : -1);
364 }
365 
366 int
367 close_session(void)
368 {
369 	struct scsi_close_track *scb;
370 	scsireq_t scr;
371 	int r;
372 
373 	bzero(&scr, sizeof(scr));
374 	scb = (struct scsi_close_track *)scr.cmd;
375 	scb->opcode = CLOSE_TRACK;
376 	scb->closefunc = CT_CLOSE_SESS;
377 	scr.cmdlen = sizeof(*scb);
378 	scr.datalen = 0;
379 	scr.timeout = 120000;
380 	scr.flags = SCCMD_ESCAPE;
381 	scr.senselen = SENSEBUFLEN;
382 
383 	r = ioctl(fd, SCIOCCOMMAND, &scr);
384 	return (r == 0 ? scr.retsts : -1);
385 }
386 
387 int
388 writetao(struct track_head *thp)
389 {
390 	u_char modebuf[70], bdlen;
391 	struct track_info *tr;
392 	int r, track = 0;
393 
394 	if ((r = mode_sense_write(modebuf)) != SCCMD_OK) {
395 		warnx("mode sense failed: %d", r);
396 		return (r);
397 	}
398 	bdlen = modebuf[7];
399 	modebuf[2+8+bdlen] |= 0x40; /* Buffer Underrun Free Enable */
400 	modebuf[2+8+bdlen] |= 0x01; /* change write type to TAO */
401 
402 	SLIST_FOREACH(tr, thp, track_list) {
403 		track++;
404 		switch (tr->type) {
405 		case 'd':
406 			modebuf[3+8+bdlen] = 0x04; /* track mode = data */
407 			modebuf[4+8+bdlen] = 0x08; /* 2048 block track mode */
408 			modebuf[8+8+bdlen] = 0x00; /* turn off XA */
409 			break;
410 		case 'a':
411 			modebuf[3+8+bdlen] = 0x00; /* track mode = audio */
412 			modebuf[4+8+bdlen] = 0x00; /* 2352 block track mode */
413 			modebuf[8+8+bdlen] = 0x00; /* turn off XA */
414 			break;
415 		default:
416 			warn("impossible tracktype detected");
417 			break;
418 		}
419 		while (unit_ready() != SCCMD_OK)
420 			continue;
421 		if ((r = mode_select_write(modebuf)) != SCCMD_OK) {
422 			warnx("mode select failed: %d", r);
423 			return (r);
424 		}
425 
426 		set_speed(tr->speed);
427 		writetrack(tr, track);
428 		synchronize_cache();
429 	}
430 	fprintf(stderr, "Closing session.\n");
431 	close_session();
432 	return (0);
433 }
434 
435 int
436 writetrack(struct track_info *tr, int track)
437 {
438 	struct timespec ts, ots, ats;
439 	u_char databuf[65536], nblk;
440 	u_int end_lba, lba, tmp;
441 	scsireq_t scr;
442 	int r;
443 
444 	nblk = 65535/tr->blklen;
445 	bzero(&scr, sizeof(scr));
446 	scr.timeout = 300000;
447 	scr.cmd[0] = WRITE_10;
448 	scr.cmd[1] = 0x00;
449 	scr.cmd[8] = nblk; /* Transfer length in blocks (LSB) */
450 	scr.cmdlen = 10;
451 	scr.databuf = (caddr_t)databuf;
452 	scr.datalen = nblk * tr->blklen;
453 	scr.senselen = SENSEBUFLEN;
454 	scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
455 
456 	timespecclear(&ots);
457 	ats.tv_sec = 1;
458 	ats.tv_nsec = 0;
459 
460 	if (get_nwa(&lba) != SCCMD_OK) {
461 		warnx("cannot get next writable address");
462 		return (-1);
463 	}
464 	tmp = htobe32(lba); /* update lba in cdb */
465 	memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
466 
467 	if (tr->sz / tr->blklen + 1 > UINT_MAX || tr->sz < tr->blklen) {
468 		warnx("file %s has invalid size", tr->file);
469 		return (-1);
470 	}
471 	if (tr->sz % tr->blklen) {
472 		warnx("file %s is not multiple of block length %d",
473 		    tr->file, tr->blklen);
474 		end_lba = tr->sz / tr->blklen + lba + 1;
475 	} else {
476 		end_lba = tr->sz / tr->blklen + lba;
477 	}
478 	if (lseek(tr->fd, tr->off, SEEK_SET) == -1)
479 		err(1, "seek failed for file %s", tr->file);
480 	while (lba < end_lba && nblk != 0) {
481 		while (lba + nblk <= end_lba) {
482 			read(tr->fd, databuf, nblk * tr->blklen);
483 			scr.cmd[8] = nblk;
484 			scr.datalen = nblk * tr->blklen;
485 again:
486 			r = ioctl(fd, SCIOCCOMMAND, &scr);
487 			if (r != 0) {
488 				printf("\r%60s", "");
489 				warn("ioctl failed while attempting to write");
490 				return (-1);
491 			}
492 			if (scr.retsts == SCCMD_SENSE && scr.sense[2] == 0x2) {
493 				usleep(1000);
494 				goto again;
495 			}
496 			if (scr.retsts != SCCMD_OK) {
497 				printf("\r%60s", "");
498 				warnx("ioctl returned bad status while "
499 				    "attempting to write: %d",
500 				    scr.retsts);
501 				return (r);
502 			}
503 			lba += nblk;
504 
505 			clock_gettime(CLOCK_MONOTONIC, &ts);
506 			if (lba == end_lba || timespeccmp(&ts, &ots, >)) {
507 				fprintf(stderr,
508 				    "\rtrack %02d '%c' %08u/%08u %3d%%",
509 				    track, tr->type,
510 				    lba, end_lba, 100 * lba / end_lba);
511 				timespecadd(&ts, &ats, &ots);
512 			}
513 			tmp = htobe32(lba); /* update lba in cdb */
514 			memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
515 		}
516 		nblk--;
517 	}
518 	printf("\n");
519 	close(tr->fd);
520 	return (0);
521 }
522 
523 int
524 mode_sense_write(unsigned char buf[])
525 {
526 	struct scsi_mode_sense_big *scb;
527 	scsireq_t scr;
528 	int r;
529 
530 	bzero(&scr, sizeof(scr));
531 	scb = (struct scsi_mode_sense_big *)scr.cmd;
532 	scb->opcode = MODE_SENSE_BIG;
533 	/* XXX: need to set disable block descriptors and check SCSI drive */
534 	scb->page = WRITE_PARAM_PAGE;
535 	scb->length[1] = 0x46; /* 16 for the header + size from pg. 89 mmc-r10a.pdf */
536 	scr.cmdlen = sizeof(*scb);
537 	scr.timeout = 4000;
538 	scr.senselen = SENSEBUFLEN;
539 	scr.datalen= 0x46;
540 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
541 	scr.databuf = (caddr_t)buf;
542 
543 	r = ioctl(fd, SCIOCCOMMAND, &scr);
544 	return (r == 0 ? scr.retsts : -1);
545 }
546 
547 int
548 mode_select_write(unsigned char buf[])
549 {
550 	struct scsi_mode_select_big *scb;
551 	scsireq_t scr;
552 	int r;
553 
554 	bzero(&scr, sizeof(scr));
555 	scb = (struct scsi_mode_select_big *)scr.cmd;
556 	scb->opcode = MODE_SELECT_BIG;
557 
558 	/*
559 	 * INF-8020 says bit 4 in byte 2 is '1'
560 	 * INF-8090 refers to it as 'PF(1)' then doesn't
561 	 * describe it.
562 	 */
563 	scb->byte2 = 0x10;
564 	scb->length[1] = 2 + buf[1] + 256 * buf[0];
565 	scr.timeout = 4000;
566 	scr.senselen = SENSEBUFLEN;
567 	scr.cmdlen = sizeof(*scb);
568 	scr.datalen = 2 + buf[1] + 256 * buf[0];
569 	scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
570 	scr.databuf = (caddr_t)buf;
571 
572 	r = ioctl(fd, SCIOCCOMMAND, &scr);
573 	return (r == 0 ? scr.retsts : -1);
574 }
575 
576 int
577 get_disc_size(off_t *availblk)
578 {
579 	u_char databuf[28];
580 	struct scsi_read_track_info *scb;
581 	scsireq_t scr;
582 	int r, tmp;
583 
584 	bzero(&scr, sizeof(scr));
585 	scb = (struct scsi_read_track_info *)scr.cmd;
586 	scr.timeout = 4000;
587 	scr.senselen = SENSEBUFLEN;
588 	scb->opcode = READ_TRACK_INFO;
589 	scb->addrtype = RTI_TRACK;
590 	scb->addr[3] = 1;
591 	scb->data_len[1] = 0x1c;
592 	scr.cmdlen = sizeof(*scb);
593 	scr.datalen= 0x1c;
594 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
595 	scr.databuf = (caddr_t)databuf;
596 
597 	r = ioctl(fd, SCIOCCOMMAND, &scr);
598 	memcpy(&tmp, &databuf[16], sizeof(tmp));
599 	*availblk = betoh32(tmp);
600 	return (r == 0 ? scr.retsts : -1);
601 }
602 
603 int
604 get_nwa(int *nwa)
605 {
606 	u_char databuf[28];
607 	scsireq_t scr;
608 	int r, tmp;
609 
610 	bzero(&scr, sizeof(scr));
611 	scr.timeout = 4000;
612 	scr.senselen = SENSEBUFLEN;
613 	scr.cmd[0] = READ_TRACK_INFO;
614 	scr.cmd[1] = 0x01;
615 	scr.cmd[5] = 0xff; /* Invisible Track */
616 	scr.cmd[7] = 0x00;
617 	scr.cmd[8] = 0x1c;
618 	scr.cmdlen = 10;
619 	scr.datalen= 0x1c;
620 	scr.flags = SCCMD_ESCAPE|SCCMD_READ;
621 	scr.databuf = (caddr_t)databuf;
622 
623 	r = ioctl(fd, SCIOCCOMMAND, &scr);
624 	memcpy(&tmp, &databuf[12], sizeof(tmp));
625 	*nwa = betoh32(tmp);
626 	return (r == 0 ? scr.retsts : -1);
627 }
628