xref: /titanic_50/usr/src/cmd/cdrw/misc_scsi.c (revision 9858459238fb4f2d8f924f4d3fe9f51ed2e32564)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23a2b4fdf6Srameshc  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <string.h>
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
347c478bd9Sstevel@tonic-gate #include <unistd.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <libintl.h>
377c478bd9Sstevel@tonic-gate #include <sys/time.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include "mmc.h"
407c478bd9Sstevel@tonic-gate #include "util.h"
417c478bd9Sstevel@tonic-gate #include "misc_scsi.h"
427c478bd9Sstevel@tonic-gate #include "transport.h"
437c478bd9Sstevel@tonic-gate #include "main.h"
447c478bd9Sstevel@tonic-gate #include "toshiba.h"
457c478bd9Sstevel@tonic-gate #include "msgs.h"
46*98584592Sarutz #include "device.h"
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate uint32_t
497c478bd9Sstevel@tonic-gate read_scsi32(void *addr)
507c478bd9Sstevel@tonic-gate {
517c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
527c478bd9Sstevel@tonic-gate 	uint32_t ret;
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate 	ret = ((((uint32_t)ad[0]) << 24) | (((uint32_t)ad[1]) << 16) |
557c478bd9Sstevel@tonic-gate 	    (((uint32_t)ad[2]) << 8) | ad[3]);
567c478bd9Sstevel@tonic-gate 	return (ret);
577c478bd9Sstevel@tonic-gate }
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate uint16_t
607c478bd9Sstevel@tonic-gate read_scsi16(void *addr)
617c478bd9Sstevel@tonic-gate {
627c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
637c478bd9Sstevel@tonic-gate 	uint16_t ret;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 	ret = ((((uint16_t)ad[0]) << 8) | ad[1]);
667c478bd9Sstevel@tonic-gate 	return (ret);
677c478bd9Sstevel@tonic-gate }
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate void
707c478bd9Sstevel@tonic-gate load_scsi32(void *addr, uint32_t v)
717c478bd9Sstevel@tonic-gate {
727c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate 	ad[0] = (uchar_t)(v >> 24);
757c478bd9Sstevel@tonic-gate 	ad[1] = (uchar_t)(v >> 16);
767c478bd9Sstevel@tonic-gate 	ad[2] = (uchar_t)(v >> 8);
777c478bd9Sstevel@tonic-gate 	ad[3] = (uchar_t)v;
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate void
817c478bd9Sstevel@tonic-gate load_scsi16(void *addr, uint16_t v)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
847c478bd9Sstevel@tonic-gate 	ad[0] = (uchar_t)(v >> 8);
857c478bd9Sstevel@tonic-gate 	ad[1] = (uchar_t)v;
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * will get the mode page only i.e. will strip off the header.
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate int
917c478bd9Sstevel@tonic-gate get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	int ret;
947c478bd9Sstevel@tonic-gate 	uchar_t byte2, *buf;
957c478bd9Sstevel@tonic-gate 	uint_t header_len, page_len, copy_cnt;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
987c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(256);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	/* Ask 254 bytes only to make our IDE driver happy */
1017c478bd9Sstevel@tonic-gate 	ret = mode_sense(fd, byte2, 1, 254, buf);
1027c478bd9Sstevel@tonic-gate 	if (ret == 0) {
1037c478bd9Sstevel@tonic-gate 		free(buf);
1047c478bd9Sstevel@tonic-gate 		return (0);
1057c478bd9Sstevel@tonic-gate 	}
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	header_len = 8 + read_scsi16(&buf[6]);
1087c478bd9Sstevel@tonic-gate 	page_len = buf[header_len + 1] + 2;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
1117c478bd9Sstevel@tonic-gate 	(void) memcpy(buffer, &buf[header_len], copy_cnt);
1127c478bd9Sstevel@tonic-gate 	free(buf);
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	return (1);
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate  * will take care of adding mode header and any extra bytes at the end.
1197c478bd9Sstevel@tonic-gate  */
1207c478bd9Sstevel@tonic-gate int
1217c478bd9Sstevel@tonic-gate set_mode_page(int fd, uchar_t *buffer)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	int ret;
1247c478bd9Sstevel@tonic-gate 	uchar_t *buf;
1257c478bd9Sstevel@tonic-gate 	uint_t total, p_len;
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	p_len = buffer[1] + 2;
1287c478bd9Sstevel@tonic-gate 	total = p_len + 8;
1297c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(total);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	(void) memcpy(&buf[8], buffer, p_len);
1327c478bd9Sstevel@tonic-gate 	if (debug) {
1337c478bd9Sstevel@tonic-gate 		int i;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 		(void) printf("MODE: [");
1367c478bd9Sstevel@tonic-gate 		for (i = 0; i < p_len; i++) {
1377c478bd9Sstevel@tonic-gate 			(void) printf("0x%02x ", (uchar_t)buffer[i]);
1387c478bd9Sstevel@tonic-gate 		}
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 		(void) printf("]\n");
1417c478bd9Sstevel@tonic-gate 	}
1427c478bd9Sstevel@tonic-gate 	ret = mode_select(fd, total, buf);
1437c478bd9Sstevel@tonic-gate 	free(buf);
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	return (ret);
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate /*
1497c478bd9Sstevel@tonic-gate  * Builds track information database for track trackno. If trackno is
1507c478bd9Sstevel@tonic-gate  * -1, builds the database for next blank track.
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate int
1537c478bd9Sstevel@tonic-gate build_track_info(cd_device *dev, int trackno, struct track_info *t_info)
1547c478bd9Sstevel@tonic-gate {
1557c478bd9Sstevel@tonic-gate 	uchar_t *ti;
1567c478bd9Sstevel@tonic-gate 	uchar_t toc[20];		/* 2 entries + 4 byte header */
1577c478bd9Sstevel@tonic-gate 	int ret;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	(void) memset(t_info, 0, sizeof (*t_info));
1607c478bd9Sstevel@tonic-gate 	/* 1st try READ TRACK INFORMATION */
1617c478bd9Sstevel@tonic-gate 	ti = (uchar_t *)my_zalloc(TRACK_INFO_SIZE);
1627c478bd9Sstevel@tonic-gate 	t_info->ti_track_no = trackno;
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	/* Gererate faked information for writing to DVD */
1657c478bd9Sstevel@tonic-gate 	if (device_type != CD_RW) {
1667c478bd9Sstevel@tonic-gate 		uint_t bsize;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 		t_info->ti_flags = 0x3000;
1697c478bd9Sstevel@tonic-gate 		t_info->ti_track_no = 1;
1707c478bd9Sstevel@tonic-gate 		t_info->ti_session_no = 1;
1717c478bd9Sstevel@tonic-gate 		t_info->ti_track_mode = 0x4;
1727c478bd9Sstevel@tonic-gate 		t_info->ti_data_mode = 1;
1737c478bd9Sstevel@tonic-gate 		t_info->ti_start_address = 0;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 		/* only 1 track on DVD make it max size */
1767c478bd9Sstevel@tonic-gate 		t_info->ti_track_size = read_format_capacity(target->d_fd,
1777c478bd9Sstevel@tonic-gate 		    &bsize);
1787c478bd9Sstevel@tonic-gate 		if (t_info->ti_track_size < MAX_CD_BLKS) {
1797c478bd9Sstevel@tonic-gate 			t_info->ti_track_size = MAX_DVD_BLKS;
1807c478bd9Sstevel@tonic-gate 		}
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 		t_info->ti_nwa = 0;
1837c478bd9Sstevel@tonic-gate 		t_info->ti_lra = 0;
1847c478bd9Sstevel@tonic-gate 		t_info->ti_packet_size = 0x10;
1857c478bd9Sstevel@tonic-gate 		t_info->ti_free_blocks = 0;
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	if (read_track_info(dev->d_fd, trackno, ti)) {
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 		if (debug)
1917c478bd9Sstevel@tonic-gate 			(void) printf("using read_track_info for TOC \n");
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 		t_info->ti_track_no = ti[2];
1947c478bd9Sstevel@tonic-gate 		t_info->ti_session_no = ti[3];
1957c478bd9Sstevel@tonic-gate 		t_info->ti_flags = (ti[6] >> 4) & 0xf;
1967c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= (uint32_t)(ti[5] & 0xf0);
1977c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= (uint32_t)(ti[7]) << 8;
1987c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= TI_SESSION_NO_VALID | TI_FREE_BLOCKS_VALID;
1997c478bd9Sstevel@tonic-gate 		t_info->ti_track_mode = ti[5] & 0xf;
2007c478bd9Sstevel@tonic-gate 		if ((ti[6] & 0xf) == 0xf)
2017c478bd9Sstevel@tonic-gate 			t_info->ti_data_mode = 0xff;
2027c478bd9Sstevel@tonic-gate 		else
2037c478bd9Sstevel@tonic-gate 			t_info->ti_data_mode = ti[6] & 0xf;
2047c478bd9Sstevel@tonic-gate 		t_info->ti_start_address = read_scsi32(&ti[8]);
2057c478bd9Sstevel@tonic-gate 		t_info->ti_nwa = read_scsi32(&ti[12]);
2067c478bd9Sstevel@tonic-gate 		t_info->ti_free_blocks = read_scsi32(&ti[16]);
2077c478bd9Sstevel@tonic-gate 		t_info->ti_packet_size = read_scsi32(&ti[20]);
2087c478bd9Sstevel@tonic-gate 		t_info->ti_track_size = read_scsi32(&ti[24]);
2097c478bd9Sstevel@tonic-gate 		t_info->ti_lra = read_scsi32(&ti[28]);
2107c478bd9Sstevel@tonic-gate 		free(ti);
2117c478bd9Sstevel@tonic-gate 		return (1);
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 	/* READ TRACK INFORMATION not supported, try other options */
2147c478bd9Sstevel@tonic-gate 	free(ti);
2157c478bd9Sstevel@tonic-gate 	/*
2167c478bd9Sstevel@tonic-gate 	 * We can get info for next blank track if READ TRACK INFO is not
2177c478bd9Sstevel@tonic-gate 	 * supported.
2187c478bd9Sstevel@tonic-gate 	 */
2197c478bd9Sstevel@tonic-gate 	if (trackno == -1)
2207c478bd9Sstevel@tonic-gate 		return (0);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	if (debug)
2237c478bd9Sstevel@tonic-gate 		(void) printf("using READ_TOC for TOC\n");
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/* Try Read TOC */
2267c478bd9Sstevel@tonic-gate 	if (!read_toc(dev->d_fd, 0, trackno, 20, toc)) {
2277c478bd9Sstevel@tonic-gate 		return (0);
2287c478bd9Sstevel@tonic-gate 	}
2297c478bd9Sstevel@tonic-gate 	t_info->ti_start_address = read_scsi32(&toc[8]);
2307c478bd9Sstevel@tonic-gate 	t_info->ti_track_mode = toc[5] & 0xf;
2317c478bd9Sstevel@tonic-gate 	t_info->ti_track_size = read_scsi32(&toc[16]) - read_scsi32(&toc[8]);
2327c478bd9Sstevel@tonic-gate 	t_info->ti_data_mode = get_data_mode(dev->d_fd, read_scsi32(&toc[8]));
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	/* Numbers for audio tracks are always in 2K chunks */
2357c478bd9Sstevel@tonic-gate 	if ((dev->d_blksize == 512) && ((t_info->ti_track_mode & 4) == 0)) {
2367c478bd9Sstevel@tonic-gate 		t_info->ti_start_address /= 4;
2377c478bd9Sstevel@tonic-gate 		t_info->ti_track_size /= 4;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	/* Now find out the session thing */
2417c478bd9Sstevel@tonic-gate 	ret = read_toc(dev->d_fd, 1, trackno, 12, toc);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	/*
2447c478bd9Sstevel@tonic-gate 	 * Make sure that the call succeeds and returns the requested
2457c478bd9Sstevel@tonic-gate 	 * TOC size correctly.
2467c478bd9Sstevel@tonic-gate 	 */
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	if ((ret == 0) || (toc[1] != 0x0a)) {
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 		/* For ATAPI drives or old Toshiba drives */
2517c478bd9Sstevel@tonic-gate 		ret = read_toc_as_per_8020(dev->d_fd, 1, trackno, 12, toc);
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 	/* If this goes through well TOC length will always be 0x0a */
2547c478bd9Sstevel@tonic-gate 	if (ret && (toc[1] == 0x0a)) {
2557c478bd9Sstevel@tonic-gate 		if (trackno >= toc[6]) {
2567c478bd9Sstevel@tonic-gate 			t_info->ti_session_no = toc[3];
2577c478bd9Sstevel@tonic-gate 			t_info->ti_flags |= TI_SESSION_NO_VALID;
2587c478bd9Sstevel@tonic-gate 		}
2597c478bd9Sstevel@tonic-gate 		/*
2607c478bd9Sstevel@tonic-gate 		 * This might be the last track of this session. If so,
2617c478bd9Sstevel@tonic-gate 		 * exclude the leadout and next lead in.
2627c478bd9Sstevel@tonic-gate 		 */
2637c478bd9Sstevel@tonic-gate 		if (trackno == (toc[6] - 1)) {
2647c478bd9Sstevel@tonic-gate 			/*
2657c478bd9Sstevel@tonic-gate 			 * 1.5 Min leadout + 1 min. leadin + 2 sec. pre-gap.
2667c478bd9Sstevel@tonic-gate 			 * For 2nd+ leadout it will be 0.5 min. But currently
2677c478bd9Sstevel@tonic-gate 			 * there is no direct way. And it will not happen
2687c478bd9Sstevel@tonic-gate 			 * for any normal case.
2697c478bd9Sstevel@tonic-gate 			 *
2707c478bd9Sstevel@tonic-gate 			 * 75 frames/sec, 60 sec/min, so leadin gap is
2717c478bd9Sstevel@tonic-gate 			 * ((1.5 +1)*60 + 2)*75 = 11400 frames (blocks)
2727c478bd9Sstevel@tonic-gate 			 */
2737c478bd9Sstevel@tonic-gate 			t_info->ti_track_size -= 11400;
2747c478bd9Sstevel@tonic-gate 		}
2757c478bd9Sstevel@tonic-gate 	}
2767c478bd9Sstevel@tonic-gate 	return (1);
2777c478bd9Sstevel@tonic-gate }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate uchar_t
2807c478bd9Sstevel@tonic-gate get_data_mode(int fd, uint32_t lba)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	int ret;
2837c478bd9Sstevel@tonic-gate 	uchar_t *buf;
2847c478bd9Sstevel@tonic-gate 	uchar_t mode;
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(8);
2877c478bd9Sstevel@tonic-gate 	ret = read_header(fd, lba, buf);
2887c478bd9Sstevel@tonic-gate 	if (ret == 0)
2897c478bd9Sstevel@tonic-gate 		mode = 0xff;
2907c478bd9Sstevel@tonic-gate 	else
2917c478bd9Sstevel@tonic-gate 		mode = buf[0];
2927c478bd9Sstevel@tonic-gate 	free(buf);
2937c478bd9Sstevel@tonic-gate 	return (mode);
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate  * Set page code 5 for TAO mode.
2987c478bd9Sstevel@tonic-gate  */
2997c478bd9Sstevel@tonic-gate int
3007c478bd9Sstevel@tonic-gate prepare_for_write(cd_device *dev, int track_mode, int test_write,
3017c478bd9Sstevel@tonic-gate     int keep_disc_open)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	uchar_t *buf;
3047c478bd9Sstevel@tonic-gate 	int no_err;
3057c478bd9Sstevel@tonic-gate 	int reset_device;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	if ((write_mode == DAO_MODE) && keep_disc_open) {
3087c478bd9Sstevel@tonic-gate 		(void) printf(gettext(
3097c478bd9Sstevel@tonic-gate 		    "Multi-session is not supported on DVD media\n"));
3107c478bd9Sstevel@tonic-gate 		exit(1);
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	if ((write_mode == DAO_MODE) && debug) {
3147c478bd9Sstevel@tonic-gate 		(void) printf("Preparing to write in DAO\n");
3157c478bd9Sstevel@tonic-gate 	}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	(void) start_stop(dev->d_fd, 1);
3187c478bd9Sstevel@tonic-gate 	/* Some drives do not support this command but still do it */
3197c478bd9Sstevel@tonic-gate 	(void) rezero_unit(dev->d_fd);
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(64);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	no_err = get_mode_page(dev->d_fd, 5, 0, 64, buf);
3247c478bd9Sstevel@tonic-gate 	if (no_err)
3257c478bd9Sstevel@tonic-gate 		no_err = ((buf[1] + 2) > 64) ? 0 : 1;
3267c478bd9Sstevel@tonic-gate 	/*
3277c478bd9Sstevel@tonic-gate 	 * If the device is already in simulation mode and again a
3287c478bd9Sstevel@tonic-gate 	 * simulation is requested, then set the device in non-simulation
3297c478bd9Sstevel@tonic-gate 	 * 1st and then take it to simulation mode. This will flush any
3307c478bd9Sstevel@tonic-gate 	 * previous fake state in the drive.
3317c478bd9Sstevel@tonic-gate 	 */
3327c478bd9Sstevel@tonic-gate 	if (no_err && test_write && (buf[2] & 0x10)) {
3337c478bd9Sstevel@tonic-gate 		reset_device = 1;
3347c478bd9Sstevel@tonic-gate 	} else {
3357c478bd9Sstevel@tonic-gate 		reset_device = 0;
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 	if (no_err != 0) {
3387c478bd9Sstevel@tonic-gate 		buf[0] &= 0x3f;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 		/* set TAO or DAO writing mode */
3417c478bd9Sstevel@tonic-gate 		buf[2] = (write_mode == TAO_MODE)?1:2;
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 		/* set simulation flag */
3447c478bd9Sstevel@tonic-gate 		if (test_write && (!reset_device)) {
3457c478bd9Sstevel@tonic-gate 			buf[2] |= 0x10;
3467c478bd9Sstevel@tonic-gate 		} else {
3477c478bd9Sstevel@tonic-gate 			buf[2] &= ~0x10;
3487c478bd9Sstevel@tonic-gate 		}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 		/* Turn on HW buffer underrun protection (BUFE) */
3517c478bd9Sstevel@tonic-gate 		if (!test_write) {
3527c478bd9Sstevel@tonic-gate 			buf[2] |= 0x40;
3537c478bd9Sstevel@tonic-gate 		}
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 		/* set track mode type */
3567c478bd9Sstevel@tonic-gate 		if (device_type == CD_RW) {
3577c478bd9Sstevel@tonic-gate 			buf[3] = track_mode & 0x0f;	/* ctrl nibble */
3587c478bd9Sstevel@tonic-gate 		} else {
3597c478bd9Sstevel@tonic-gate 			buf[3] = 5;	/* always 5 for DVD */
3607c478bd9Sstevel@tonic-gate 		}
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		if (keep_disc_open) {
3637c478bd9Sstevel@tonic-gate 			buf[3] |= 0xc0;		/* Allow more sessions */
3647c478bd9Sstevel@tonic-gate 		}
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 		/* Select track type (audio or data) */
3677c478bd9Sstevel@tonic-gate 		if (track_mode == TRACK_MODE_DATA) {
3687c478bd9Sstevel@tonic-gate 			buf[4] = 8;		/* 2048 byte sector */
3697c478bd9Sstevel@tonic-gate 		} else {
3707c478bd9Sstevel@tonic-gate 			buf[4] = 0;		/* 2352 byte sector */
3717c478bd9Sstevel@tonic-gate 		}
3727c478bd9Sstevel@tonic-gate 		buf[7] = buf[8] = 0;
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 		/* Need to clear these fields for setting into DAO */
3757c478bd9Sstevel@tonic-gate 		if (write_mode == DAO_MODE)
3767c478bd9Sstevel@tonic-gate 			buf[5] = buf[15] = 0;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 		/* print out mode for detailed log */
3797c478bd9Sstevel@tonic-gate 		if (debug && verbose) {
3807c478bd9Sstevel@tonic-gate 			int i;
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 			(void) printf("setting = [ ");
3837c478bd9Sstevel@tonic-gate 			for (i = 0; i < 15; i++)
3847c478bd9Sstevel@tonic-gate 				(void) printf("0x%x ", buf[i]);
3857c478bd9Sstevel@tonic-gate 			(void) printf("]\n");
3867c478bd9Sstevel@tonic-gate 		}
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 		no_err = set_mode_page(dev->d_fd, buf);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 		if (no_err && reset_device) {
3917c478bd9Sstevel@tonic-gate 			/* Turn the test write bit back on */
3927c478bd9Sstevel@tonic-gate 			buf[2] |= 0x10;
3937c478bd9Sstevel@tonic-gate 			no_err = set_mode_page(dev->d_fd, buf);
3947c478bd9Sstevel@tonic-gate 		}
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 		/*
3977c478bd9Sstevel@tonic-gate 		 * Since BUFE is the only optional flag we are
3987c478bd9Sstevel@tonic-gate 		 * setting we will try to turn it off if the command
3997c478bd9Sstevel@tonic-gate 		 * fails.
4007c478bd9Sstevel@tonic-gate 		 */
4017c478bd9Sstevel@tonic-gate 		if (!no_err) {
4027c478bd9Sstevel@tonic-gate 			/*
4037c478bd9Sstevel@tonic-gate 			 * Some old drives may not support HW
4047c478bd9Sstevel@tonic-gate 			 * buffer underrun protection, try again
4057c478bd9Sstevel@tonic-gate 			 * after turning it off.
4067c478bd9Sstevel@tonic-gate 			 */
4077c478bd9Sstevel@tonic-gate 			if (debug)
4087c478bd9Sstevel@tonic-gate 				(void) printf("Turning off BUFE\n");
4097c478bd9Sstevel@tonic-gate 			buf[2] &= ~0x40;
4107c478bd9Sstevel@tonic-gate 			no_err = set_mode_page(dev->d_fd, buf);
4117c478bd9Sstevel@tonic-gate 		}
4127c478bd9Sstevel@tonic-gate 	}
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	free(buf);
4157c478bd9Sstevel@tonic-gate 	return (no_err);
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate /*
4197c478bd9Sstevel@tonic-gate  * Close session. This will write TOC.
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate int
4227c478bd9Sstevel@tonic-gate finalize(cd_device *dev)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	uchar_t *di;
4257c478bd9Sstevel@tonic-gate 	int count, ret, err;
4267c478bd9Sstevel@tonic-gate 	int immediate;
4277c478bd9Sstevel@tonic-gate 	int finalize_max;
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/*
4307c478bd9Sstevel@tonic-gate 	 * For ATAPI devices we will use the immediate mode and will
4317c478bd9Sstevel@tonic-gate 	 * poll the command for completion so that this command may
4327c478bd9Sstevel@tonic-gate 	 * not hog the channel. But for SCSI, we will use the treditional
4337c478bd9Sstevel@tonic-gate 	 * way of issuing the command with a large enough timeout. This
4347c478bd9Sstevel@tonic-gate 	 * is done because immediate mode was designed for ATAPI and some
4357c478bd9Sstevel@tonic-gate 	 * SCSI RW drives might not be even tested with it.
4367c478bd9Sstevel@tonic-gate 	 */
4377c478bd9Sstevel@tonic-gate 	if ((dev->d_inq[2] & 7) != 0) {
4387c478bd9Sstevel@tonic-gate 		/* SCSI device */
4397c478bd9Sstevel@tonic-gate 		immediate = 0;
4407c478bd9Sstevel@tonic-gate 	} else {
4417c478bd9Sstevel@tonic-gate 		/* non-SCSI (e.g ATAPI) device */
4427c478bd9Sstevel@tonic-gate 		immediate = 1;
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	/* We need to close track before close session */
4467c478bd9Sstevel@tonic-gate 	if (device_type == DVD_PLUS) {
4477c478bd9Sstevel@tonic-gate 		if (!close_track(dev->d_fd, 0, 0, immediate))
4487c478bd9Sstevel@tonic-gate 			return (0);
4497c478bd9Sstevel@tonic-gate 	}
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	if (!close_track(dev->d_fd, 0, 1, immediate)) {
4527c478bd9Sstevel@tonic-gate 		/*
4537c478bd9Sstevel@tonic-gate 		 * For DVD-RW close track is not well defined
4547c478bd9Sstevel@tonic-gate 		 * some drives dont like it, others want us
4557c478bd9Sstevel@tonic-gate 		 * to close track before closing the session.
4567c478bd9Sstevel@tonic-gate 		 * NOTE that for MMC specification it is not mandatory
4577c478bd9Sstevel@tonic-gate 		 * to support close track.
4587c478bd9Sstevel@tonic-gate 		 */
4597c478bd9Sstevel@tonic-gate 		if (device_type == DVD_MINUS) {
4607c478bd9Sstevel@tonic-gate 			if (!close_track(dev->d_fd, 1, 0, immediate)) {
4617c478bd9Sstevel@tonic-gate 				return (0);
4627c478bd9Sstevel@tonic-gate 			} else {
4637c478bd9Sstevel@tonic-gate 				/* command is already done */
4647c478bd9Sstevel@tonic-gate 				if (!immediate)
4657c478bd9Sstevel@tonic-gate 					return (1);
4667c478bd9Sstevel@tonic-gate 			}
4677c478bd9Sstevel@tonic-gate 		} else {
4687c478bd9Sstevel@tonic-gate 			return (0);
4697c478bd9Sstevel@tonic-gate 		}
4707c478bd9Sstevel@tonic-gate 	} else {
4717c478bd9Sstevel@tonic-gate 		if (!immediate)
4727c478bd9Sstevel@tonic-gate 			return (1);
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 	if (immediate) {
4757c478bd9Sstevel@tonic-gate 		(void) sleep(10);
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 		di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
4787c478bd9Sstevel@tonic-gate 		err = 0;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 		if (device_type == CD_RW) {
4817c478bd9Sstevel@tonic-gate 			/* Finalization should not take more than 6 minutes */
4827c478bd9Sstevel@tonic-gate 			finalize_max = FINALIZE_TIMEOUT;
4837c478bd9Sstevel@tonic-gate 		} else {
4847c478bd9Sstevel@tonic-gate 			/* some DVD-RW drives take longer than 6 minutes */
4857c478bd9Sstevel@tonic-gate 			finalize_max = FINALIZE_TIMEOUT*2;
4867c478bd9Sstevel@tonic-gate 		}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 		for (count = 0; count < finalize_max; count++) {
4897c478bd9Sstevel@tonic-gate 			ret = read_disc_info(dev->d_fd, di);
4907c478bd9Sstevel@tonic-gate 			if (ret != 0)
4917c478bd9Sstevel@tonic-gate 				break;
4927c478bd9Sstevel@tonic-gate 			if (uscsi_status != 2)
4937c478bd9Sstevel@tonic-gate 				err = 1;
4947c478bd9Sstevel@tonic-gate 			if (SENSE_KEY(rqbuf) == 2) {
4957c478bd9Sstevel@tonic-gate 				/* not ready but not becoming ready */
4967c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) != 4)
4977c478bd9Sstevel@tonic-gate 					err = 1;
4987c478bd9Sstevel@tonic-gate 			} else if (SENSE_KEY(rqbuf) == 5) {
4997c478bd9Sstevel@tonic-gate 				/* illegal mode for this track */
5007c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) != 0x64)
5017c478bd9Sstevel@tonic-gate 					err = 1;
5027c478bd9Sstevel@tonic-gate 			} else {
5037c478bd9Sstevel@tonic-gate 				err = 1;
5047c478bd9Sstevel@tonic-gate 			}
5057c478bd9Sstevel@tonic-gate 			if (err == 1) {
5067c478bd9Sstevel@tonic-gate 				if (debug) {
5077c478bd9Sstevel@tonic-gate 					(void) printf("Finalization failed\n");
5087c478bd9Sstevel@tonic-gate 					(void) printf("%x %x %x %x\n",
5097c478bd9Sstevel@tonic-gate 					    uscsi_status, SENSE_KEY(rqbuf),
5107c478bd9Sstevel@tonic-gate 					    ASC(rqbuf), ASCQ(rqbuf));
5117c478bd9Sstevel@tonic-gate 				}
5127c478bd9Sstevel@tonic-gate 				free(di);
5137c478bd9Sstevel@tonic-gate 				return (0);
5147c478bd9Sstevel@tonic-gate 			}
5157c478bd9Sstevel@tonic-gate 			if (uscsi_status == 2) {
5167c478bd9Sstevel@tonic-gate 				int i;
5177c478bd9Sstevel@tonic-gate 				/* illegal field in command packet */
5187c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) == 0x24) {
5197c478bd9Sstevel@tonic-gate 					/* print it out! */
5207c478bd9Sstevel@tonic-gate 					(void) printf("\n");
5217c478bd9Sstevel@tonic-gate 					for (i = 0; i < 18; i++)
5227c478bd9Sstevel@tonic-gate 						(void) printf("%x ",
5237c478bd9Sstevel@tonic-gate 						    (unsigned)(rqbuf[i]));
5247c478bd9Sstevel@tonic-gate 					(void) printf("\n");
5257c478bd9Sstevel@tonic-gate 				}
5267c478bd9Sstevel@tonic-gate 			}
5277c478bd9Sstevel@tonic-gate 			(void) sleep(5);
5287c478bd9Sstevel@tonic-gate 		}
5297c478bd9Sstevel@tonic-gate 		free(di);
5307c478bd9Sstevel@tonic-gate 	}
5317c478bd9Sstevel@tonic-gate 	return (ret);
5327c478bd9Sstevel@tonic-gate }
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate /*
5357c478bd9Sstevel@tonic-gate  * Find out media capacity.
5367c478bd9Sstevel@tonic-gate  */
5377c478bd9Sstevel@tonic-gate int
5387c478bd9Sstevel@tonic-gate get_last_possible_lba(cd_device *dev)
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	uchar_t *di;
5417c478bd9Sstevel@tonic-gate 	int cap;
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
5447c478bd9Sstevel@tonic-gate 	if (!read_disc_info(dev->d_fd, di)) {
5457c478bd9Sstevel@tonic-gate 		free(di);
5467c478bd9Sstevel@tonic-gate 		return (0);
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 	if ((di[21] != 0) && (di[21] != 0xff)) {
5497c478bd9Sstevel@tonic-gate 		cap = ((di[21] * 60) + di[22]) * 75;
5507c478bd9Sstevel@tonic-gate 	} else {
5517c478bd9Sstevel@tonic-gate 		cap = 0;
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	free(di);
5557c478bd9Sstevel@tonic-gate 	return (cap);
5567c478bd9Sstevel@tonic-gate }
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate int
5597c478bd9Sstevel@tonic-gate read_audio_through_read_cd(cd_device *dev, uint_t start_lba, uint_t nblks,
5607c478bd9Sstevel@tonic-gate     uchar_t *buf)
5617c478bd9Sstevel@tonic-gate {
5627c478bd9Sstevel@tonic-gate 	int retry;
5637c478bd9Sstevel@tonic-gate 	int ret;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	for (retry = 0; retry < 3; retry++) {
5667c478bd9Sstevel@tonic-gate 		ret = read_cd(dev->d_fd, (uint32_t)start_lba, (uint16_t)nblks,
5677c478bd9Sstevel@tonic-gate 		    1, buf, (uint32_t)(nblks * 2352));
5687c478bd9Sstevel@tonic-gate 		if (ret)
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 	}
5717c478bd9Sstevel@tonic-gate 	return (ret);
5727c478bd9Sstevel@tonic-gate }
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate int
5757c478bd9Sstevel@tonic-gate eject_media(cd_device *dev)
5767c478bd9Sstevel@tonic-gate {
5777c478bd9Sstevel@tonic-gate 	if (vol_running) {
5787c478bd9Sstevel@tonic-gate 		/* If there is a media, try using DKIOCEJECT 1st */
5797c478bd9Sstevel@tonic-gate 		if (check_device(dev, CHECK_NO_MEDIA) == 0) {
5807c478bd9Sstevel@tonic-gate 			if (ioctl(dev->d_fd, DKIOCEJECT, 0) == 0) {
5817c478bd9Sstevel@tonic-gate 				return (1);
5827c478bd9Sstevel@tonic-gate 			}
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 	if (load_unload(dev->d_fd, 0) == 0) {
5867c478bd9Sstevel@tonic-gate 		/* if eject fails */
5877c478bd9Sstevel@tonic-gate 		if ((uscsi_status == 2) && (ASC(rqbuf) == 0x53)) {
5887c478bd9Sstevel@tonic-gate 			/*
5897c478bd9Sstevel@tonic-gate 			 * check that eject is not blocked on the device
5907c478bd9Sstevel@tonic-gate 			 */
5917c478bd9Sstevel@tonic-gate 			if (!prevent_allow_mr(dev->d_fd, 1))
5927c478bd9Sstevel@tonic-gate 				return (0);
5937c478bd9Sstevel@tonic-gate 			return (load_unload(dev->d_fd, 0));
5947c478bd9Sstevel@tonic-gate 		}
5957c478bd9Sstevel@tonic-gate 		return (0);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 	return (1);
5987c478bd9Sstevel@tonic-gate }
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate /*
601*98584592Sarutz  * Get current Read or Write Speed from Mode Page 0x2a.
602*98584592Sarutz  *
603*98584592Sarutz  * Use the size of the Page to determine which Multimedia Command
604*98584592Sarutz  * set (MMC) is present.  Based on the MMC version, get the
605*98584592Sarutz  * specified Read/Write Speed.
606*98584592Sarutz  *
607*98584592Sarutz  * Note that some MMC versions do not necessarily support a
608*98584592Sarutz  * (current) Read or Write Speed.  As a result, this function
609*98584592Sarutz  * _can_ return a value of zero.
610*98584592Sarutz  *
611*98584592Sarutz  * The newer standards (reserve and) mark the field(s) as Obsolete,
612*98584592Sarutz  * yet many vendors populate the Obsolete fields with valid values
613*98584592Sarutz  * (assumedly for backward compatibility).  This is important, as
614*98584592Sarutz  * a command like GET PERFORMANCE cannot return _the_ speed; it can
615*98584592Sarutz  * only return a Logical-Block-Address-dependent (LBA) speed.  Such
616*98584592Sarutz  * values can vary widely between the innermost and outermost Track.
617*98584592Sarutz  * Mode Page 0x2a is the best solution identifying "the current
618*98584592Sarutz  * (nominal) speed".
6197c478bd9Sstevel@tonic-gate  */
6207c478bd9Sstevel@tonic-gate static uint16_t
621*98584592Sarutz cd_speed_get(cd_device *dev, int cmd)
6227c478bd9Sstevel@tonic-gate {
6237c478bd9Sstevel@tonic-gate 	uchar_t		*mp2a;
624*98584592Sarutz 	uint16_t	rate = 0;
625*98584592Sarutz 	int		offset;
626*98584592Sarutz 	uint_t		buflen = 254;
6277c478bd9Sstevel@tonic-gate 
628*98584592Sarutz 	/*
629*98584592Sarutz 	 * Allocate a buffer acceptably larger than any nominal
630*98584592Sarutz 	 * Page for Page Code 0x2A.
631*98584592Sarutz 	 */
632*98584592Sarutz 	mp2a = (uchar_t *)my_zalloc(buflen);
633*98584592Sarutz 	if (get_mode_page(dev->d_fd, 0x2A, 0, buflen, mp2a) == 0)
634*98584592Sarutz 		goto end;
635*98584592Sarutz 
636*98584592Sarutz 	/* Determine MMC version based on 'Page Length' field */
637*98584592Sarutz 	switch (mp2a[1]) {
638*98584592Sarutz 	case 0x14:  /* MMC-1 */
639*98584592Sarutz 		if (debug)
640*98584592Sarutz 			(void) printf("Mode Page 2A: MMC-1\n");
641*98584592Sarutz 
642*98584592Sarutz 		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
643*98584592Sarutz 		rate = read_scsi16(&mp2a[offset]);
644*98584592Sarutz 		break;
645*98584592Sarutz 
646*98584592Sarutz 
647*98584592Sarutz 	case 0x18: /* MMC-2 */
648*98584592Sarutz 		if (debug)
649*98584592Sarutz 			(void) printf("Mode Page 2A: MMC-2;"
650*98584592Sarutz 			    " Read and Write Speeds are "
651*98584592Sarutz 			    "obsolete\n");
652*98584592Sarutz 
653*98584592Sarutz 		/* see if "Obsolete" values are valid: */
654*98584592Sarutz 		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
655*98584592Sarutz 		rate = read_scsi16(&mp2a[offset]);
656*98584592Sarutz 		break;
657*98584592Sarutz 
658*98584592Sarutz 	default: /* MMC-3 or newer */
659*98584592Sarutz 		if (debug)
660*98584592Sarutz 			(void) printf("Mode Page 2A: MMC-3 or"
661*98584592Sarutz 			    " newer; Read Speed is obsolete.\n");
662*98584592Sarutz 
6637c478bd9Sstevel@tonic-gate 		if (cmd == GET_READ_SPEED) {
664*98584592Sarutz 			/* this is Obsolete, but try it */
665*98584592Sarutz 			offset = 14;
666*98584592Sarutz 			rate = read_scsi16(&mp2a[offset]);
6677c478bd9Sstevel@tonic-gate 		} else {
668*98584592Sarutz 			/* Write Speed is not obsolete */
669*98584592Sarutz 			offset = 28;
670*98584592Sarutz 			rate = read_scsi16(&mp2a[offset]);
671*98584592Sarutz 
672*98584592Sarutz 			if (rate == 0) {
673*98584592Sarutz 				/*
674*98584592Sarutz 				 * then try an Obsolete field
675*98584592Sarutz 				 * (but this shouldn't happen!)
676*98584592Sarutz 				 */
677*98584592Sarutz 				offset = 20;
678*98584592Sarutz 				rate = read_scsi16(&mp2a[offset]);
6797c478bd9Sstevel@tonic-gate 			}
6807c478bd9Sstevel@tonic-gate 		}
681*98584592Sarutz 		break;
682*98584592Sarutz 	}
683*98584592Sarutz end:
6847c478bd9Sstevel@tonic-gate 	free(mp2a);
685*98584592Sarutz 
686*98584592Sarutz 	if (debug)
687*98584592Sarutz 		(void) printf("cd_speed_get: %s Speed is "
688*98584592Sarutz 		    "%uX\n", (cmd == GET_READ_SPEED) ?
689*98584592Sarutz 		    "Read" : "Write", cdrw_bandwidth_to_x(rate));
6907c478bd9Sstevel@tonic-gate 	return (rate);
6917c478bd9Sstevel@tonic-gate }
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate /*
6947c478bd9Sstevel@tonic-gate  * CD speed related functions (ioctl style) for drives which do not support
6957c478bd9Sstevel@tonic-gate  * real time streaming.
696*98584592Sarutz  *
697*98584592Sarutz  * For the SET operations, the SET CD SPEED command needs
698*98584592Sarutz  * both the Read Speed and the Write Speed.  Eg, if
699*98584592Sarutz  * we're trying to set the Write Speed (SET_WRITE_SPEED),
700*98584592Sarutz  * then we first need to obtain the current Read Speed.
701*98584592Sarutz  * That speed is specified along with the chosen_speed (the
702*98584592Sarutz  * Write Speed in this case) in the SET CD SPEED command.
7037c478bd9Sstevel@tonic-gate  */
7047c478bd9Sstevel@tonic-gate int
7057c478bd9Sstevel@tonic-gate cd_speed_ctrl(cd_device *dev, int cmd, int speed)
7067c478bd9Sstevel@tonic-gate {
7077c478bd9Sstevel@tonic-gate 	uint16_t rate;
7087c478bd9Sstevel@tonic-gate 
709*98584592Sarutz 	switch (cmd) {
710*98584592Sarutz 	case GET_READ_SPEED:
711*98584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
712*98584592Sarutz 		return (cdrw_bandwidth_to_x(rate));
713*98584592Sarutz 
714*98584592Sarutz 	case GET_WRITE_SPEED:
715*98584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
716*98584592Sarutz 		return (cdrw_bandwidth_to_x(rate));
717*98584592Sarutz 
718*98584592Sarutz 	case SET_READ_SPEED:
719*98584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
720*98584592Sarutz 		return (set_cd_speed(dev->d_fd,
721*98584592Sarutz 		    cdrw_x_to_bandwidth(speed), rate));
722*98584592Sarutz 		break;
723*98584592Sarutz 
724*98584592Sarutz 	case SET_WRITE_SPEED:
725*98584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
7267c478bd9Sstevel@tonic-gate 		return (set_cd_speed(dev->d_fd, rate,
727*98584592Sarutz 		    cdrw_x_to_bandwidth(speed)));
728*98584592Sarutz 		break;
729*98584592Sarutz 
730*98584592Sarutz 	default:
7317c478bd9Sstevel@tonic-gate 		return (0);
7327c478bd9Sstevel@tonic-gate 	}
733*98584592Sarutz }
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate /*
736*98584592Sarutz  * Manage sending of SET STREAMING command using the specified
737*98584592Sarutz  * read_speed and write_speed.
738*98584592Sarutz  *
739*98584592Sarutz  * This function allocates and initializes a Performance
740*98584592Sarutz  * Descriptor, which is sent as part of the SET STREAMING
741*98584592Sarutz  * command.  The descriptor is deallocated before function
742*98584592Sarutz  * exit.
743*98584592Sarutz  */
744*98584592Sarutz static int
745*98584592Sarutz do_set_streaming(cd_device *dev, uint_t read_speed,
746*98584592Sarutz 	uint_t write_speed)
747*98584592Sarutz {
748*98584592Sarutz 	int ret;
749*98584592Sarutz 	uchar_t *str;
750*98584592Sarutz 
751*98584592Sarutz 	/* Allocate and initialize the Performance Descriptor */
752*98584592Sarutz 	str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN);
753*98584592Sarutz 
754*98584592Sarutz 	/* Read Time (in milliseconds) */
755*98584592Sarutz 	load_scsi32(&str[16], 1000);
756*98584592Sarutz 	/* Write Time (in milliseconds) */
757*98584592Sarutz 	load_scsi32(&str[24], 1000);
758*98584592Sarutz 
759*98584592Sarutz 	/* Read Speed */
760*98584592Sarutz 	load_scsi32(&str[12], (uint32_t)read_speed);
761*98584592Sarutz 	/* Write Speed */
762*98584592Sarutz 	load_scsi32(&str[20], (uint32_t)write_speed);
763*98584592Sarutz 
764*98584592Sarutz 	/* issue SET STREAMING command */
765*98584592Sarutz 	ret = set_streaming(dev->d_fd, str);
766*98584592Sarutz 	free(str);
767*98584592Sarutz 
768*98584592Sarutz 	return (ret);
769*98584592Sarutz }
770*98584592Sarutz 
771*98584592Sarutz /*
772*98584592Sarutz  * cd speed related functions for drives which support
773*98584592Sarutz  * Real-Time Streaming Feature.
774*98584592Sarutz  *
775*98584592Sarutz  * For the SET operations, the SET STREAMING command needs
776*98584592Sarutz  * both the Read Speed and the Write Speed.  Eg, if
777*98584592Sarutz  * we're trying to set the Write Speed (SET_WRITE_SPEED),
778*98584592Sarutz  * then we first need to obtain the current Read Speed.
779*98584592Sarutz  * That speed is specified along with the chosen_speed (the
780*98584592Sarutz  * Write Speed in this case) in the SET STREAMING command.
7817c478bd9Sstevel@tonic-gate  */
7827c478bd9Sstevel@tonic-gate int
7837c478bd9Sstevel@tonic-gate rt_streaming_ctrl(cd_device *dev, int cmd, int speed)
7847c478bd9Sstevel@tonic-gate {
785*98584592Sarutz 	int ret = 0;
786*98584592Sarutz 	uint_t rate;
7877c478bd9Sstevel@tonic-gate 
788*98584592Sarutz 	switch (cmd) {
789*98584592Sarutz 	case GET_WRITE_SPEED:
790*98584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
791*98584592Sarutz 		ret = (int)cdrw_bandwidth_to_x(rate);
792*98584592Sarutz 		break;
793*98584592Sarutz 
794*98584592Sarutz 	case GET_READ_SPEED:
795*98584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
796*98584592Sarutz 		ret = (int)cdrw_bandwidth_to_x(rate);
797*98584592Sarutz 		break;
798*98584592Sarutz 
799*98584592Sarutz 	case SET_READ_SPEED: {
800*98584592Sarutz 		uint_t write_speed = cd_speed_get(dev, GET_WRITE_SPEED);
801*98584592Sarutz 
802*98584592Sarutz 		/* set Read Speed using SET STREAMING */
803*98584592Sarutz 		ret = do_set_streaming(dev,
804*98584592Sarutz 		    cdrw_x_to_bandwidth(speed), write_speed);
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
8077c478bd9Sstevel@tonic-gate 		if (ret == 0) {
8087c478bd9Sstevel@tonic-gate 			if (debug)
8097c478bd9Sstevel@tonic-gate 				(void) printf(" real time speed control"
8107c478bd9Sstevel@tonic-gate 				    " failed, using CD speed control\n");
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 			dev->d_speed_ctrl = cd_speed_ctrl;
8137c478bd9Sstevel@tonic-gate 			ret = dev->d_speed_ctrl(dev, cmd, speed);
8147c478bd9Sstevel@tonic-gate 		}
815*98584592Sarutz 		break;
816*98584592Sarutz 	}
8177c478bd9Sstevel@tonic-gate 
818*98584592Sarutz 	case SET_WRITE_SPEED: {
819*98584592Sarutz 		uint_t read_speed = cd_speed_get(dev, GET_READ_SPEED);
820*98584592Sarutz 
821*98584592Sarutz 		/* set Write Speed using SET STREAMING */
822*98584592Sarutz 		ret = do_set_streaming(dev, read_speed,
823*98584592Sarutz 		    cdrw_x_to_bandwidth(speed));
824*98584592Sarutz 
825*98584592Sarutz 		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
826*98584592Sarutz 		if (ret == 0) {
827*98584592Sarutz 			if (debug)
828*98584592Sarutz 				(void) printf(" real time speed control"
829*98584592Sarutz 				    " failed, using CD speed control\n");
830*98584592Sarutz 
831*98584592Sarutz 			dev->d_speed_ctrl = cd_speed_ctrl;
832*98584592Sarutz 			ret = dev->d_speed_ctrl(dev, cmd, speed);
833*98584592Sarutz 		}
834*98584592Sarutz 		break;
835*98584592Sarutz 	}
836*98584592Sarutz 
837*98584592Sarutz 	default:
838*98584592Sarutz 		break;
839*98584592Sarutz 	}
840*98584592Sarutz 
8417c478bd9Sstevel@tonic-gate 	return (ret);
8427c478bd9Sstevel@tonic-gate }
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate /*
8457c478bd9Sstevel@tonic-gate  * Initialize device for track-at-once mode of writing. All of the data will
8467c478bd9Sstevel@tonic-gate  * need to be written to the track without interruption.
8477c478bd9Sstevel@tonic-gate  * This initialized TAO by setting page code 5 and speed.
8487c478bd9Sstevel@tonic-gate  */
8497c478bd9Sstevel@tonic-gate void
8507c478bd9Sstevel@tonic-gate write_init(int mode)
8517c478bd9Sstevel@tonic-gate {
8527c478bd9Sstevel@tonic-gate 	(void) printf(gettext("Initializing device"));
8537c478bd9Sstevel@tonic-gate 	if (simulation)
8547c478bd9Sstevel@tonic-gate 		(void) printf(gettext("(Simulation mode)"));
8557c478bd9Sstevel@tonic-gate 	print_n_flush("...");
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	get_media_type(target->d_fd);
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	/* DVD- requires DAO mode */
8607c478bd9Sstevel@tonic-gate 	if (device_type == DVD_MINUS) {
8617c478bd9Sstevel@tonic-gate 		write_mode = DAO_MODE;
8627c478bd9Sstevel@tonic-gate 	}
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	/* For debug, print out device config information */
8657c478bd9Sstevel@tonic-gate 	if (debug) {
8667c478bd9Sstevel@tonic-gate 		int i;
8677c478bd9Sstevel@tonic-gate 		uchar_t cap[80];
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 		if (get_configuration(target->d_fd, 0, 80, cap))
8707c478bd9Sstevel@tonic-gate 			(void) printf("Drive profile = ");
8717c478bd9Sstevel@tonic-gate 			for (i = 10; i < 70; i += 8)
8727c478bd9Sstevel@tonic-gate 				(void) printf(" 0x%x", cap[i]);
8737c478bd9Sstevel@tonic-gate 			(void) printf("\n");
8747c478bd9Sstevel@tonic-gate 	}
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	/* DVD+ and DVD- have no support for AUDIO, bail out */
8777c478bd9Sstevel@tonic-gate 	if ((mode == TRACK_MODE_AUDIO) && (device_type != CD_RW)) {
8787c478bd9Sstevel@tonic-gate 		err_msg(gettext("Audio mode is only supported for CD media\n"));
8797c478bd9Sstevel@tonic-gate 		exit(1);
8807c478bd9Sstevel@tonic-gate 	}
881a2b4fdf6Srameshc 	if (simulation &&
882a2b4fdf6Srameshc 	    check_device(target, CHECK_MEDIA_IS_NOT_BLANK) &&
883a2b4fdf6Srameshc 	    !check_device(target, CHECK_MEDIA_IS_NOT_ERASABLE) &&
884a2b4fdf6Srameshc 	    device_type != DVD_PLUS_W) {
885a2b4fdf6Srameshc 		/*
886a2b4fdf6Srameshc 		 * If we were in simulation mode, and media wasn't blank,
887a2b4fdf6Srameshc 		 * but medium was erasable, then cdrw goes to erase the
888a2b4fdf6Srameshc 		 * contents of the media after the simulation writing in order
889a2b4fdf6Srameshc 		 * to cleanup the ghost TOC (see write_fini() calls blank()).
890a2b4fdf6Srameshc 		 * This is bad because it removes existing data if media was
891a2b4fdf6Srameshc 		 * multi-session. Therefore, we no longer allow simulation
892a2b4fdf6Srameshc 		 * writing if such condition is met. we don't blank the DVD+RW
893a2b4fdf6Srameshc 		 * media, so DVD+RWs are fine.
894a2b4fdf6Srameshc 		 */
895a2b4fdf6Srameshc 		err_msg(gettext(
896a2b4fdf6Srameshc 		    "Cannot perform simulation for non-blank media\n"));
897a2b4fdf6Srameshc 		exit(1);
898a2b4fdf6Srameshc 	}
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	if (!prepare_for_write(target, mode, simulation, keep_disc_open)) {
9017c478bd9Sstevel@tonic-gate 		/* l10n_NOTE : 'failed' as in Initializing device...failed  */
9027c478bd9Sstevel@tonic-gate 		(void) printf(gettext("failed.\n"));
9037c478bd9Sstevel@tonic-gate 		err_msg(gettext("Cannot initialize device for write\n"));
9047c478bd9Sstevel@tonic-gate 		exit(1);
9057c478bd9Sstevel@tonic-gate 	}
9067c478bd9Sstevel@tonic-gate 	/* l10n_NOTE : 'done' as in "Initializing device...done"  */
9077c478bd9Sstevel@tonic-gate 	(void) printf(gettext("done.\n"));
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	/* if speed change option was used (-p) then try to set the speed */
9107c478bd9Sstevel@tonic-gate 	if (requested_speed != 0) {
9117c478bd9Sstevel@tonic-gate 		if (verbose)
9127c478bd9Sstevel@tonic-gate 			(void) printf(gettext("Trying to set speed to %dX.\n"),
9137c478bd9Sstevel@tonic-gate 			    requested_speed);
9147c478bd9Sstevel@tonic-gate 		if (target->d_speed_ctrl(target, SET_WRITE_SPEED,
9157c478bd9Sstevel@tonic-gate 		    requested_speed) == 0) {
9167c478bd9Sstevel@tonic-gate 			err_msg(gettext("Unable to set speed.\n"));
9177c478bd9Sstevel@tonic-gate 			exit(1);
9187c478bd9Sstevel@tonic-gate 		}
9197c478bd9Sstevel@tonic-gate 		if (verbose) {
9207c478bd9Sstevel@tonic-gate 			int speed;
9217c478bd9Sstevel@tonic-gate 			speed = target->d_speed_ctrl(target,
9227c478bd9Sstevel@tonic-gate 			    GET_WRITE_SPEED, 0);
9237c478bd9Sstevel@tonic-gate 			if (speed == requested_speed) {
9247c478bd9Sstevel@tonic-gate 				(void) printf(gettext("Speed set to %dX.\n"),
9257c478bd9Sstevel@tonic-gate 				    speed);
926*98584592Sarutz 			} else if (speed == 0) {
927*98584592Sarutz 				(void) printf(gettext("Could not obtain "
928*98584592Sarutz 				    "current Write Speed.\n"));
9297c478bd9Sstevel@tonic-gate 			} else {
9307c478bd9Sstevel@tonic-gate 				(void) printf(
9317c478bd9Sstevel@tonic-gate 				gettext("Speed set to closest approximation "
9327c478bd9Sstevel@tonic-gate 				    "of %dX allowed by device (%dX).\n"),
9337c478bd9Sstevel@tonic-gate 				    requested_speed, speed);
9347c478bd9Sstevel@tonic-gate 			}
9357c478bd9Sstevel@tonic-gate 		}
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate }
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate void
9407c478bd9Sstevel@tonic-gate write_fini(void)
9417c478bd9Sstevel@tonic-gate {
9427c478bd9Sstevel@tonic-gate 	print_n_flush(gettext("Finalizing (Can take several minutes)..."));
9437c478bd9Sstevel@tonic-gate 	/* Some drives don't like this while in test write mode */
9447c478bd9Sstevel@tonic-gate 	if (!simulation) {
9457c478bd9Sstevel@tonic-gate 		if (!finalize(target)) {
9467c478bd9Sstevel@tonic-gate 			/*
9477c478bd9Sstevel@tonic-gate 			 * It is possible that the drive is busy writing the
9487c478bd9Sstevel@tonic-gate 			 * buffered portion. So do not get upset yet.
9497c478bd9Sstevel@tonic-gate 			 */
9507c478bd9Sstevel@tonic-gate 			(void) sleep(10);
9517c478bd9Sstevel@tonic-gate 			if (!finalize(target)) {
9527c478bd9Sstevel@tonic-gate 				if (debug) {
9537c478bd9Sstevel@tonic-gate 					(void) printf("status %x, %x/%x/%x\n",
9547c478bd9Sstevel@tonic-gate 					    uscsi_status, SENSE_KEY(rqbuf),
9557c478bd9Sstevel@tonic-gate 					    ASC(rqbuf), ASCQ(rqbuf));
9567c478bd9Sstevel@tonic-gate 				}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 				if ((device_type == DVD_MINUS) &&
9597c478bd9Sstevel@tonic-gate 				    (SENSE_KEY(rqbuf) == 5)) {
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 					if (verbose) {
9627c478bd9Sstevel@tonic-gate 						(void) printf(
9637c478bd9Sstevel@tonic-gate 						    "skipping finalizing\n");
9647c478bd9Sstevel@tonic-gate 					}
9657c478bd9Sstevel@tonic-gate 				} else {
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate 			/* l10n_NOTE : 'failed' as in finishing up...failed  */
9687c478bd9Sstevel@tonic-gate 					(void) printf(gettext("failed.\n"));
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 					err_msg(gettext(
9717c478bd9Sstevel@tonic-gate 					    "Could not finalize the disc.\n"));
9727c478bd9Sstevel@tonic-gate 					exit(1);
9737c478bd9Sstevel@tonic-gate 				}
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 			}
9777c478bd9Sstevel@tonic-gate 		}
9787c478bd9Sstevel@tonic-gate 		if (vol_running) {
9797c478bd9Sstevel@tonic-gate 			(void) eject_media(target);
9807c478bd9Sstevel@tonic-gate 		}
9817c478bd9Sstevel@tonic-gate 	} else if (check_device(target, CHECK_MEDIA_IS_NOT_BLANK)) {
9827c478bd9Sstevel@tonic-gate 		/*
9837c478bd9Sstevel@tonic-gate 		 * Some drives such as the pioneer A04 will retain a
9847c478bd9Sstevel@tonic-gate 		 * ghost TOC after a simulation write is done. The
9857c478bd9Sstevel@tonic-gate 		 * media will actually be blank, but the drive will
9867c478bd9Sstevel@tonic-gate 		 * report a TOC. There is currently no other way to
9877c478bd9Sstevel@tonic-gate 		 * re-initialize the media other than ejecting or
9887c478bd9Sstevel@tonic-gate 		 * to ask the drive to clear the leadout. The laser
9897c478bd9Sstevel@tonic-gate 		 * is currently off so nothing is written to the
9907c478bd9Sstevel@tonic-gate 		 * media (on a good behaving drive).
9917c478bd9Sstevel@tonic-gate 		 * NOTE that a device reset does not work to make
9927c478bd9Sstevel@tonic-gate 		 * the drive re-initialize the media.
9937c478bd9Sstevel@tonic-gate 		 */
9947c478bd9Sstevel@tonic-gate 
995a2b4fdf6Srameshc 		blanking_type = "clear_ghost";
9967c478bd9Sstevel@tonic-gate 		blank();
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate 	/* l10n_NOTE : 'done' as in "Finishing up...done"  */
10007c478bd9Sstevel@tonic-gate 	(void) printf(gettext("done.\n"));
10017c478bd9Sstevel@tonic-gate }
1002