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 /* 223503b75eSec158148 * 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 463503b75eSec158148 static int check_track_size(cd_device *dev, int trk_num, 473503b75eSec158148 struct track_info *tip); 483503b75eSec158148 static int rtoc_get_trk_sess_num(uchar_t *rtoc, size_t rtoc_len, int trk_num, 493503b75eSec158148 int *sess_nump); 503503b75eSec158148 static int rtoc_get_sess_last_trk_num(uchar_t *rtoc, size_t rtoc_len, 513503b75eSec158148 int sess_num, int *last_trk_nump); 523503b75eSec158148 static int rtoc_get_sess_leadout_lba(uchar_t *rtoc, size_t rtoc_len, 533503b75eSec158148 int sess_num, uint32_t *leadout_lba); 543503b75eSec158148 static rtoc_td_t *get_rtoc_td(rtoc_td_t *begin_tdp, rtoc_td_t *end_tdp, 553503b75eSec158148 uchar_t adr, uchar_t point); 563503b75eSec158148 577c478bd9Sstevel@tonic-gate uint32_t 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 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 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 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 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 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 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 } 2843503b75eSec158148 } else { 2853503b75eSec158148 if (check_track_size(dev, trackno, t_info) != 1) 2863503b75eSec158148 return (0); 2873503b75eSec158148 } 2883503b75eSec158148 2893503b75eSec158148 return (1); 2903503b75eSec158148 } 2913503b75eSec158148 2923503b75eSec158148 /* 2933503b75eSec158148 * The size of the last track in one of the first N - 1 sessions of an 2943503b75eSec158148 * N-session (N > 1) disc is reported incorrectly by some drives and calculated 2953503b75eSec158148 * incorrectly for others, because a pre-gap/lead-out/lead-in section that ends 2963503b75eSec158148 * a session is erroneously considered part of that track. This function checks 2973503b75eSec158148 * for this corner case, and adjusts the track size if necessary. 2983503b75eSec158148 */ 2993503b75eSec158148 static int 3003503b75eSec158148 check_track_size(cd_device *dev, int trk_num, struct track_info *tip) 3013503b75eSec158148 { 3023503b75eSec158148 size_t raw_toc_len; 3033503b75eSec158148 uchar_t *raw_toc; 3043503b75eSec158148 rtoc_hdr_t hdr; 3053503b75eSec158148 uint32_t sess_leadout_lba; 3063503b75eSec158148 int sess_last_trk_num; 3073503b75eSec158148 int trk_sess_num; 3083503b75eSec158148 uint32_t trk_size; 3093503b75eSec158148 3103503b75eSec158148 /* Request Raw TOC Header for session count. */ 3113503b75eSec158148 if (read_toc(dev->d_fd, FORMAT_RAW_TOC, 1, 3123503b75eSec158148 sizeof (rtoc_hdr_t), (uchar_t *)&hdr) != 1) 3133503b75eSec158148 return (0); 3143503b75eSec158148 3153503b75eSec158148 /* Is this a multi-session medium? */ 3163503b75eSec158148 if (hdr.rh_last_sess_num > hdr.rh_first_sess_num) { 3173503b75eSec158148 /* Yes; request entire Raw TOC. */ 3183503b75eSec158148 raw_toc_len = read_scsi16(&hdr.rh_data_len1) + RTOC_DATA_LEN_SZ; 3193503b75eSec158148 raw_toc = (uchar_t *)my_zalloc(raw_toc_len); 3203503b75eSec158148 3213503b75eSec158148 if (read_toc(dev->d_fd, FORMAT_RAW_TOC, 1, raw_toc_len, raw_toc) 3223503b75eSec158148 != 1) 3233503b75eSec158148 goto fail; 3243503b75eSec158148 3253503b75eSec158148 if (rtoc_get_trk_sess_num(raw_toc, raw_toc_len, trk_num, 3263503b75eSec158148 &trk_sess_num) != 1) 3273503b75eSec158148 goto fail; 3283503b75eSec158148 3293503b75eSec158148 tip->ti_session_no = trk_sess_num; 3303503b75eSec158148 tip->ti_flags |= TI_SESSION_NO_VALID; 3313503b75eSec158148 3323503b75eSec158148 /* Is the track in one of the first N - 1 sessions? */ 3333503b75eSec158148 if (trk_sess_num < hdr.rh_last_sess_num) { 3343503b75eSec158148 if (rtoc_get_sess_last_trk_num(raw_toc, raw_toc_len, 3353503b75eSec158148 trk_sess_num, &sess_last_trk_num) != 1) 3363503b75eSec158148 goto fail; 3373503b75eSec158148 3383503b75eSec158148 /* Is the track the last track in the session? */ 3393503b75eSec158148 if (trk_num == sess_last_trk_num) { 3403503b75eSec158148 if (rtoc_get_sess_leadout_lba(raw_toc, 3413503b75eSec158148 raw_toc_len, trk_sess_num, 3423503b75eSec158148 &sess_leadout_lba) != 1) 3433503b75eSec158148 goto fail; 3443503b75eSec158148 3453503b75eSec158148 trk_size = sess_leadout_lba - 3463503b75eSec158148 tip->ti_start_address; 3473503b75eSec158148 3483503b75eSec158148 /* Fix track size if it was too big. */ 3493503b75eSec158148 if (tip->ti_track_size > trk_size) 3503503b75eSec158148 tip->ti_track_size = trk_size; 3513503b75eSec158148 } 3523503b75eSec158148 } 3533503b75eSec158148 free(raw_toc); 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate return (1); 3563503b75eSec158148 3573503b75eSec158148 fail: 3583503b75eSec158148 free(raw_toc); 3593503b75eSec158148 return (0); 3603503b75eSec158148 } 3613503b75eSec158148 3623503b75eSec158148 /* 3633503b75eSec158148 * Determine what session number a track is in by parsing the Raw TOC format of 3643503b75eSec158148 * the the READ TOC/PMA/ATIP command response data. 3653503b75eSec158148 */ 3663503b75eSec158148 static int 3673503b75eSec158148 rtoc_get_trk_sess_num(uchar_t *rtoc, size_t rtoc_len, int trk_num, 3683503b75eSec158148 int *sess_nump) 3693503b75eSec158148 { 3703503b75eSec158148 rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t)); 3713503b75eSec158148 rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len - 3723503b75eSec158148 sizeof (rtoc_td_t)); 3733503b75eSec158148 3743503b75eSec158148 if ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1, (uchar_t)trk_num)) != 3753503b75eSec158148 NULL) { 3763503b75eSec158148 *sess_nump = tdp->rt_session_num; 3773503b75eSec158148 return (1); 3783503b75eSec158148 } else 3793503b75eSec158148 return (0); 3803503b75eSec158148 } 3813503b75eSec158148 3823503b75eSec158148 /* 3833503b75eSec158148 * Determine the last track number in a specified session number by parsing the 3843503b75eSec158148 * Raw TOC format of the READ TOC/PMA/ATIP command response data. 3853503b75eSec158148 */ 3863503b75eSec158148 static int 3873503b75eSec158148 rtoc_get_sess_last_trk_num(uchar_t *rtoc, size_t rtoc_len, int sess_num, 3883503b75eSec158148 int *last_trk_nump) 3893503b75eSec158148 { 3903503b75eSec158148 rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t)); 3913503b75eSec158148 rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len - 3923503b75eSec158148 sizeof (rtoc_td_t)); 3933503b75eSec158148 3943503b75eSec158148 while ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1, 3953503b75eSec158148 POINT_SESS_LAST_TRK)) != NULL) { 3963503b75eSec158148 if (tdp->rt_session_num == sess_num) { 3973503b75eSec158148 *last_trk_nump = tdp->rt_pmin; 3983503b75eSec158148 return (1); 3993503b75eSec158148 } else { 4003503b75eSec158148 ++tdp; 4013503b75eSec158148 } 4023503b75eSec158148 } 4033503b75eSec158148 4043503b75eSec158148 return (0); 4053503b75eSec158148 } 4063503b75eSec158148 4073503b75eSec158148 /* 4083503b75eSec158148 * Determine the starting LBA of the the session leadout by parsing the Raw TOC 4093503b75eSec158148 * format of the READ TOC/PMA/ATIP command response data. 4103503b75eSec158148 */ 4113503b75eSec158148 static int 4123503b75eSec158148 rtoc_get_sess_leadout_lba(uchar_t *rtoc, size_t rtoc_len, int sess_num, 4133503b75eSec158148 uint32_t *leadout_lba) 4143503b75eSec158148 { 4153503b75eSec158148 rtoc_td_t *tdp = (rtoc_td_t *)(rtoc + sizeof (rtoc_hdr_t)); 4163503b75eSec158148 rtoc_td_t *last_tdp = (rtoc_td_t *)(rtoc + rtoc_len - 4173503b75eSec158148 sizeof (rtoc_td_t)); 4183503b75eSec158148 4193503b75eSec158148 while ((tdp = get_rtoc_td(tdp, last_tdp, Q_MODE_1, 4203503b75eSec158148 POINT_LEADOUT_ADDR)) != NULL) { 4213503b75eSec158148 if (tdp->rt_session_num == sess_num) { 4223503b75eSec158148 *leadout_lba = MSF2LBA(tdp->rt_pmin, tdp->rt_psec, 4233503b75eSec158148 tdp->rt_pframe); 4243503b75eSec158148 return (1); 4253503b75eSec158148 } else { 4263503b75eSec158148 ++tdp; 4273503b75eSec158148 } 4283503b75eSec158148 } 4293503b75eSec158148 4303503b75eSec158148 return (0); 4313503b75eSec158148 } 4323503b75eSec158148 4333503b75eSec158148 /* 4343503b75eSec158148 * Search a set of Raw TOC Track Descriptors using <'adr', 'point'> as the 4353503b75eSec158148 * search key. Return a pointer to the first Track Descriptor that matches. 4363503b75eSec158148 */ 4373503b75eSec158148 static rtoc_td_t * 4383503b75eSec158148 get_rtoc_td(rtoc_td_t *begin_tdp, rtoc_td_t *end_tdp, uchar_t adr, 4393503b75eSec158148 uchar_t point) 4403503b75eSec158148 { 4413503b75eSec158148 rtoc_td_t *cur_tdp = begin_tdp; 4423503b75eSec158148 4433503b75eSec158148 while (cur_tdp <= end_tdp) { 4443503b75eSec158148 if ((cur_tdp->rt_adr == adr) && (cur_tdp->rt_point == point)) 4453503b75eSec158148 return (cur_tdp); 4463503b75eSec158148 else 4473503b75eSec158148 cur_tdp++; 4483503b75eSec158148 } 4493503b75eSec158148 4503503b75eSec158148 return (NULL); 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate uchar_t 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 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 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 */ 7074d218355Szk194757 uint32_t 7087c478bd9Sstevel@tonic-gate get_last_possible_lba(cd_device *dev) 7097c478bd9Sstevel@tonic-gate { 7107c478bd9Sstevel@tonic-gate uchar_t *di; 7114d218355Szk194757 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 } 7184d218355Szk194757 7194d218355Szk194757 /* 7204d218355Szk194757 * If we have a DVD+R this field is an LBA. If the media is 7214d218355Szk194757 * a CD-R/W the field is MSF formatted. Otherwise this field 7224d218355Szk194757 * is not valid and will be zero. 7234d218355Szk194757 */ 7244d218355Szk194757 if (device_type == DVD_PLUS) { 7254d218355Szk194757 if (read_scsi32(&di[20]) != 0xffffffff) { 7264d218355Szk194757 cap = read_scsi32(&di[20]); 7274d218355Szk194757 } else { 7284d218355Szk194757 cap = 0; 7294d218355Szk194757 } 7304d218355Szk194757 } else { 7317c478bd9Sstevel@tonic-gate if ((di[21] != 0) && (di[21] != 0xff)) { 7329103ebf4Szk194757 cap = MSF2LBA(di[21], di[22], di[23]); 7337c478bd9Sstevel@tonic-gate } else { 7347c478bd9Sstevel@tonic-gate cap = 0; 7357c478bd9Sstevel@tonic-gate } 7364d218355Szk194757 } 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate free(di); 7397c478bd9Sstevel@tonic-gate return (cap); 7407c478bd9Sstevel@tonic-gate } 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate int 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 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) { 764b36bd5c0Szk194757 /* 765b36bd5c0Szk194757 * The check_device() call will issue 766b36bd5c0Szk194757 * a TEST UNIT READY (TUR) and retry many 767b36bd5c0Szk194757 * times when a DVD-R is present. The DKIOCEJECT 768b36bd5c0Szk194757 * ioctl will subsequently fail causing us to 769b36bd5c0Szk194757 * issue the LOAD/UNLOAD SCSI command to the device 770b36bd5c0Szk194757 * with out ejecting the media. Insted of letting 771b36bd5c0Szk194757 * this happen, issue a reset to the device before 772b36bd5c0Szk194757 * issuing the DKIOCEJCET ioctl. 773b36bd5c0Szk194757 */ 774b36bd5c0Szk194757 if (device_type == DVD_MINUS) 775b36bd5c0Szk194757 reset_dev(dev->d_fd); 776b36bd5c0Szk194757 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 81898584592Sarutz cd_speed_get(cd_device *dev, int cmd) 8197c478bd9Sstevel@tonic-gate { 8207c478bd9Sstevel@tonic-gate uchar_t *mp2a; 82198584592Sarutz uint16_t rate = 0; 82298584592Sarutz int offset; 82398584592Sarutz uint_t buflen = 254; 8247c478bd9Sstevel@tonic-gate 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"); 85998584592Sarutz 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]); 8767c478bd9Sstevel@tonic-gate } 8777c478bd9Sstevel@tonic-gate } 87898584592Sarutz break; 87998584592Sarutz } 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 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: 9267c478bd9Sstevel@tonic-gate return (0); 9277c478bd9Sstevel@tonic-gate } 92898584592Sarutz } 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. 93898584592Sarutz */ 93998584592Sarutz static int 94098584592Sarutz do_set_streaming(cd_device *dev, uint_t read_speed, 94198584592Sarutz uint_t write_speed) 94298584592Sarutz { 94398584592Sarutz int ret; 94498584592Sarutz uchar_t *str; 94598584592Sarutz 94698584592Sarutz /* Allocate and initialize the Performance Descriptor */ 94798584592Sarutz str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN); 94898584592Sarutz 94998584592Sarutz /* Read Time (in milliseconds) */ 95098584592Sarutz load_scsi32(&str[16], 1000); 95198584592Sarutz /* Write Time (in milliseconds) */ 95298584592Sarutz 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 */ 96098584592Sarutz ret = set_streaming(dev->d_fd, str); 96198584592Sarutz free(str); 96298584592Sarutz 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. 9767c478bd9Sstevel@tonic-gate */ 9777c478bd9Sstevel@tonic-gate int 9787c478bd9Sstevel@tonic-gate rt_streaming_ctrl(cd_device *dev, int cmd, int speed) 9797c478bd9Sstevel@tonic-gate { 98098584592Sarutz int ret = 0; 98198584592Sarutz uint_t rate; 9827c478bd9Sstevel@tonic-gate 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); 10007c478bd9Sstevel@tonic-gate 10017c478bd9Sstevel@tonic-gate /* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */ 10027c478bd9Sstevel@tonic-gate if (ret == 0) { 10037c478bd9Sstevel@tonic-gate if (debug) 10047c478bd9Sstevel@tonic-gate (void) printf(" real time speed control" 10057c478bd9Sstevel@tonic-gate " failed, using CD speed control\n"); 10067c478bd9Sstevel@tonic-gate 10077c478bd9Sstevel@tonic-gate dev->d_speed_ctrl = cd_speed_ctrl; 10087c478bd9Sstevel@tonic-gate ret = dev->d_speed_ctrl(dev, cmd, speed); 10097c478bd9Sstevel@tonic-gate } 101098584592Sarutz break; 101198584592Sarutz } 10127c478bd9Sstevel@tonic-gate 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 } 103198584592Sarutz 103298584592Sarutz default: 103398584592Sarutz break; 103498584592Sarutz } 103598584592Sarutz 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 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 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"; 11837c478bd9Sstevel@tonic-gate 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