xref: /illumos-gate/usr/src/cmd/cdrw/misc_scsi.c (revision 33f5ff17)
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
54730c9c4Smikeri  * Common Development and Distribution License (the "License").
64730c9c4Smikeri  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
223503b75eSec  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*33f5ff17SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <errno.h>
347c478bd9Sstevel@tonic-gate #include <libintl.h>
357c478bd9Sstevel@tonic-gate #include <sys/time.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include "mmc.h"
387c478bd9Sstevel@tonic-gate #include "util.h"
397c478bd9Sstevel@tonic-gate #include "misc_scsi.h"
407c478bd9Sstevel@tonic-gate #include "transport.h"
417c478bd9Sstevel@tonic-gate #include "main.h"
427c478bd9Sstevel@tonic-gate #include "toshiba.h"
437c478bd9Sstevel@tonic-gate #include "msgs.h"
4498584592Sarutz #include "device.h"
457c478bd9Sstevel@tonic-gate 
463503b75eSec static int check_track_size(cd_device *dev, int trk_num,
473503b75eSec     struct track_info *tip);
483503b75eSec static int rtoc_get_trk_sess_num(uchar_t *rtoc, size_t rtoc_len, int trk_num,
493503b75eSec     int *sess_nump);
503503b75eSec static int rtoc_get_sess_last_trk_num(uchar_t *rtoc, size_t rtoc_len,
513503b75eSec     int sess_num, int *last_trk_nump);
523503b75eSec static int rtoc_get_sess_leadout_lba(uchar_t *rtoc, size_t rtoc_len,
533503b75eSec     int sess_num, uint32_t *leadout_lba);
543503b75eSec static rtoc_td_t *get_rtoc_td(rtoc_td_t *begin_tdp, rtoc_td_t *end_tdp,
553503b75eSec     uchar_t adr, uchar_t point);
563503b75eSec 
577c478bd9Sstevel@tonic-gate uint32_t
read_scsi32(void * addr)587c478bd9Sstevel@tonic-gate read_scsi32(void *addr)
597c478bd9Sstevel@tonic-gate {
607c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
617c478bd9Sstevel@tonic-gate 	uint32_t ret;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	ret = ((((uint32_t)ad[0]) << 24) | (((uint32_t)ad[1]) << 16) |
647c478bd9Sstevel@tonic-gate 	    (((uint32_t)ad[2]) << 8) | ad[3]);
657c478bd9Sstevel@tonic-gate 	return (ret);
667c478bd9Sstevel@tonic-gate }
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate uint16_t
read_scsi16(void * addr)697c478bd9Sstevel@tonic-gate read_scsi16(void *addr)
707c478bd9Sstevel@tonic-gate {
717c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
727c478bd9Sstevel@tonic-gate 	uint16_t ret;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate 	ret = ((((uint16_t)ad[0]) << 8) | ad[1]);
757c478bd9Sstevel@tonic-gate 	return (ret);
767c478bd9Sstevel@tonic-gate }
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate void
load_scsi32(void * addr,uint32_t v)797c478bd9Sstevel@tonic-gate load_scsi32(void *addr, uint32_t v)
807c478bd9Sstevel@tonic-gate {
817c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	ad[0] = (uchar_t)(v >> 24);
847c478bd9Sstevel@tonic-gate 	ad[1] = (uchar_t)(v >> 16);
857c478bd9Sstevel@tonic-gate 	ad[2] = (uchar_t)(v >> 8);
867c478bd9Sstevel@tonic-gate 	ad[3] = (uchar_t)v;
877c478bd9Sstevel@tonic-gate }
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate void
load_scsi16(void * addr,uint16_t v)907c478bd9Sstevel@tonic-gate load_scsi16(void *addr, uint16_t v)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	uchar_t *ad = (uchar_t *)addr;
937c478bd9Sstevel@tonic-gate 	ad[0] = (uchar_t)(v >> 8);
947c478bd9Sstevel@tonic-gate 	ad[1] = (uchar_t)v;
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * will get the mode page only i.e. will strip off the header.
987c478bd9Sstevel@tonic-gate  */
997c478bd9Sstevel@tonic-gate int
get_mode_page(int fd,int page_no,int pc,int buf_len,uchar_t * buffer)1007c478bd9Sstevel@tonic-gate get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	int ret;
1037c478bd9Sstevel@tonic-gate 	uchar_t byte2, *buf;
1047c478bd9Sstevel@tonic-gate 	uint_t header_len, page_len, copy_cnt;
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
1077c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(256);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	/* Ask 254 bytes only to make our IDE driver happy */
1107c478bd9Sstevel@tonic-gate 	ret = mode_sense(fd, byte2, 1, 254, buf);
1117c478bd9Sstevel@tonic-gate 	if (ret == 0) {
1127c478bd9Sstevel@tonic-gate 		free(buf);
1137c478bd9Sstevel@tonic-gate 		return (0);
1147c478bd9Sstevel@tonic-gate 	}
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	header_len = 8 + read_scsi16(&buf[6]);
1177c478bd9Sstevel@tonic-gate 	page_len = buf[header_len + 1] + 2;
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
1207c478bd9Sstevel@tonic-gate 	(void) memcpy(buffer, &buf[header_len], copy_cnt);
1217c478bd9Sstevel@tonic-gate 	free(buf);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	return (1);
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * will take care of adding mode header and any extra bytes at the end.
1287c478bd9Sstevel@tonic-gate  */
1297c478bd9Sstevel@tonic-gate int
set_mode_page(int fd,uchar_t * buffer)1307c478bd9Sstevel@tonic-gate set_mode_page(int fd, uchar_t *buffer)
1317c478bd9Sstevel@tonic-gate {
1327c478bd9Sstevel@tonic-gate 	int ret;
1337c478bd9Sstevel@tonic-gate 	uchar_t *buf;
1347c478bd9Sstevel@tonic-gate 	uint_t total, p_len;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	p_len = buffer[1] + 2;
1377c478bd9Sstevel@tonic-gate 	total = p_len + 8;
1387c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(total);
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	(void) memcpy(&buf[8], buffer, p_len);
1417c478bd9Sstevel@tonic-gate 	if (debug) {
1427c478bd9Sstevel@tonic-gate 		int i;
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 		(void) printf("MODE: [");
1457c478bd9Sstevel@tonic-gate 		for (i = 0; i < p_len; i++) {
1467c478bd9Sstevel@tonic-gate 			(void) printf("0x%02x ", (uchar_t)buffer[i]);
1477c478bd9Sstevel@tonic-gate 		}
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 		(void) printf("]\n");
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	ret = mode_select(fd, total, buf);
1527c478bd9Sstevel@tonic-gate 	free(buf);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	return (ret);
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * Builds track information database for track trackno. If trackno is
1597c478bd9Sstevel@tonic-gate  * -1, builds the database for next blank track.
1607c478bd9Sstevel@tonic-gate  */
1617c478bd9Sstevel@tonic-gate int
build_track_info(cd_device * dev,int trackno,struct track_info * t_info)1627c478bd9Sstevel@tonic-gate build_track_info(cd_device *dev, int trackno, struct track_info *t_info)
1637c478bd9Sstevel@tonic-gate {
1647c478bd9Sstevel@tonic-gate 	uchar_t *ti;
1657c478bd9Sstevel@tonic-gate 	uchar_t toc[20];		/* 2 entries + 4 byte header */
1667c478bd9Sstevel@tonic-gate 	int ret;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	(void) memset(t_info, 0, sizeof (*t_info));
1697c478bd9Sstevel@tonic-gate 	/* 1st try READ TRACK INFORMATION */
1707c478bd9Sstevel@tonic-gate 	ti = (uchar_t *)my_zalloc(TRACK_INFO_SIZE);
1717c478bd9Sstevel@tonic-gate 	t_info->ti_track_no = trackno;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	/* Gererate faked information for writing to DVD */
1747c478bd9Sstevel@tonic-gate 	if (device_type != CD_RW) {
1757c478bd9Sstevel@tonic-gate 		uint_t bsize;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		t_info->ti_flags = 0x3000;
1787c478bd9Sstevel@tonic-gate 		t_info->ti_track_no = 1;
1797c478bd9Sstevel@tonic-gate 		t_info->ti_session_no = 1;
1807c478bd9Sstevel@tonic-gate 		t_info->ti_track_mode = 0x4;
1817c478bd9Sstevel@tonic-gate 		t_info->ti_data_mode = 1;
1827c478bd9Sstevel@tonic-gate 		t_info->ti_start_address = 0;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		/* only 1 track on DVD make it max size */
1857c478bd9Sstevel@tonic-gate 		t_info->ti_track_size = read_format_capacity(target->d_fd,
1867c478bd9Sstevel@tonic-gate 		    &bsize);
1877c478bd9Sstevel@tonic-gate 		if (t_info->ti_track_size < MAX_CD_BLKS) {
1887c478bd9Sstevel@tonic-gate 			t_info->ti_track_size = MAX_DVD_BLKS;
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 		t_info->ti_nwa = 0;
1927c478bd9Sstevel@tonic-gate 		t_info->ti_lra = 0;
1937c478bd9Sstevel@tonic-gate 		t_info->ti_packet_size = 0x10;
1947c478bd9Sstevel@tonic-gate 		t_info->ti_free_blocks = 0;
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	if (read_track_info(dev->d_fd, trackno, ti)) {
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 		if (debug)
2007c478bd9Sstevel@tonic-gate 			(void) printf("using read_track_info for TOC \n");
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 		t_info->ti_track_no = ti[2];
2037c478bd9Sstevel@tonic-gate 		t_info->ti_session_no = ti[3];
2047c478bd9Sstevel@tonic-gate 		t_info->ti_flags = (ti[6] >> 4) & 0xf;
2057c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= (uint32_t)(ti[5] & 0xf0);
2067c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= (uint32_t)(ti[7]) << 8;
2077c478bd9Sstevel@tonic-gate 		t_info->ti_flags |= TI_SESSION_NO_VALID | TI_FREE_BLOCKS_VALID;
2087c478bd9Sstevel@tonic-gate 		t_info->ti_track_mode = ti[5] & 0xf;
2097c478bd9Sstevel@tonic-gate 		if ((ti[6] & 0xf) == 0xf)
2107c478bd9Sstevel@tonic-gate 			t_info->ti_data_mode = 0xff;
2117c478bd9Sstevel@tonic-gate 		else
2127c478bd9Sstevel@tonic-gate 			t_info->ti_data_mode = ti[6] & 0xf;
2137c478bd9Sstevel@tonic-gate 		t_info->ti_start_address = read_scsi32(&ti[8]);
2147c478bd9Sstevel@tonic-gate 		t_info->ti_nwa = read_scsi32(&ti[12]);
2157c478bd9Sstevel@tonic-gate 		t_info->ti_free_blocks = read_scsi32(&ti[16]);
2167c478bd9Sstevel@tonic-gate 		t_info->ti_packet_size = read_scsi32(&ti[20]);
2177c478bd9Sstevel@tonic-gate 		t_info->ti_track_size = read_scsi32(&ti[24]);
2187c478bd9Sstevel@tonic-gate 		t_info->ti_lra = read_scsi32(&ti[28]);
2197c478bd9Sstevel@tonic-gate 		free(ti);
2207c478bd9Sstevel@tonic-gate 		return (1);
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 	/* READ TRACK INFORMATION not supported, try other options */
2237c478bd9Sstevel@tonic-gate 	free(ti);
2247c478bd9Sstevel@tonic-gate 	/*
2257c478bd9Sstevel@tonic-gate 	 * We can get info for next blank track if READ TRACK INFO is not
2267c478bd9Sstevel@tonic-gate 	 * supported.
2277c478bd9Sstevel@tonic-gate 	 */
2287c478bd9Sstevel@tonic-gate 	if (trackno == -1)
2297c478bd9Sstevel@tonic-gate 		return (0);
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	if (debug)
2327c478bd9Sstevel@tonic-gate 		(void) printf("using READ_TOC for TOC\n");
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	/* Try Read TOC */
2357c478bd9Sstevel@tonic-gate 	if (!read_toc(dev->d_fd, 0, trackno, 20, toc)) {
2367c478bd9Sstevel@tonic-gate 		return (0);
2377c478bd9Sstevel@tonic-gate 	}
2387c478bd9Sstevel@tonic-gate 	t_info->ti_start_address = read_scsi32(&toc[8]);
2397c478bd9Sstevel@tonic-gate 	t_info->ti_track_mode = toc[5] & 0xf;
2407c478bd9Sstevel@tonic-gate 	t_info->ti_track_size = read_scsi32(&toc[16]) - read_scsi32(&toc[8]);
2417c478bd9Sstevel@tonic-gate 	t_info->ti_data_mode = get_data_mode(dev->d_fd, read_scsi32(&toc[8]));
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	/* Numbers for audio tracks are always in 2K chunks */
2447c478bd9Sstevel@tonic-gate 	if ((dev->d_blksize == 512) && ((t_info->ti_track_mode & 4) == 0)) {
2457c478bd9Sstevel@tonic-gate 		t_info->ti_start_address /= 4;
2467c478bd9Sstevel@tonic-gate 		t_info->ti_track_size /= 4;
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	/* Now find out the session thing */
2507c478bd9Sstevel@tonic-gate 	ret = read_toc(dev->d_fd, 1, trackno, 12, toc);
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	/*
2537c478bd9Sstevel@tonic-gate 	 * Make sure that the call succeeds and returns the requested
2547c478bd9Sstevel@tonic-gate 	 * TOC size correctly.
2557c478bd9Sstevel@tonic-gate 	 */
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	if ((ret == 0) || (toc[1] != 0x0a)) {
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 		/* For ATAPI drives or old Toshiba drives */
2607c478bd9Sstevel@tonic-gate 		ret = read_toc_as_per_8020(dev->d_fd, 1, trackno, 12, toc);
2617c478bd9Sstevel@tonic-gate 	}
2627c478bd9Sstevel@tonic-gate 	/* If this goes through well TOC length will always be 0x0a */
2637c478bd9Sstevel@tonic-gate 	if (ret && (toc[1] == 0x0a)) {
2647c478bd9Sstevel@tonic-gate 		if (trackno >= toc[6]) {
2657c478bd9Sstevel@tonic-gate 			t_info->ti_session_no = toc[3];
2667c478bd9Sstevel@tonic-gate 			t_info->ti_flags |= TI_SESSION_NO_VALID;
2677c478bd9Sstevel@tonic-gate 		}
2687c478bd9Sstevel@tonic-gate 		/*
2697c478bd9Sstevel@tonic-gate 		 * This might be the last track of this session. If so,
2707c478bd9Sstevel@tonic-gate 		 * exclude the leadout and next lead in.
2717c478bd9Sstevel@tonic-gate 		 */
2727c478bd9Sstevel@tonic-gate 		if (trackno == (toc[6] - 1)) {
2737c478bd9Sstevel@tonic-gate 			/*
2747c478bd9Sstevel@tonic-gate 			 * 1.5 Min leadout + 1 min. leadin + 2 sec. pre-gap.
2757c478bd9Sstevel@tonic-gate 			 * For 2nd+ leadout it will be 0.5 min. But currently
2767c478bd9Sstevel@tonic-gate 			 * there is no direct way. And it will not happen
2777c478bd9Sstevel@tonic-gate 			 * for any normal case.
2787c478bd9Sstevel@tonic-gate 			 *
2797c478bd9Sstevel@tonic-gate 			 * 75 frames/sec, 60 sec/min, so leadin gap is
2807c478bd9Sstevel@tonic-gate 			 * ((1.5 +1)*60 + 2)*75 = 11400 frames (blocks)
2817c478bd9Sstevel@tonic-gate 			 */
2827c478bd9Sstevel@tonic-gate 			t_info->ti_track_size -= 11400;
2837c478bd9Sstevel@tonic-gate 		}
2843503b75eSec 	} else {
2853503b75eSec 		if (check_track_size(dev, trackno, t_info) != 1)
2863503b75eSec 			return (0);
2873503b75eSec 	}
2883503b75eSec 
2893503b75eSec 	return (1);
2903503b75eSec }
2913503b75eSec 
2923503b75eSec /*
2933503b75eSec  * The size of the last track in one of the first N - 1 sessions of an
2943503b75eSec  * N-session (N > 1) disc is reported incorrectly by some drives and calculated
2953503b75eSec  * incorrectly for others, because a pre-gap/lead-out/lead-in section that ends
2963503b75eSec  * a session is erroneously considered part of that track. This function checks
2973503b75eSec  * for this corner case, and adjusts the track size if necessary.
2983503b75eSec  */
2993503b75eSec static int
check_track_size(cd_device * dev,int trk_num,struct track_info * tip)3003503b75eSec check_track_size(cd_device *dev, int trk_num, struct track_info *tip)
3013503b75eSec {
3023503b75eSec 	size_t raw_toc_len;
3033503b75eSec 	uchar_t *raw_toc;
3043503b75eSec 	rtoc_hdr_t hdr;
3053503b75eSec 	uint32_t sess_leadout_lba;
3063503b75eSec 	int sess_last_trk_num;
3073503b75eSec 	int trk_sess_num;
3083503b75eSec 	uint32_t trk_size;
3093503b75eSec 
3103503b75eSec 	/* Request Raw TOC Header for session count. */
3113503b75eSec 	if (read_toc(dev->d_fd, FORMAT_RAW_TOC, 1,
3123503b75eSec 	    sizeof (rtoc_hdr_t), (uchar_t *)&hdr) != 1)
3133503b75eSec 		return (0);
3143503b75eSec 
3153503b75eSec 	/* Is this a multi-session medium? */
3163503b75eSec 	if (hdr.rh_last_sess_num > hdr.rh_first_sess_num) {
3173503b75eSec 		/* Yes; request entire Raw TOC. */
3183503b75eSec 		raw_toc_len = read_scsi16(&hdr.rh_data_len1) + RTOC_DATA_LEN_SZ;
3193503b75eSec 		raw_toc = (uchar_t *)my_zalloc(raw_toc_len);
3203503b75eSec 
3213503b75eSec 		if (read_toc(dev->d_fd, FORMAT_RAW_TOC, 1, raw_toc_len, raw_toc)
3223503b75eSec 		    != 1)
3233503b75eSec 			goto fail;
3243503b75eSec 
3253503b75eSec 		if (rtoc_get_trk_sess_num(raw_toc, raw_toc_len, trk_num,
3263503b75eSec 		    &trk_sess_num) != 1)
3273503b75eSec 			goto fail;
3283503b75eSec 
3293503b75eSec 		tip->ti_session_no = trk_sess_num;
3303503b75eSec 		tip->ti_flags |= TI_SESSION_NO_VALID;
3313503b75eSec 
3323503b75eSec 		/* Is the track in one of the first N - 1 sessions? */
3333503b75eSec 		if (trk_sess_num < hdr.rh_last_sess_num) {
3343503b75eSec 			if (rtoc_get_sess_last_trk_num(raw_toc, raw_toc_len,
3353503b75eSec 			    trk_sess_num, &sess_last_trk_num) != 1)
3363503b75eSec 				goto fail;
3373503b75eSec 
3383503b75eSec 			/* Is the track the last track in the session? */
3393503b75eSec 			if (trk_num == sess_last_trk_num) {
3403503b75eSec 				if (rtoc_get_sess_leadout_lba(raw_toc,
3413503b75eSec 				    raw_toc_len, trk_sess_num,
3423503b75eSec 				    &sess_leadout_lba) != 1)
3433503b75eSec 					goto fail;
3443503b75eSec 
3453503b75eSec 				trk_size = sess_leadout_lba -
3463503b75eSec 				    tip->ti_start_address;
3473503b75eSec 
3483503b75eSec 				/* Fix track size if it was too big. */
3493503b75eSec 				if (tip->ti_track_size > trk_size)
3503503b75eSec 					tip->ti_track_size = trk_size;
3513503b75eSec 			}
3523503b75eSec 		}
3533503b75eSec 		free(raw_toc);
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 	return (1);
3563503b75eSec 
3573503b75eSec fail:
3583503b75eSec 	free(raw_toc);
3593503b75eSec 	return (0);
3603503b75eSec }
3613503b75eSec 
3623503b75eSec /*
3633503b75eSec  * Determine what session number a track is in by parsing the Raw TOC format of
3643503b75eSec  * the the READ TOC/PMA/ATIP command response data.
3653503b75eSec  */
3663503b75eSec static int
rtoc_get_trk_sess_num(uchar_t * rtoc,size_t rtoc_len,int trk_num,int * sess_nump)3673503b75eSec rtoc_get_trk_sess_num(uchar_t *rtoc, size_t rtoc_len, int trk_num,
3683503b75eSec     int *sess_nump)
3693503b75eSec {
3703503b75eSec 	rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t));
3713503b75eSec 	rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len -
3723503b75eSec 	    sizeof (rtoc_td_t));
3733503b75eSec 
3743503b75eSec 	if ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1, (uchar_t)trk_num)) !=
3753503b75eSec 	    NULL) {
3763503b75eSec 		*sess_nump = tdp->rt_session_num;
3773503b75eSec 		return (1);
3783503b75eSec 	} else
3793503b75eSec 		return (0);
3803503b75eSec }
3813503b75eSec 
3823503b75eSec /*
3833503b75eSec  * Determine the last track number in a specified session number by parsing the
3843503b75eSec  * Raw TOC format of the READ TOC/PMA/ATIP command response data.
3853503b75eSec  */
3863503b75eSec static int
rtoc_get_sess_last_trk_num(uchar_t * rtoc,size_t rtoc_len,int sess_num,int * last_trk_nump)3873503b75eSec rtoc_get_sess_last_trk_num(uchar_t *rtoc, size_t rtoc_len, int sess_num,
3883503b75eSec     int *last_trk_nump)
3893503b75eSec {
3903503b75eSec 	rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t));
3913503b75eSec 	rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len -
3923503b75eSec 	    sizeof (rtoc_td_t));
3933503b75eSec 
3943503b75eSec 	while ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1,
3953503b75eSec 	    POINT_SESS_LAST_TRK)) != NULL) {
3963503b75eSec 		if (tdp->rt_session_num == sess_num) {
3973503b75eSec 			*last_trk_nump = tdp->rt_pmin;
3983503b75eSec 			return (1);
3993503b75eSec 		} else {
4003503b75eSec 			++tdp;
4013503b75eSec 		}
4023503b75eSec 	}
4033503b75eSec 
4043503b75eSec 	return (0);
4053503b75eSec }
4063503b75eSec 
4073503b75eSec /*
4083503b75eSec  * Determine the starting LBA of the the session leadout by parsing the Raw TOC
4093503b75eSec  * format of the READ TOC/PMA/ATIP command response data.
4103503b75eSec  */
4113503b75eSec static int
rtoc_get_sess_leadout_lba(uchar_t * rtoc,size_t rtoc_len,int sess_num,uint32_t * leadout_lba)4123503b75eSec rtoc_get_sess_leadout_lba(uchar_t *rtoc, size_t rtoc_len, int sess_num,
4133503b75eSec     uint32_t *leadout_lba)
4143503b75eSec {
4153503b75eSec 	rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t));
4163503b75eSec 	rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len -
4173503b75eSec 	    sizeof (rtoc_td_t));
4183503b75eSec 
4193503b75eSec 	while ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1,
4203503b75eSec 	    POINT_LEADOUT_ADDR)) != NULL) {
4213503b75eSec 		if (tdp->rt_session_num == sess_num) {
4223503b75eSec 			*leadout_lba = MSF2LBA(tdp->rt_pmin, tdp->rt_psec,
4233503b75eSec 			    tdp->rt_pframe);
4243503b75eSec 			return (1);
4253503b75eSec 		} else {
4263503b75eSec 			++tdp;
4273503b75eSec 		}
4283503b75eSec 	}
4293503b75eSec 
4303503b75eSec 	return (0);
4313503b75eSec }
4323503b75eSec 
4333503b75eSec /*
4343503b75eSec  * Search a set of Raw TOC Track Descriptors using <'adr', 'point'> as the
4353503b75eSec  * search key. Return a pointer to the first Track Descriptor that matches.
4363503b75eSec  */
4373503b75eSec static rtoc_td_t *
get_rtoc_td(rtoc_td_t * begin_tdp,rtoc_td_t * end_tdp,uchar_t adr,uchar_t point)4383503b75eSec get_rtoc_td(rtoc_td_t *begin_tdp, rtoc_td_t *end_tdp, uchar_t adr,
4393503b75eSec     uchar_t point)
4403503b75eSec {
4413503b75eSec 	rtoc_td_t *cur_tdp = begin_tdp;
4423503b75eSec 
4433503b75eSec 	while (cur_tdp <= end_tdp) {
4443503b75eSec 		if ((cur_tdp->rt_adr == adr) && (cur_tdp->rt_point == point))
4453503b75eSec 			return (cur_tdp);
4463503b75eSec 		else
4473503b75eSec 			cur_tdp++;
4483503b75eSec 	}
4493503b75eSec 
4503503b75eSec 	return (NULL);
4517c478bd9Sstevel@tonic-gate }
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate uchar_t
get_data_mode(int fd,uint32_t lba)4547c478bd9Sstevel@tonic-gate get_data_mode(int fd, uint32_t lba)
4557c478bd9Sstevel@tonic-gate {
4567c478bd9Sstevel@tonic-gate 	int ret;
4577c478bd9Sstevel@tonic-gate 	uchar_t *buf;
4587c478bd9Sstevel@tonic-gate 	uchar_t mode;
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(8);
4617c478bd9Sstevel@tonic-gate 	ret = read_header(fd, lba, buf);
4627c478bd9Sstevel@tonic-gate 	if (ret == 0)
4637c478bd9Sstevel@tonic-gate 		mode = 0xff;
4647c478bd9Sstevel@tonic-gate 	else
4657c478bd9Sstevel@tonic-gate 		mode = buf[0];
4667c478bd9Sstevel@tonic-gate 	free(buf);
4677c478bd9Sstevel@tonic-gate 	return (mode);
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate  * Set page code 5 for TAO mode.
4727c478bd9Sstevel@tonic-gate  */
4737c478bd9Sstevel@tonic-gate int
prepare_for_write(cd_device * dev,int track_mode,int test_write,int keep_disc_open)4747c478bd9Sstevel@tonic-gate prepare_for_write(cd_device *dev, int track_mode, int test_write,
4757c478bd9Sstevel@tonic-gate     int keep_disc_open)
4767c478bd9Sstevel@tonic-gate {
4777c478bd9Sstevel@tonic-gate 	uchar_t *buf;
4787c478bd9Sstevel@tonic-gate 	int no_err;
4797c478bd9Sstevel@tonic-gate 	int reset_device;
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	if ((write_mode == DAO_MODE) && keep_disc_open) {
4827c478bd9Sstevel@tonic-gate 		(void) printf(gettext(
4837c478bd9Sstevel@tonic-gate 		    "Multi-session is not supported on DVD media\n"));
4847c478bd9Sstevel@tonic-gate 		exit(1);
4857c478bd9Sstevel@tonic-gate 	}
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	if ((write_mode == DAO_MODE) && debug) {
4887c478bd9Sstevel@tonic-gate 		(void) printf("Preparing to write in DAO\n");
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	(void) start_stop(dev->d_fd, 1);
4927c478bd9Sstevel@tonic-gate 	/* Some drives do not support this command but still do it */
4937c478bd9Sstevel@tonic-gate 	(void) rezero_unit(dev->d_fd);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)my_zalloc(64);
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	no_err = get_mode_page(dev->d_fd, 5, 0, 64, buf);
4987c478bd9Sstevel@tonic-gate 	if (no_err)
4997c478bd9Sstevel@tonic-gate 		no_err = ((buf[1] + 2) > 64) ? 0 : 1;
5007c478bd9Sstevel@tonic-gate 	/*
5017c478bd9Sstevel@tonic-gate 	 * If the device is already in simulation mode and again a
5027c478bd9Sstevel@tonic-gate 	 * simulation is requested, then set the device in non-simulation
5037c478bd9Sstevel@tonic-gate 	 * 1st and then take it to simulation mode. This will flush any
5047c478bd9Sstevel@tonic-gate 	 * previous fake state in the drive.
5057c478bd9Sstevel@tonic-gate 	 */
5067c478bd9Sstevel@tonic-gate 	if (no_err && test_write && (buf[2] & 0x10)) {
5077c478bd9Sstevel@tonic-gate 		reset_device = 1;
5087c478bd9Sstevel@tonic-gate 	} else {
5097c478bd9Sstevel@tonic-gate 		reset_device = 0;
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 	if (no_err != 0) {
5127c478bd9Sstevel@tonic-gate 		buf[0] &= 0x3f;
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 		/* set TAO or DAO writing mode */
5157c478bd9Sstevel@tonic-gate 		buf[2] = (write_mode == TAO_MODE)?1:2;
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 		/* set simulation flag */
5187c478bd9Sstevel@tonic-gate 		if (test_write && (!reset_device)) {
5197c478bd9Sstevel@tonic-gate 			buf[2] |= 0x10;
5207c478bd9Sstevel@tonic-gate 		} else {
5217c478bd9Sstevel@tonic-gate 			buf[2] &= ~0x10;
5227c478bd9Sstevel@tonic-gate 		}
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 		/* Turn on HW buffer underrun protection (BUFE) */
5257c478bd9Sstevel@tonic-gate 		if (!test_write) {
5267c478bd9Sstevel@tonic-gate 			buf[2] |= 0x40;
5277c478bd9Sstevel@tonic-gate 		}
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 		/* set track mode type */
5307c478bd9Sstevel@tonic-gate 		if (device_type == CD_RW) {
5317c478bd9Sstevel@tonic-gate 			buf[3] = track_mode & 0x0f;	/* ctrl nibble */
5327c478bd9Sstevel@tonic-gate 		}
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 		if (keep_disc_open) {
5357c478bd9Sstevel@tonic-gate 			buf[3] |= 0xc0;		/* Allow more sessions */
5367c478bd9Sstevel@tonic-gate 		}
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 		/* Select track type (audio or data) */
5397c478bd9Sstevel@tonic-gate 		if (track_mode == TRACK_MODE_DATA) {
5407c478bd9Sstevel@tonic-gate 			buf[4] = 8;		/* 2048 byte sector */
5417c478bd9Sstevel@tonic-gate 		} else {
5427c478bd9Sstevel@tonic-gate 			buf[4] = 0;		/* 2352 byte sector */
5437c478bd9Sstevel@tonic-gate 		}
5447c478bd9Sstevel@tonic-gate 		buf[7] = buf[8] = 0;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 		/* Need to clear these fields for setting into DAO */
5477c478bd9Sstevel@tonic-gate 		if (write_mode == DAO_MODE)
5487c478bd9Sstevel@tonic-gate 			buf[5] = buf[15] = 0;
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 		/* print out mode for detailed log */
5517c478bd9Sstevel@tonic-gate 		if (debug && verbose) {
5527c478bd9Sstevel@tonic-gate 			int i;
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 			(void) printf("setting = [ ");
5557c478bd9Sstevel@tonic-gate 			for (i = 0; i < 15; i++)
5567c478bd9Sstevel@tonic-gate 				(void) printf("0x%x ", buf[i]);
5577c478bd9Sstevel@tonic-gate 			(void) printf("]\n");
5587c478bd9Sstevel@tonic-gate 		}
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 		no_err = set_mode_page(dev->d_fd, buf);
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 		if (no_err && reset_device) {
5637c478bd9Sstevel@tonic-gate 			/* Turn the test write bit back on */
5647c478bd9Sstevel@tonic-gate 			buf[2] |= 0x10;
5657c478bd9Sstevel@tonic-gate 			no_err = set_mode_page(dev->d_fd, buf);
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		/*
5697c478bd9Sstevel@tonic-gate 		 * Since BUFE is the only optional flag we are
5707c478bd9Sstevel@tonic-gate 		 * setting we will try to turn it off if the command
5717c478bd9Sstevel@tonic-gate 		 * fails.
5727c478bd9Sstevel@tonic-gate 		 */
5737c478bd9Sstevel@tonic-gate 		if (!no_err) {
5747c478bd9Sstevel@tonic-gate 			/*
5757c478bd9Sstevel@tonic-gate 			 * Some old drives may not support HW
5767c478bd9Sstevel@tonic-gate 			 * buffer underrun protection, try again
5777c478bd9Sstevel@tonic-gate 			 * after turning it off.
5787c478bd9Sstevel@tonic-gate 			 */
5797c478bd9Sstevel@tonic-gate 			if (debug)
5807c478bd9Sstevel@tonic-gate 				(void) printf("Turning off BUFE\n");
5817c478bd9Sstevel@tonic-gate 			buf[2] &= ~0x40;
5827c478bd9Sstevel@tonic-gate 			no_err = set_mode_page(dev->d_fd, buf);
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	free(buf);
5877c478bd9Sstevel@tonic-gate 	return (no_err);
5887c478bd9Sstevel@tonic-gate }
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate /*
5917c478bd9Sstevel@tonic-gate  * Close session. This will write TOC.
5927c478bd9Sstevel@tonic-gate  */
5937c478bd9Sstevel@tonic-gate int
finalize(cd_device * dev)5947c478bd9Sstevel@tonic-gate finalize(cd_device *dev)
5957c478bd9Sstevel@tonic-gate {
5967c478bd9Sstevel@tonic-gate 	uchar_t *di;
5977c478bd9Sstevel@tonic-gate 	int count, ret, err;
5987c478bd9Sstevel@tonic-gate 	int immediate;
5997c478bd9Sstevel@tonic-gate 	int finalize_max;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/*
6027c478bd9Sstevel@tonic-gate 	 * For ATAPI devices we will use the immediate mode and will
6037c478bd9Sstevel@tonic-gate 	 * poll the command for completion so that this command may
6047c478bd9Sstevel@tonic-gate 	 * not hog the channel. But for SCSI, we will use the treditional
6057c478bd9Sstevel@tonic-gate 	 * way of issuing the command with a large enough timeout. This
6067c478bd9Sstevel@tonic-gate 	 * is done because immediate mode was designed for ATAPI and some
6077c478bd9Sstevel@tonic-gate 	 * SCSI RW drives might not be even tested with it.
6087c478bd9Sstevel@tonic-gate 	 */
6097c478bd9Sstevel@tonic-gate 	if ((dev->d_inq[2] & 7) != 0) {
6107c478bd9Sstevel@tonic-gate 		/* SCSI device */
6117c478bd9Sstevel@tonic-gate 		immediate = 0;
6127c478bd9Sstevel@tonic-gate 	} else {
6137c478bd9Sstevel@tonic-gate 		/* non-SCSI (e.g ATAPI) device */
6147c478bd9Sstevel@tonic-gate 		immediate = 1;
6157c478bd9Sstevel@tonic-gate 	}
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	/* We need to close track before close session */
6187c478bd9Sstevel@tonic-gate 	if (device_type == DVD_PLUS) {
6197c478bd9Sstevel@tonic-gate 		if (!close_track(dev->d_fd, 0, 0, immediate))
6207c478bd9Sstevel@tonic-gate 			return (0);
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	if (!close_track(dev->d_fd, 0, 1, immediate)) {
6247c478bd9Sstevel@tonic-gate 		/*
6255d465cc7Sminht 		 * For DAO mode which we use for DVD-RW, the latest MMC
6265d465cc7Sminht 		 * specification does not mention close_track. Some
6275d465cc7Sminht 		 * newer drives will return an ILLEGAL INSTRUCTION
6285d465cc7Sminht 		 * which we will ignore. We have also found a Panasonic
6295d465cc7Sminht 		 * drive which will return a MEDIA ERROR. It is safe
6305d465cc7Sminht 		 * to ignore both errors as this is not needed for
6315d465cc7Sminht 		 * these drives.
6325d465cc7Sminht 		 * This is kept for older drives which had needed
6335d465cc7Sminht 		 * us to issue close_track to flush the cache fully.
6345d465cc7Sminht 		 * once we are certain these drives have cleared the
6355d465cc7Sminht 		 * market, this can be removed.
6367c478bd9Sstevel@tonic-gate 		 */
6377c478bd9Sstevel@tonic-gate 		if (device_type == DVD_MINUS) {
6387c478bd9Sstevel@tonic-gate 			return (0);
6397c478bd9Sstevel@tonic-gate 		}
6407c478bd9Sstevel@tonic-gate 	} else {
6417c478bd9Sstevel@tonic-gate 		if (!immediate)
6427c478bd9Sstevel@tonic-gate 			return (1);
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate 	if (immediate) {
6457c478bd9Sstevel@tonic-gate 		(void) sleep(10);
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 		di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
6487c478bd9Sstevel@tonic-gate 		err = 0;
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 		if (device_type == CD_RW) {
6517c478bd9Sstevel@tonic-gate 			/* Finalization should not take more than 6 minutes */
6527c478bd9Sstevel@tonic-gate 			finalize_max = FINALIZE_TIMEOUT;
6537c478bd9Sstevel@tonic-gate 		} else {
6547c478bd9Sstevel@tonic-gate 			/* some DVD-RW drives take longer than 6 minutes */
6557c478bd9Sstevel@tonic-gate 			finalize_max = FINALIZE_TIMEOUT*2;
6567c478bd9Sstevel@tonic-gate 		}
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 		for (count = 0; count < finalize_max; count++) {
6597c478bd9Sstevel@tonic-gate 			ret = read_disc_info(dev->d_fd, di);
6607c478bd9Sstevel@tonic-gate 			if (ret != 0)
6617c478bd9Sstevel@tonic-gate 				break;
6627c478bd9Sstevel@tonic-gate 			if (uscsi_status != 2)
6637c478bd9Sstevel@tonic-gate 				err = 1;
6647c478bd9Sstevel@tonic-gate 			if (SENSE_KEY(rqbuf) == 2) {
6657c478bd9Sstevel@tonic-gate 				/* not ready but not becoming ready */
6667c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) != 4)
6677c478bd9Sstevel@tonic-gate 					err = 1;
6687c478bd9Sstevel@tonic-gate 			} else if (SENSE_KEY(rqbuf) == 5) {
6697c478bd9Sstevel@tonic-gate 				/* illegal mode for this track */
6707c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) != 0x64)
6717c478bd9Sstevel@tonic-gate 					err = 1;
6727c478bd9Sstevel@tonic-gate 			} else {
6737c478bd9Sstevel@tonic-gate 				err = 1;
6747c478bd9Sstevel@tonic-gate 			}
6757c478bd9Sstevel@tonic-gate 			if (err == 1) {
6767c478bd9Sstevel@tonic-gate 				if (debug) {
6777c478bd9Sstevel@tonic-gate 					(void) printf("Finalization failed\n");
6787c478bd9Sstevel@tonic-gate 					(void) printf("%x %x %x %x\n",
6797c478bd9Sstevel@tonic-gate 					    uscsi_status, SENSE_KEY(rqbuf),
6807c478bd9Sstevel@tonic-gate 					    ASC(rqbuf), ASCQ(rqbuf));
6817c478bd9Sstevel@tonic-gate 				}
6827c478bd9Sstevel@tonic-gate 				free(di);
6837c478bd9Sstevel@tonic-gate 				return (0);
6847c478bd9Sstevel@tonic-gate 			}
6857c478bd9Sstevel@tonic-gate 			if (uscsi_status == 2) {
6867c478bd9Sstevel@tonic-gate 				int i;
6877c478bd9Sstevel@tonic-gate 				/* illegal field in command packet */
6887c478bd9Sstevel@tonic-gate 				if (ASC(rqbuf) == 0x24) {
6897c478bd9Sstevel@tonic-gate 					/* print it out! */
6907c478bd9Sstevel@tonic-gate 					(void) printf("\n");
6917c478bd9Sstevel@tonic-gate 					for (i = 0; i < 18; i++)
6927c478bd9Sstevel@tonic-gate 						(void) printf("%x ",
6937c478bd9Sstevel@tonic-gate 						    (unsigned)(rqbuf[i]));
6947c478bd9Sstevel@tonic-gate 					(void) printf("\n");
6957c478bd9Sstevel@tonic-gate 				}
6967c478bd9Sstevel@tonic-gate 			}
6977c478bd9Sstevel@tonic-gate 			(void) sleep(5);
6987c478bd9Sstevel@tonic-gate 		}
6997c478bd9Sstevel@tonic-gate 		free(di);
7007c478bd9Sstevel@tonic-gate 	}
7017c478bd9Sstevel@tonic-gate 	return (ret);
7027c478bd9Sstevel@tonic-gate }
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate /*
7057c478bd9Sstevel@tonic-gate  * Find out media capacity.
7067c478bd9Sstevel@tonic-gate  */
7074d218355Szk uint32_t
get_last_possible_lba(cd_device * dev)7087c478bd9Sstevel@tonic-gate get_last_possible_lba(cd_device *dev)
7097c478bd9Sstevel@tonic-gate {
7107c478bd9Sstevel@tonic-gate 	uchar_t *di;
7114d218355Szk 	uint32_t cap;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
7147c478bd9Sstevel@tonic-gate 	if (!read_disc_info(dev->d_fd, di)) {
7157c478bd9Sstevel@tonic-gate 		free(di);
7167c478bd9Sstevel@tonic-gate 		return (0);
7177c478bd9Sstevel@tonic-gate 	}
7184d218355Szk 
7194d218355Szk 	/*
7204d218355Szk 	 * If we have a DVD+R this field is an LBA. If the media is
7214d218355Szk 	 * a CD-R/W the field is MSF formatted. Otherwise this field
7224d218355Szk 	 * is not valid and will be zero.
7234d218355Szk 	 */
7244d218355Szk 	if (device_type == DVD_PLUS) {
7254d218355Szk 		if (read_scsi32(&di[20]) != 0xffffffff) {
7264d218355Szk 			cap = read_scsi32(&di[20]);
7274d218355Szk 		} else {
7284d218355Szk 			cap = 0;
7294d218355Szk 		}
7307c478bd9Sstevel@tonic-gate 	} else {
7314d218355Szk 		if ((di[21] != 0) && (di[21] != 0xff)) {
7329103ebf4Szk 			cap = MSF2LBA(di[21], di[22], di[23]);
7334d218355Szk 		} else {
7344d218355Szk 			cap = 0;
7354d218355Szk 		}
7367c478bd9Sstevel@tonic-gate 	}
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	free(di);
7397c478bd9Sstevel@tonic-gate 	return (cap);
7407c478bd9Sstevel@tonic-gate }
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate int
read_audio_through_read_cd(cd_device * dev,uint_t start_lba,uint_t nblks,uchar_t * buf)7437c478bd9Sstevel@tonic-gate read_audio_through_read_cd(cd_device *dev, uint_t start_lba, uint_t nblks,
7447c478bd9Sstevel@tonic-gate     uchar_t *buf)
7457c478bd9Sstevel@tonic-gate {
7467c478bd9Sstevel@tonic-gate 	int retry;
7477c478bd9Sstevel@tonic-gate 	int ret;
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	for (retry = 0; retry < 3; retry++) {
7507c478bd9Sstevel@tonic-gate 		ret = read_cd(dev->d_fd, (uint32_t)start_lba, (uint16_t)nblks,
7517c478bd9Sstevel@tonic-gate 		    1, buf, (uint32_t)(nblks * 2352));
7527c478bd9Sstevel@tonic-gate 		if (ret)
7537c478bd9Sstevel@tonic-gate 			break;
7547c478bd9Sstevel@tonic-gate 	}
7557c478bd9Sstevel@tonic-gate 	return (ret);
7567c478bd9Sstevel@tonic-gate }
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate int
eject_media(cd_device * dev)7597c478bd9Sstevel@tonic-gate eject_media(cd_device *dev)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	if (vol_running) {
7627c478bd9Sstevel@tonic-gate 		/* If there is a media, try using DKIOCEJECT 1st */
7637c478bd9Sstevel@tonic-gate 		if (check_device(dev, CHECK_NO_MEDIA) == 0) {
764b36bd5c0Szk 			/*
765b36bd5c0Szk 			 * The check_device() call will issue
766b36bd5c0Szk 			 * a TEST UNIT READY (TUR) and retry many
767b36bd5c0Szk 			 * times when a DVD-R is present. The DKIOCEJECT
768b36bd5c0Szk 			 * ioctl will subsequently fail causing us to
769b36bd5c0Szk 			 * issue the LOAD/UNLOAD SCSI command to the device
770b36bd5c0Szk 			 * with out ejecting the media. Insted of letting
771b36bd5c0Szk 			 * this happen, issue a reset to the device before
772b36bd5c0Szk 			 * issuing the DKIOCEJCET ioctl.
773b36bd5c0Szk 			 */
774b36bd5c0Szk 			if (device_type == DVD_MINUS)
775b36bd5c0Szk 				reset_dev(dev->d_fd);
776b36bd5c0Szk 
7777c478bd9Sstevel@tonic-gate 			if (ioctl(dev->d_fd, DKIOCEJECT, 0) == 0) {
7787c478bd9Sstevel@tonic-gate 				return (1);
7797c478bd9Sstevel@tonic-gate 			}
7807c478bd9Sstevel@tonic-gate 		}
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate 	if (load_unload(dev->d_fd, 0) == 0) {
7837c478bd9Sstevel@tonic-gate 		/* if eject fails */
7847c478bd9Sstevel@tonic-gate 		if ((uscsi_status == 2) && (ASC(rqbuf) == 0x53)) {
7857c478bd9Sstevel@tonic-gate 			/*
7867c478bd9Sstevel@tonic-gate 			 * check that eject is not blocked on the device
7877c478bd9Sstevel@tonic-gate 			 */
7887c478bd9Sstevel@tonic-gate 			if (!prevent_allow_mr(dev->d_fd, 1))
7897c478bd9Sstevel@tonic-gate 				return (0);
7907c478bd9Sstevel@tonic-gate 			return (load_unload(dev->d_fd, 0));
7917c478bd9Sstevel@tonic-gate 		}
7927c478bd9Sstevel@tonic-gate 		return (0);
7937c478bd9Sstevel@tonic-gate 	}
7947c478bd9Sstevel@tonic-gate 	return (1);
7957c478bd9Sstevel@tonic-gate }
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate /*
79898584592Sarutz  * Get current Read or Write Speed from Mode Page 0x2a.
79998584592Sarutz  *
80098584592Sarutz  * Use the size of the Page to determine which Multimedia Command
80198584592Sarutz  * set (MMC) is present.  Based on the MMC version, get the
80298584592Sarutz  * specified Read/Write Speed.
80398584592Sarutz  *
80498584592Sarutz  * Note that some MMC versions do not necessarily support a
80598584592Sarutz  * (current) Read or Write Speed.  As a result, this function
80698584592Sarutz  * _can_ return a value of zero.
80798584592Sarutz  *
80898584592Sarutz  * The newer standards (reserve and) mark the field(s) as Obsolete,
80998584592Sarutz  * yet many vendors populate the Obsolete fields with valid values
81098584592Sarutz  * (assumedly for backward compatibility).  This is important, as
81198584592Sarutz  * a command like GET PERFORMANCE cannot return _the_ speed; it can
81298584592Sarutz  * only return a Logical-Block-Address-dependent (LBA) speed.  Such
81398584592Sarutz  * values can vary widely between the innermost and outermost Track.
81498584592Sarutz  * Mode Page 0x2a is the best solution identifying "the current
81598584592Sarutz  * (nominal) speed".
8167c478bd9Sstevel@tonic-gate  */
8177c478bd9Sstevel@tonic-gate static uint16_t
cd_speed_get(cd_device * dev,int cmd)81898584592Sarutz cd_speed_get(cd_device *dev, int cmd)
8197c478bd9Sstevel@tonic-gate {
82098584592Sarutz 	uchar_t		*mp2a;
82198584592Sarutz 	uint16_t	rate = 0;
82298584592Sarutz 	int		offset;
82398584592Sarutz 	uint_t		buflen = 254;
82498584592Sarutz 
82598584592Sarutz 	/*
82698584592Sarutz 	 * Allocate a buffer acceptably larger than any nominal
82798584592Sarutz 	 * Page for Page Code 0x2A.
82898584592Sarutz 	 */
82998584592Sarutz 	mp2a = (uchar_t *)my_zalloc(buflen);
83098584592Sarutz 	if (get_mode_page(dev->d_fd, 0x2A, 0, buflen, mp2a) == 0)
83198584592Sarutz 		goto end;
83298584592Sarutz 
83398584592Sarutz 	/* Determine MMC version based on 'Page Length' field */
83498584592Sarutz 	switch (mp2a[1]) {
83598584592Sarutz 	case 0x14:  /* MMC-1 */
83698584592Sarutz 		if (debug)
83798584592Sarutz 			(void) printf("Mode Page 2A: MMC-1\n");
83898584592Sarutz 
83998584592Sarutz 		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
84098584592Sarutz 		rate = read_scsi16(&mp2a[offset]);
84198584592Sarutz 		break;
84298584592Sarutz 
84398584592Sarutz 
84498584592Sarutz 	case 0x18: /* MMC-2 */
84598584592Sarutz 		if (debug)
84698584592Sarutz 			(void) printf("Mode Page 2A: MMC-2;"
84798584592Sarutz 			    " Read and Write Speeds are "
84898584592Sarutz 			    "obsolete\n");
84998584592Sarutz 
85098584592Sarutz 		/* see if "Obsolete" values are valid: */
85198584592Sarutz 		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
85298584592Sarutz 		rate = read_scsi16(&mp2a[offset]);
85398584592Sarutz 		break;
85498584592Sarutz 
85598584592Sarutz 	default: /* MMC-3 or newer */
85698584592Sarutz 		if (debug)
85798584592Sarutz 			(void) printf("Mode Page 2A: MMC-3 or"
85898584592Sarutz 			    " newer; Read Speed is obsolete.\n");
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 		if (cmd == GET_READ_SPEED) {
86198584592Sarutz 			/* this is Obsolete, but try it */
86298584592Sarutz 			offset = 14;
86398584592Sarutz 			rate = read_scsi16(&mp2a[offset]);
8647c478bd9Sstevel@tonic-gate 		} else {
86598584592Sarutz 			/* Write Speed is not obsolete */
86698584592Sarutz 			offset = 28;
86798584592Sarutz 			rate = read_scsi16(&mp2a[offset]);
86898584592Sarutz 
86998584592Sarutz 			if (rate == 0) {
87098584592Sarutz 				/*
87198584592Sarutz 				 * then try an Obsolete field
87298584592Sarutz 				 * (but this shouldn't happen!)
87398584592Sarutz 				 */
87498584592Sarutz 				offset = 20;
87598584592Sarutz 				rate = read_scsi16(&mp2a[offset]);
87698584592Sarutz 			}
8777c478bd9Sstevel@tonic-gate 		}
87898584592Sarutz 		break;
8797c478bd9Sstevel@tonic-gate 	}
88098584592Sarutz end:
8817c478bd9Sstevel@tonic-gate 	free(mp2a);
88298584592Sarutz 
88398584592Sarutz 	if (debug)
88498584592Sarutz 		(void) printf("cd_speed_get: %s Speed is "
88598584592Sarutz 		    "%uX\n", (cmd == GET_READ_SPEED) ?
88698584592Sarutz 		    "Read" : "Write", cdrw_bandwidth_to_x(rate));
8877c478bd9Sstevel@tonic-gate 	return (rate);
8887c478bd9Sstevel@tonic-gate }
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate /*
8917c478bd9Sstevel@tonic-gate  * CD speed related functions (ioctl style) for drives which do not support
8927c478bd9Sstevel@tonic-gate  * real time streaming.
89398584592Sarutz  *
89498584592Sarutz  * For the SET operations, the SET CD SPEED command needs
89598584592Sarutz  * both the Read Speed and the Write Speed.  Eg, if
89698584592Sarutz  * we're trying to set the Write Speed (SET_WRITE_SPEED),
89798584592Sarutz  * then we first need to obtain the current Read Speed.
89898584592Sarutz  * That speed is specified along with the chosen_speed (the
89998584592Sarutz  * Write Speed in this case) in the SET CD SPEED command.
9007c478bd9Sstevel@tonic-gate  */
9017c478bd9Sstevel@tonic-gate int
cd_speed_ctrl(cd_device * dev,int cmd,int speed)9027c478bd9Sstevel@tonic-gate cd_speed_ctrl(cd_device *dev, int cmd, int speed)
9037c478bd9Sstevel@tonic-gate {
9047c478bd9Sstevel@tonic-gate 	uint16_t rate;
9057c478bd9Sstevel@tonic-gate 
90698584592Sarutz 	switch (cmd) {
90798584592Sarutz 	case GET_READ_SPEED:
90898584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
90998584592Sarutz 		return (cdrw_bandwidth_to_x(rate));
91098584592Sarutz 
91198584592Sarutz 	case GET_WRITE_SPEED:
91298584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
91398584592Sarutz 		return (cdrw_bandwidth_to_x(rate));
91498584592Sarutz 
91598584592Sarutz 	case SET_READ_SPEED:
91698584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
91798584592Sarutz 		return (set_cd_speed(dev->d_fd,
91898584592Sarutz 		    cdrw_x_to_bandwidth(speed), rate));
91998584592Sarutz 
92098584592Sarutz 	case SET_WRITE_SPEED:
92198584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
9227c478bd9Sstevel@tonic-gate 		return (set_cd_speed(dev->d_fd, rate,
92398584592Sarutz 		    cdrw_x_to_bandwidth(speed)));
92498584592Sarutz 
92598584592Sarutz 	default:
92698584592Sarutz 		return (0);
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate /*
93198584592Sarutz  * Manage sending of SET STREAMING command using the specified
93298584592Sarutz  * read_speed and write_speed.
93398584592Sarutz  *
93498584592Sarutz  * This function allocates and initializes a Performance
93598584592Sarutz  * Descriptor, which is sent as part of the SET STREAMING
93698584592Sarutz  * command.  The descriptor is deallocated before function
93798584592Sarutz  * exit.
9387c478bd9Sstevel@tonic-gate  */
93998584592Sarutz static int
do_set_streaming(cd_device * dev,uint_t read_speed,uint_t write_speed)94098584592Sarutz do_set_streaming(cd_device *dev, uint_t read_speed,
94198584592Sarutz 	uint_t write_speed)
9427c478bd9Sstevel@tonic-gate {
9437c478bd9Sstevel@tonic-gate 	int ret;
94498584592Sarutz 	uchar_t *str;
9457c478bd9Sstevel@tonic-gate 
94698584592Sarutz 	/* Allocate and initialize the Performance Descriptor */
9477c478bd9Sstevel@tonic-gate 	str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN);
94898584592Sarutz 
94998584592Sarutz 	/* Read Time (in milliseconds) */
9507c478bd9Sstevel@tonic-gate 	load_scsi32(&str[16], 1000);
95198584592Sarutz 	/* Write Time (in milliseconds) */
9527c478bd9Sstevel@tonic-gate 	load_scsi32(&str[24], 1000);
95398584592Sarutz 
95498584592Sarutz 	/* Read Speed */
95598584592Sarutz 	load_scsi32(&str[12], (uint32_t)read_speed);
95698584592Sarutz 	/* Write Speed */
95798584592Sarutz 	load_scsi32(&str[20], (uint32_t)write_speed);
95898584592Sarutz 
95998584592Sarutz 	/* issue SET STREAMING command */
9607c478bd9Sstevel@tonic-gate 	ret = set_streaming(dev->d_fd, str);
9617c478bd9Sstevel@tonic-gate 	free(str);
9627c478bd9Sstevel@tonic-gate 
96398584592Sarutz 	return (ret);
96498584592Sarutz }
96598584592Sarutz 
96698584592Sarutz /*
96798584592Sarutz  * cd speed related functions for drives which support
96898584592Sarutz  * Real-Time Streaming Feature.
96998584592Sarutz  *
97098584592Sarutz  * For the SET operations, the SET STREAMING command needs
97198584592Sarutz  * both the Read Speed and the Write Speed.  Eg, if
97298584592Sarutz  * we're trying to set the Write Speed (SET_WRITE_SPEED),
97398584592Sarutz  * then we first need to obtain the current Read Speed.
97498584592Sarutz  * That speed is specified along with the chosen_speed (the
97598584592Sarutz  * Write Speed in this case) in the SET STREAMING command.
97698584592Sarutz  */
97798584592Sarutz int
rt_streaming_ctrl(cd_device * dev,int cmd,int speed)97898584592Sarutz rt_streaming_ctrl(cd_device *dev, int cmd, int speed)
97998584592Sarutz {
98098584592Sarutz 	int ret = 0;
98198584592Sarutz 	uint_t rate;
98298584592Sarutz 
98398584592Sarutz 	switch (cmd) {
98498584592Sarutz 	case GET_WRITE_SPEED:
98598584592Sarutz 		rate = cd_speed_get(dev, GET_WRITE_SPEED);
98698584592Sarutz 		ret = (int)cdrw_bandwidth_to_x(rate);
98798584592Sarutz 		break;
98898584592Sarutz 
98998584592Sarutz 	case GET_READ_SPEED:
99098584592Sarutz 		rate = cd_speed_get(dev, GET_READ_SPEED);
99198584592Sarutz 		ret = (int)cdrw_bandwidth_to_x(rate);
99298584592Sarutz 		break;
99398584592Sarutz 
99498584592Sarutz 	case SET_READ_SPEED: {
99598584592Sarutz 		uint_t write_speed = cd_speed_get(dev, GET_WRITE_SPEED);
99698584592Sarutz 
99798584592Sarutz 		/* set Read Speed using SET STREAMING */
99898584592Sarutz 		ret = do_set_streaming(dev,
99998584592Sarutz 		    cdrw_x_to_bandwidth(speed), write_speed);
100098584592Sarutz 
100198584592Sarutz 		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
100298584592Sarutz 		if (ret == 0) {
100398584592Sarutz 			if (debug)
100498584592Sarutz 				(void) printf(" real time speed control"
100598584592Sarutz 				    " failed, using CD speed control\n");
100698584592Sarutz 
100798584592Sarutz 			dev->d_speed_ctrl = cd_speed_ctrl;
100898584592Sarutz 			ret = dev->d_speed_ctrl(dev, cmd, speed);
100998584592Sarutz 		}
101098584592Sarutz 		break;
101198584592Sarutz 	}
101298584592Sarutz 
101398584592Sarutz 	case SET_WRITE_SPEED: {
101498584592Sarutz 		uint_t read_speed = cd_speed_get(dev, GET_READ_SPEED);
101598584592Sarutz 
101698584592Sarutz 		/* set Write Speed using SET STREAMING */
101798584592Sarutz 		ret = do_set_streaming(dev, read_speed,
101898584592Sarutz 		    cdrw_x_to_bandwidth(speed));
101998584592Sarutz 
102098584592Sarutz 		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
102198584592Sarutz 		if (ret == 0) {
102298584592Sarutz 			if (debug)
102398584592Sarutz 				(void) printf(" real time speed control"
102498584592Sarutz 				    " failed, using CD speed control\n");
102598584592Sarutz 
102698584592Sarutz 			dev->d_speed_ctrl = cd_speed_ctrl;
102798584592Sarutz 			ret = dev->d_speed_ctrl(dev, cmd, speed);
102898584592Sarutz 		}
102998584592Sarutz 		break;
103098584592Sarutz 	}
10317c478bd9Sstevel@tonic-gate 
103298584592Sarutz 	default:
103398584592Sarutz 		break;
10347c478bd9Sstevel@tonic-gate 	}
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	return (ret);
10377c478bd9Sstevel@tonic-gate }
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate /*
10407c478bd9Sstevel@tonic-gate  * Initialize device for track-at-once mode of writing. All of the data will
10417c478bd9Sstevel@tonic-gate  * need to be written to the track without interruption.
10427c478bd9Sstevel@tonic-gate  * This initialized TAO by setting page code 5 and speed.
10437c478bd9Sstevel@tonic-gate  */
10447c478bd9Sstevel@tonic-gate void
write_init(int mode)10457c478bd9Sstevel@tonic-gate write_init(int mode)
10467c478bd9Sstevel@tonic-gate {
10477c478bd9Sstevel@tonic-gate 	(void) printf(gettext("Initializing device"));
10487c478bd9Sstevel@tonic-gate 	if (simulation)
10497c478bd9Sstevel@tonic-gate 		(void) printf(gettext("(Simulation mode)"));
10507c478bd9Sstevel@tonic-gate 	print_n_flush("...");
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	get_media_type(target->d_fd);
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	/* DVD- requires DAO mode */
10557c478bd9Sstevel@tonic-gate 	if (device_type == DVD_MINUS) {
10567c478bd9Sstevel@tonic-gate 		write_mode = DAO_MODE;
10577c478bd9Sstevel@tonic-gate 	}
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	/* DVD+ and DVD- have no support for AUDIO, bail out */
10607c478bd9Sstevel@tonic-gate 	if ((mode == TRACK_MODE_AUDIO) && (device_type != CD_RW)) {
10617c478bd9Sstevel@tonic-gate 		err_msg(gettext("Audio mode is only supported for CD media\n"));
10627c478bd9Sstevel@tonic-gate 		exit(1);
10637c478bd9Sstevel@tonic-gate 	}
1064a2b4fdf6Srameshc 	if (simulation &&
1065a2b4fdf6Srameshc 	    check_device(target, CHECK_MEDIA_IS_NOT_BLANK) &&
1066a2b4fdf6Srameshc 	    !check_device(target, CHECK_MEDIA_IS_NOT_ERASABLE) &&
1067a2b4fdf6Srameshc 	    device_type != DVD_PLUS_W) {
1068a2b4fdf6Srameshc 		/*
1069a2b4fdf6Srameshc 		 * If we were in simulation mode, and media wasn't blank,
1070a2b4fdf6Srameshc 		 * but medium was erasable, then cdrw goes to erase the
1071a2b4fdf6Srameshc 		 * contents of the media after the simulation writing in order
1072a2b4fdf6Srameshc 		 * to cleanup the ghost TOC (see write_fini() calls blank()).
1073a2b4fdf6Srameshc 		 * This is bad because it removes existing data if media was
1074a2b4fdf6Srameshc 		 * multi-session. Therefore, we no longer allow simulation
1075a2b4fdf6Srameshc 		 * writing if such condition is met. we don't blank the DVD+RW
1076a2b4fdf6Srameshc 		 * media, so DVD+RWs are fine.
1077a2b4fdf6Srameshc 		 */
1078a2b4fdf6Srameshc 		err_msg(gettext(
1079a2b4fdf6Srameshc 		    "Cannot perform simulation for non-blank media\n"));
1080a2b4fdf6Srameshc 		exit(1);
1081a2b4fdf6Srameshc 	}
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	if (!prepare_for_write(target, mode, simulation, keep_disc_open)) {
10847c478bd9Sstevel@tonic-gate 		/* l10n_NOTE : 'failed' as in Initializing device...failed  */
10857c478bd9Sstevel@tonic-gate 		(void) printf(gettext("failed.\n"));
10867c478bd9Sstevel@tonic-gate 		err_msg(gettext("Cannot initialize device for write\n"));
10877c478bd9Sstevel@tonic-gate 		exit(1);
10887c478bd9Sstevel@tonic-gate 	}
10897c478bd9Sstevel@tonic-gate 	/* l10n_NOTE : 'done' as in "Initializing device...done"  */
10907c478bd9Sstevel@tonic-gate 	(void) printf(gettext("done.\n"));
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	/* if speed change option was used (-p) then try to set the speed */
10937c478bd9Sstevel@tonic-gate 	if (requested_speed != 0) {
10947c478bd9Sstevel@tonic-gate 		if (verbose)
10957c478bd9Sstevel@tonic-gate 			(void) printf(gettext("Trying to set speed to %dX.\n"),
10967c478bd9Sstevel@tonic-gate 			    requested_speed);
10977c478bd9Sstevel@tonic-gate 		if (target->d_speed_ctrl(target, SET_WRITE_SPEED,
10987c478bd9Sstevel@tonic-gate 		    requested_speed) == 0) {
10997c478bd9Sstevel@tonic-gate 			err_msg(gettext("Unable to set speed.\n"));
11007c478bd9Sstevel@tonic-gate 			exit(1);
11017c478bd9Sstevel@tonic-gate 		}
11027c478bd9Sstevel@tonic-gate 		if (verbose) {
11037c478bd9Sstevel@tonic-gate 			int speed;
11047c478bd9Sstevel@tonic-gate 			speed = target->d_speed_ctrl(target,
11057c478bd9Sstevel@tonic-gate 			    GET_WRITE_SPEED, 0);
11067c478bd9Sstevel@tonic-gate 			if (speed == requested_speed) {
11077c478bd9Sstevel@tonic-gate 				(void) printf(gettext("Speed set to %dX.\n"),
11087c478bd9Sstevel@tonic-gate 				    speed);
110998584592Sarutz 			} else if (speed == 0) {
111098584592Sarutz 				(void) printf(gettext("Could not obtain "
111198584592Sarutz 				    "current Write Speed.\n"));
11127c478bd9Sstevel@tonic-gate 			} else {
11137c478bd9Sstevel@tonic-gate 				(void) printf(
11147c478bd9Sstevel@tonic-gate 				gettext("Speed set to closest approximation "
11157c478bd9Sstevel@tonic-gate 				    "of %dX allowed by device (%dX).\n"),
11167c478bd9Sstevel@tonic-gate 				    requested_speed, speed);
11177c478bd9Sstevel@tonic-gate 			}
11187c478bd9Sstevel@tonic-gate 		}
11197c478bd9Sstevel@tonic-gate 	}
11207c478bd9Sstevel@tonic-gate }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate void
write_fini(void)11237c478bd9Sstevel@tonic-gate write_fini(void)
11247c478bd9Sstevel@tonic-gate {
11257c478bd9Sstevel@tonic-gate 	print_n_flush(gettext("Finalizing (Can take several minutes)..."));
11267c478bd9Sstevel@tonic-gate 	/* Some drives don't like this while in test write mode */
11277c478bd9Sstevel@tonic-gate 	if (!simulation) {
11287c478bd9Sstevel@tonic-gate 		if (!finalize(target)) {
11297c478bd9Sstevel@tonic-gate 			/*
11307c478bd9Sstevel@tonic-gate 			 * It is possible that the drive is busy writing the
11317c478bd9Sstevel@tonic-gate 			 * buffered portion. So do not get upset yet.
11327c478bd9Sstevel@tonic-gate 			 */
11337c478bd9Sstevel@tonic-gate 			(void) sleep(10);
11347c478bd9Sstevel@tonic-gate 			if (!finalize(target)) {
11357c478bd9Sstevel@tonic-gate 				if (debug) {
11367c478bd9Sstevel@tonic-gate 					(void) printf("status %x, %x/%x/%x\n",
11377c478bd9Sstevel@tonic-gate 					    uscsi_status, SENSE_KEY(rqbuf),
11387c478bd9Sstevel@tonic-gate 					    ASC(rqbuf), ASCQ(rqbuf));
11397c478bd9Sstevel@tonic-gate 				}
11407c478bd9Sstevel@tonic-gate 
11415d465cc7Sminht 				/*
11425d465cc7Sminht 				 * Different vendor drives return different
11435d465cc7Sminht 				 * sense error info for CLOSE SESSION command.
11445d465cc7Sminht 				 * The Panasonic drive that we are using is
11455d465cc7Sminht 				 * one such drive.
11465d465cc7Sminht 				 */
11475d465cc7Sminht 				if (device_type == DVD_MINUS) {
11487c478bd9Sstevel@tonic-gate 					if (verbose) {
11497c478bd9Sstevel@tonic-gate 						(void) printf(
11507c478bd9Sstevel@tonic-gate 						    "skipping finalizing\n");
11517c478bd9Sstevel@tonic-gate 					}
11527c478bd9Sstevel@tonic-gate 				} else {
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 			/* l10n_NOTE : 'failed' as in finishing up...failed  */
11557c478bd9Sstevel@tonic-gate 					(void) printf(gettext("failed.\n"));
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 					err_msg(gettext(
11587c478bd9Sstevel@tonic-gate 					    "Could not finalize the disc.\n"));
11597c478bd9Sstevel@tonic-gate 					exit(1);
11607c478bd9Sstevel@tonic-gate 				}
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 			}
11647c478bd9Sstevel@tonic-gate 		}
11657c478bd9Sstevel@tonic-gate 		if (vol_running) {
11667c478bd9Sstevel@tonic-gate 			(void) eject_media(target);
11677c478bd9Sstevel@tonic-gate 		}
11687c478bd9Sstevel@tonic-gate 	} else if (check_device(target, CHECK_MEDIA_IS_NOT_BLANK)) {
11697c478bd9Sstevel@tonic-gate 		/*
11707c478bd9Sstevel@tonic-gate 		 * Some drives such as the pioneer A04 will retain a
11717c478bd9Sstevel@tonic-gate 		 * ghost TOC after a simulation write is done. The
11727c478bd9Sstevel@tonic-gate 		 * media will actually be blank, but the drive will
11737c478bd9Sstevel@tonic-gate 		 * report a TOC. There is currently no other way to
11747c478bd9Sstevel@tonic-gate 		 * re-initialize the media other than ejecting or
11757c478bd9Sstevel@tonic-gate 		 * to ask the drive to clear the leadout. The laser
11767c478bd9Sstevel@tonic-gate 		 * is currently off so nothing is written to the
11777c478bd9Sstevel@tonic-gate 		 * media (on a good behaving drive).
11787c478bd9Sstevel@tonic-gate 		 * NOTE that a device reset does not work to make
11797c478bd9Sstevel@tonic-gate 		 * the drive re-initialize the media.
11807c478bd9Sstevel@tonic-gate 		 */
11817c478bd9Sstevel@tonic-gate 
1182a2b4fdf6Srameshc 		blanking_type = "clear_ghost";
1183a2b4fdf6Srameshc 		blank();
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 	}
11867c478bd9Sstevel@tonic-gate 	/* l10n_NOTE : 'done' as in "Finishing up...done"  */
11877c478bd9Sstevel@tonic-gate 	(void) printf(gettext("done.\n"));
11887c478bd9Sstevel@tonic-gate }
1189