summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cdrw
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cdrw
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cdrw')
-rw-r--r--usr/src/cmd/cdrw/Makefile69
-rw-r--r--usr/src/cmd/cdrw/audio.h78
-rw-r--r--usr/src/cmd/cdrw/blank.c242
-rw-r--r--usr/src/cmd/cdrw/bstream.c656
-rw-r--r--usr/src/cmd/cdrw/bstream.h82
-rw-r--r--usr/src/cmd/cdrw/byteorder.h72
-rw-r--r--usr/src/cmd/cdrw/copycd.c427
-rw-r--r--usr/src/cmd/cdrw/dae.c342
-rw-r--r--usr/src/cmd/cdrw/device.c853
-rw-r--r--usr/src/cmd/cdrw/device.h85
-rw-r--r--usr/src/cmd/cdrw/dumpinfo.c122
-rw-r--r--usr/src/cmd/cdrw/main.c399
-rw-r--r--usr/src/cmd/cdrw/main.h94
-rw-r--r--usr/src/cmd/cdrw/misc_scsi.c839
-rw-r--r--usr/src/cmd/cdrw/misc_scsi.h113
-rw-r--r--usr/src/cmd/cdrw/mmc.c664
-rw-r--r--usr/src/cmd/cdrw/mmc.h114
-rw-r--r--usr/src/cmd/cdrw/msgs.c71
-rw-r--r--usr/src/cmd/cdrw/msgs.h53
-rw-r--r--usr/src/cmd/cdrw/options.c87
-rw-r--r--usr/src/cmd/cdrw/options.h50
-rw-r--r--usr/src/cmd/cdrw/toshiba.c186
-rw-r--r--usr/src/cmd/cdrw/toshiba.h48
-rw-r--r--usr/src/cmd/cdrw/trackio.c579
-rw-r--r--usr/src/cmd/cdrw/trackio.h86
-rw-r--r--usr/src/cmd/cdrw/transport.c224
-rw-r--r--usr/src/cmd/cdrw/transport.h54
-rw-r--r--usr/src/cmd/cdrw/util.c293
-rw-r--r--usr/src/cmd/cdrw/util.h82
-rw-r--r--usr/src/cmd/cdrw/write_audio.c154
-rw-r--r--usr/src/cmd/cdrw/write_image.c206
31 files changed, 7424 insertions, 0 deletions
diff --git a/usr/src/cmd/cdrw/Makefile b/usr/src/cmd/cdrw/Makefile
new file mode 100644
index 0000000000..b7705289b5
--- /dev/null
+++ b/usr/src/cmd/cdrw/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= cdrw
+
+OBJS= main.o mmc.o device.o transport.o util.o msgs.o misc_scsi.o dumpinfo.o \
+ toshiba.o bstream.o options.o trackio.o write_image.o blank.o \
+ write_audio.o dae.o copycd.o
+
+include ../Makefile.cmd
+
+SRCS= $(OBJS:.o=.c)
+
+LDLIBS += -lvolmgt -lsecdb
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+LINTFLAGS += -um
+
+$(ROOTBIN)/cdrw := FILEMODE = 04755
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(CFLAGS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+$(POFILE) : $(SRCS)
+ $(RM) $@
+ $(COMPILE.cpp) $(SRCS) | $(XGETTEXT) $(XGETFLAGS) -
+ $(SED) -e '/^domain/d' messages.po > $@
+
+sb: $(SRCS)
+ $(COMPILE.c) -xsbfast $(SRCS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/cdrw/audio.h b/usr/src/cmd/cdrw/audio.h
new file mode 100644
index 0000000000..e8f2242867
--- /dev/null
+++ b/usr/src/cmd/cdrw/audio.h
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _AUDIO_H
+#define _AUDIO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Contains the audio files information for cdrw.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+typedef struct {
+ char riff[4];
+ uint32_t total_chunk_size;
+ char wave[4];
+ char fmt[4];
+ uint32_t fmt_size;
+ uint16_t fmt_tag;
+ uint16_t n_channels;
+ uint32_t sample_rate;
+ uint32_t bytes_per_second;
+ uint16_t align;
+ uint16_t bits_per_sample;
+ char data[4];
+ uint32_t data_size;
+} Wave_filehdr;
+
+
+#define PRE_DEF_WAV_HDR { 'R', 'I', 'F', 'F', 0, 0, 0, 0, \
+ 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', \
+ 0x10, 0, 0, 0, 1, 0, 2, 0, \
+ 0x44, 0xac, 0, 0, 0x10, 0xb1, 2, 0, \
+ 4, 0, 0x10, 0, 'd', 'a', 't', 'a', \
+ 0, 0, 0, 0 }
+#define PRE_DEF_WAV_HDR_LEN 44
+
+#define PRE_DEF_AU_HDR { '.', 's', 'n', 'd', 0, 0, 0, 0x28, \
+ 0, 0, 0, 0, 0, 0, 0, 3, \
+ 0, 0, 0xac, 0x44, 0, 0, 0, 2, \
+ 0x43, 0x44, 0x20, 0x41, 0x75, 0x64, 0x69, \
+ 0x6f, 0, 0, 0, 0, 0, 0, 0, 0 }
+#define PRE_DEF_AU_HDR_LEN 40
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUDIO_H */
diff --git a/usr/src/cmd/cdrw/blank.c b/usr/src/cmd/cdrw/blank.c
new file mode 100644
index 0000000000..f237997030
--- /dev/null
+++ b/usr/src/cmd/cdrw/blank.c
@@ -0,0 +1,242 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <unistd.h>
+
+#include "msgs.h"
+#include "mmc.h"
+#include "util.h"
+#include "transport.h"
+#include "main.h"
+#include "misc_scsi.h"
+
+/*
+ * This is called recursively once, if an ALL blank succeeds but the
+ * media is not blank we call blank() again to perform a fast blank.
+ * This is a workaround for some drives such as older Toshiba DVD-RW
+ * which has this problem with ALL blanking.
+ */
+void
+blank(void)
+{
+ int type, invalid;
+ int count, ret;
+ uchar_t *di, *buf;
+ int immediate, err;
+ int silent_pass = 0;
+ invalid = 0;
+ err = 0;
+
+ (void) check_device(target, CHECK_TYPE_NOT_CDROM | CHECK_NO_MEDIA |
+ EXIT_IF_CHECK_FAILED);
+ (void) check_device(target, CHECK_DEVICE_NOT_READY |
+ CHECK_DEVICE_NOT_WRITABLE | EXIT_IF_CHECK_FAILED);
+
+ if (blanking_type == NULL) {
+ invalid = 1;
+ }
+
+ get_media_type(target->d_fd);
+
+ /*
+ * many DVD+RW drives do not allow blanking the media, it is also
+ * not included in the spec, we would just reformat the media prior
+ * to writing. This is not the equivelent to blanking as the media
+ * contains a TOC when formatted.
+ */
+ if (device_type == DVD_PLUS_W) {
+ err_msg(gettext("Blanking cannot be done on DVD+RW media\n"));
+ exit(1);
+ }
+
+ if (strcmp(blanking_type, "all") == 0) {
+ /* erase the whole disk */
+ type = ALL;
+ } else if (strcmp(blanking_type, "session") == 0) {
+ /* only erase the last session */
+ type = SESSION;
+ } else if (strcmp(blanking_type, "fast") == 0) {
+ /* quick blank the TOC on the media */
+ type = FAST;
+ } else if (strcmp(blanking_type, "leadout") == 0) {
+ /* erase the track tail to unclose the media */
+ type = LEADOUT;
+ silent_pass = 1;
+ } else if (strcmp(blanking_type, "clear") == 0) {
+ /*
+ * used for drives where "all" blanking fails,
+ * if it fails we follow up with a quick erase of TOC.
+ * This is only called from within this function to do
+ * a second blanking pass.
+ */
+ type = CLEAR;
+ silent_pass = 1;
+ } else {
+ /* invalid blank type was passed on the command line */
+ invalid = 1;
+ }
+
+ if (invalid) {
+ err_msg(gettext("Invalid blanking type specified\n"));
+ exit(1);
+ }
+ if ((target->d_inq[2] & 7) != 0) {
+ /* SCSI device */
+ immediate = 0;
+ } else {
+ /* non-SCSI (e.g ATAPI) device */
+ immediate = 1;
+ }
+
+ /* we are doing a second pass. We don't want to re-print messsages */
+ if (!silent_pass)
+ print_n_flush(gettext("Initializing device..."));
+
+ /* Make sure that test write is off */
+ buf = (uchar_t *)my_zalloc(64);
+
+ /* get mode page for test writing if it fails we cannot turn it off */
+ if (!get_mode_page(target->d_fd, 5, 0, 64, buf)) {
+ err_msg(gettext("Device not supported\n"));
+ exit(1);
+ }
+
+ buf[2] &= 0xef;
+
+ /* turn laser on */
+ if (!set_mode_page(target->d_fd, buf)) {
+ err_msg(gettext("Unable to configure device\n"));
+ exit(1);
+ }
+ free(buf);
+
+ /* we are doing a second pass. We don't want to re-print messsages */
+ if (!silent_pass) {
+ /* l10n_NOTE : 'done' as in "Initializing device...done" */
+ (void) printf(gettext("done.\n"));
+
+ print_n_flush(gettext(
+ "Blanking the media (Can take several minutes)..."));
+ }
+ if (!blank_disc(target->d_fd, type, immediate)) {
+ err_msg(gettext("Blank command failed\n"));
+ if (debug)
+ (void) printf("%x %x %x %x\n", uscsi_status,
+ SENSE_KEY(rqbuf), ASC(rqbuf), ASCQ(rqbuf));
+ goto blank_failed;
+ }
+ /* Allow the blanking to start */
+ (void) sleep(10);
+
+ /*
+ * set ATAPI devices to immediately return from the command and poll
+ * so that we don't hog the channel.
+ */
+
+ if (immediate) {
+ di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
+ /* Blanking should not take more than 75 minutes */
+ for (count = 0; count < (16*60); count++) {
+ ret = read_disc_info(target->d_fd, di);
+ if (ret != 0)
+ break;
+ if (uscsi_status != 2)
+ err = 1;
+ /* not ready but not becoming ready */
+ if (SENSE_KEY(rqbuf) == 2) {
+ if (ASC(rqbuf) != 4)
+ err = 1;
+ /* illegal mode for this track */
+ } else if (SENSE_KEY(rqbuf) == 5) {
+ if (ASC(rqbuf) != 0x64)
+ err = 1;
+ } else {
+ err = 1;
+ }
+ if (err == 1) {
+ err_msg(gettext("Blanking operation failed\n"));
+ if (debug) {
+ (void) printf("%x %x %x %x\n",
+ uscsi_status, SENSE_KEY(rqbuf),
+ ASC(rqbuf), ASCQ(rqbuf));
+ }
+ free(di);
+ goto blank_failed;
+ }
+ (void) sleep(5);
+ }
+ free(di);
+ if (count == (16*60)) {
+ (void) printf(gettext("Blank command timed out.\n"));
+ goto blank_failed;
+ }
+ }
+ /* we are doing a second pass. We don't want to re-print messsages */
+ if (!silent_pass) {
+ /* l10n_NOTE : 'done' as in "Erasing track 1...done" */
+ (void) printf(gettext("done.\n"));
+ }
+
+ /*
+ * some cruft left from all blanking, this has been seen on some
+ * newer drives including Toshiba SD-6112 DVD-RW and Sony 510A.
+ * we will do a second pass with a recursive call to blank the
+ * lead-in.
+ */
+ if (type == ALL) {
+ if ((check_device(target, CHECK_MEDIA_IS_NOT_BLANK)) &&
+ (!vol_running)) {
+ blanking_type = "clear";
+ blank();
+ exit(0);
+ }
+ }
+
+ /*
+ * We erased part of the leadout for the media to unclose
+ * the disk, we still need to generate an appendable leadout
+ * so that the next track can be written. so do not eject or exit.
+ */
+ if (silent_pass)
+ return;
+
+ if (vol_running)
+ (void) eject_media(target);
+ exit(0);
+blank_failed:
+ if ((type != ALL) && !silent_pass) {
+ (void) printf("Try using blanking type 'all'\n");
+ }
+ if (vol_running)
+ (void) eject_media(target);
+ exit(1);
+}
diff --git a/usr/src/cmd/cdrw/bstream.c b/usr/src/cmd/cdrw/bstream.c
new file mode 100644
index 0000000000..59e688ca1d
--- /dev/null
+++ b/usr/src/cmd/cdrw/bstream.c
@@ -0,0 +1,656 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <limits.h>
+#include <audio/au.h>
+
+#include "bstream.h"
+#include "util.h"
+#include "audio.h"
+#include "byteorder.h"
+#include "main.h"
+
+int str_errno;
+
+char *
+str_errno_to_string(int serrno)
+{
+ switch (serrno) {
+ case STR_ERR_NO_ERR:
+ return (gettext("No error"));
+ case STR_ERR_NO_REG_FILE:
+ return (gettext("Not a regular file"));
+ case STR_ERR_NO_READ_STDIN:
+ return (gettext("Stdin not open for reading"));
+ case STR_ERR_AU_READ_ERR:
+ return (gettext("Unable to read au header"));
+ case STR_ERR_AU_UNSUPPORTED_FORMAT:
+ return (gettext("Unsupported au format"));
+ case STR_ERR_AU_BAD_HEADER:
+ return (gettext("Bad au header"));
+ case STR_ERR_WAV_READ_ERR:
+ return (gettext("Unable to read wav header"));
+ case STR_ERR_WAV_UNSUPPORTED_FORMAT:
+ return (gettext("Unsupported wav format"));
+ case STR_ERR_WAV_BAD_HEADER:
+ return (gettext("Bad wav header"));
+ default:
+ return (gettext("unknown error"));
+ }
+}
+
+static int
+file_stream_size(bstreamhandle h, off_t *size)
+{
+ struct stat st;
+
+ str_errno = 0;
+
+ if (fstat(h->bstr_fd, &st) < 0)
+ return (0);
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ str_errno = STR_ERR_NO_REG_FILE;
+ return (0);
+ }
+ *size = st.st_size;
+ return (1);
+}
+
+static int
+audio_stream_size(bstreamhandle h, off_t *size)
+{
+ str_errno = 0;
+ *size = (off_t)(uintptr_t)(h->bstr_private);
+ return (1);
+}
+
+static int
+file_stream_read(bstreamhandle h, uchar_t *buf, off_t size)
+{
+ str_errno = 0;
+ return (read(h->bstr_fd, buf, size));
+}
+
+static int
+file_stream_write(bstreamhandle h, uchar_t *buf, off_t size)
+{
+ str_errno = 0;
+ return (write(h->bstr_fd, buf, size));
+}
+
+/*
+ * with reverse byteorder
+ */
+static int
+file_stream_read_wrbo(bstreamhandle h, uchar_t *buf, off_t size)
+{
+ int cnt;
+
+ str_errno = 0;
+ cnt = read(h->bstr_fd, buf, size);
+ if (cnt > 0) {
+ int i;
+ uchar_t ch;
+
+ for (i = 0; i < cnt; i += 2) {
+ ch = buf[i];
+ buf[i] = buf[i+1];
+ buf[i+1] = ch;
+ }
+ }
+ return (cnt);
+}
+
+/*
+ * This will change the byteorder in the buffer but that is fine with us.
+ */
+static int
+file_stream_write_wrbo(bstreamhandle h, uchar_t *buf, off_t size)
+{
+ int i;
+ uchar_t ch;
+
+ str_errno = 0;
+ if (size > 0) {
+ for (i = 0; i < size; i += 2) {
+ ch = buf[i];
+ buf[i] = buf[i+1];
+ buf[i+1] = ch;
+ }
+ }
+ return (write(h->bstr_fd, buf, size));
+}
+
+static int
+file_stream_close(bstreamhandle h)
+{
+ int fd;
+
+ str_errno = 0;
+ fd = h->bstr_fd;
+ free(h);
+ return (close(fd));
+}
+
+static int
+stdin_stream_close(bstreamhandle h)
+{
+ str_errno = 0;
+ free(h);
+ return (0);
+}
+
+static int
+wav_write_stream_close(bstreamhandle h)
+{
+ uint32_t sz;
+ Wave_filehdr wav;
+
+ str_errno = 0;
+ (void) memset(&wav, 0, sizeof (wav));
+ sz = lseek(h->bstr_fd, 0L, SEEK_END);
+ (void) lseek(h->bstr_fd, 0L, SEEK_SET);
+ if (read(h->bstr_fd, &wav, sizeof (wav)) != sizeof (wav)) {
+ return (1);
+ }
+ wav.total_chunk_size = CPU_TO_LE32(sz - 8);
+ wav.data_size = CPU_TO_LE32(sz - 44);
+ (void) lseek(h->bstr_fd, 0L, SEEK_SET);
+ if (write(h->bstr_fd, &wav, sizeof (wav)) != sizeof (wav)) {
+ return (1);
+ }
+ (void) close(h->bstr_fd);
+ free(h);
+ return (0);
+}
+
+static int
+au_write_stream_close(bstreamhandle h)
+{
+ uint32_t sz;
+
+ str_errno = 0;
+ sz = lseek(h->bstr_fd, 0L, SEEK_END);
+ sz -= PRE_DEF_AU_HDR_LEN;
+ sz = CPU_TO_BE32(sz);
+ if (lseek(h->bstr_fd, 8L, SEEK_SET) < 0)
+ return (1);
+
+ if (write(h->bstr_fd, &sz, 4) < 0)
+ return (1);
+
+ (void) close(h->bstr_fd);
+ free(h);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+stdin_stream_rewind(bstreamhandle h)
+{
+}
+
+static void
+file_stream_rewind(bstreamhandle h)
+{
+ (void) lseek(h->bstr_fd, 0L, SEEK_SET);
+}
+
+static void
+au_stream_rewind(bstreamhandle h)
+{
+ au_filehdr_t au;
+
+ (void) lseek(h->bstr_fd, 0L, SEEK_SET);
+ if (read(h->bstr_fd, &au, sizeof (au)) != sizeof (au)) {
+ return;
+ }
+
+ if (lseek(h->bstr_fd, (long)(BE32_TO_CPU(au.au_offset)),
+ SEEK_SET) < 0) {
+ return;
+ }
+}
+
+static void
+wav_stream_rewind(bstreamhandle h)
+{
+ (void) lseek(h->bstr_fd, (long)(sizeof (Wave_filehdr)), SEEK_SET);
+}
+
+bstreamhandle
+open_file_read_stream(char *file)
+{
+ bstreamhandle h;
+ int fd;
+ struct stat st;
+
+ str_errno = 0;
+ if (stat(file, &st) < 0)
+ return (NULL);
+ if ((st.st_mode & S_IFMT) == S_IFDIR) {
+ str_errno = STR_ERR_NO_REG_FILE;
+ return (NULL);
+ }
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_read = file_stream_read;
+ h->bstr_close = file_stream_close;
+ h->bstr_size = file_stream_size;
+ h->bstr_rewind = file_stream_rewind;
+
+ return (h);
+}
+
+bstreamhandle
+open_stdin_read_stream(void)
+{
+ bstreamhandle h;
+ int mode;
+
+ str_errno = 0;
+ if ((mode = fcntl(0, F_GETFD, NULL)) < 0) {
+ str_errno = STR_ERR_NO_READ_STDIN;
+ return (NULL);
+ }
+ mode &= 3;
+ if ((mode != O_RDONLY) && (mode != O_RDWR)) {
+ str_errno = STR_ERR_NO_READ_STDIN;
+ return (NULL);
+ }
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = 0;
+ h->bstr_read = file_stream_read;
+ h->bstr_close = stdin_stream_close;
+ h->bstr_size = file_stream_size;
+ h->bstr_rewind = stdin_stream_rewind;
+
+ return (h);
+}
+
+bstreamhandle
+open_au_read_stream(char *fname)
+{
+ bstreamhandle h;
+ int fd, sav;
+ au_filehdr_t *au;
+ struct stat st;
+ uint32_t data_size;
+
+ au = NULL;
+ str_errno = 0;
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ if (fstat(fd, &st) < 0) {
+ goto au_open_failed;
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ str_errno = STR_ERR_NO_REG_FILE;
+ goto au_open_failed;
+ }
+ au = (au_filehdr_t *)my_zalloc(sizeof (*au));
+ if (read(fd, au, sizeof (*au)) != sizeof (*au)) {
+ str_errno = STR_ERR_AU_READ_ERR;
+ goto au_open_failed;
+ }
+ au->au_magic = BE32_TO_CPU(au->au_magic);
+ au->au_offset = BE32_TO_CPU(au->au_offset);
+ au->au_data_size = BE32_TO_CPU(au->au_data_size);
+ au->au_encoding = BE32_TO_CPU(au->au_encoding);
+ au->au_sample_rate = BE32_TO_CPU(au->au_sample_rate);
+ au->au_channels = BE32_TO_CPU(au->au_channels);
+
+ if (au->au_magic != AUDIO_AU_FILE_MAGIC) {
+ str_errno = STR_ERR_AU_BAD_HEADER;
+ goto au_open_failed;
+ }
+ if ((au->au_encoding != AUDIO_AU_ENCODING_LINEAR_16) ||
+ (au->au_sample_rate != 44100) || (au->au_channels != 2)) {
+
+ str_errno = STR_ERR_AU_UNSUPPORTED_FORMAT;
+ goto au_open_failed;
+ }
+ if (au->au_data_size != AUDIO_AU_UNKNOWN_SIZE) {
+ if ((au->au_offset + au->au_data_size) != st.st_size) {
+ str_errno = STR_ERR_AU_BAD_HEADER;
+ goto au_open_failed;
+ }
+ data_size = au->au_data_size;
+ } else {
+ data_size = st.st_size - au->au_offset;
+ }
+ if (data_size == 0) {
+ str_errno = STR_ERR_AU_UNSUPPORTED_FORMAT;
+ goto au_open_failed;
+ }
+ if (lseek(fd, au->au_offset, SEEK_SET) < 0) {
+ goto au_open_failed;
+ }
+
+ free(au);
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_read = file_stream_read_wrbo;
+ h->bstr_close = file_stream_close;
+ h->bstr_size = audio_stream_size;
+ h->bstr_rewind = au_stream_rewind;
+ h->bstr_private = (void *)data_size;
+
+ return (h);
+
+au_open_failed:
+ sav = errno;
+ (void) close(fd);
+ if (au != NULL)
+ free(au);
+ errno = sav;
+ return (NULL);
+}
+
+bstreamhandle
+open_wav_read_stream(char *fname)
+{
+ bstreamhandle h;
+ int fd, sav;
+ Wave_filehdr *wav;
+ struct stat st;
+ uint32_t data_size;
+
+ wav = NULL;
+ str_errno = 0;
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ if (fstat(fd, &st) < 0) {
+ goto wav_open_failed;
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ str_errno = STR_ERR_NO_REG_FILE;
+ goto wav_open_failed;
+ }
+ wav = (Wave_filehdr *)my_zalloc(sizeof (*wav));
+ if (read(fd, wav, sizeof (*wav)) != sizeof (*wav)) {
+ str_errno = STR_ERR_WAV_READ_ERR;
+ goto wav_open_failed;
+ }
+ if ((strncmp(wav->riff, "RIFF", 4) != 0) ||
+ (strncmp(wav->wave, "WAVE", 4) != 0)) {
+ str_errno = STR_ERR_WAV_BAD_HEADER;
+ goto wav_open_failed;
+ }
+ if (((CPU_TO_LE32(wav->total_chunk_size) + 8) != st.st_size) ||
+ (strncmp(wav->fmt, "fmt ", 4) != 0) ||
+ (CPU_TO_LE16(wav->fmt_tag) != 1) ||
+ (CPU_TO_LE16(wav->n_channels) != 2) ||
+ (CPU_TO_LE32(wav->sample_rate) != 44100) ||
+ (CPU_TO_LE16(wav->bits_per_sample) != 16) ||
+ (strncmp(wav->data, "data", 4) != 0) ||
+ ((CPU_TO_LE32(wav->data_size) + 44) != st.st_size)) {
+
+ str_errno = STR_ERR_WAV_UNSUPPORTED_FORMAT;
+ goto wav_open_failed;
+ }
+ data_size = CPU_TO_LE32(wav->data_size);
+ if (lseek(fd, sizeof (*wav), SEEK_SET) < 0) {
+ goto wav_open_failed;
+ }
+
+ free(wav);
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_read = file_stream_read;
+ h->bstr_close = file_stream_close;
+ h->bstr_size = audio_stream_size;
+ h->bstr_rewind = wav_stream_rewind;
+ h->bstr_private = (void *)data_size;
+
+ return (h);
+
+wav_open_failed:
+ sav = errno;
+ (void) close(fd);
+ if (wav != NULL)
+ free(wav);
+ errno = sav;
+ return (NULL);
+}
+
+bstreamhandle
+open_aur_read_stream(char *fname)
+{
+ bstreamhandle h;
+
+ h = open_file_read_stream(fname);
+ if (h != NULL) {
+ h->bstr_read = file_stream_read_wrbo;
+ }
+ return (h);
+}
+
+bstreamhandle
+open_au_write_stream(char *fname)
+{
+ bstreamhandle h;
+ int esav, fd;
+ uchar_t head[] = PRE_DEF_AU_HDR;
+
+ str_errno = 0;
+ fd = -1;
+ /* O_RDWR because we need to read while closing */
+ fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0)
+ goto open_au_write_stream_failed;
+ if (write(fd, head, PRE_DEF_AU_HDR_LEN) != PRE_DEF_AU_HDR_LEN) {
+ goto open_au_write_stream_failed;
+ }
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_write = file_stream_write_wrbo;
+ h->bstr_close = au_write_stream_close;
+ return (h);
+
+open_au_write_stream_failed:
+ esav = errno;
+ if (fd != -1)
+ (void) close(fd);
+ errno = esav;
+ return (NULL);
+}
+
+bstreamhandle
+open_wav_write_stream(char *fname)
+{
+ bstreamhandle h;
+ int esav, fd;
+ uchar_t head[] = PRE_DEF_WAV_HDR;
+
+ str_errno = 0;
+ fd = -1;
+ fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0)
+ goto open_wav_write_stream_failed;
+ if (write(fd, head, PRE_DEF_WAV_HDR_LEN) != PRE_DEF_WAV_HDR_LEN) {
+ goto open_wav_write_stream_failed;
+ }
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_write = file_stream_write;
+ h->bstr_close = wav_write_stream_close;
+ return (h);
+
+open_wav_write_stream_failed:
+ esav = errno;
+ if (fd != -1)
+ (void) close(fd);
+ errno = esav;
+ return (NULL);
+}
+
+bstreamhandle
+open_aur_write_stream(char *fname)
+{
+ bstreamhandle h;
+ int fd;
+
+ str_errno = 0;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0)
+ return (NULL);
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_write = file_stream_write_wrbo;
+ h->bstr_close = file_stream_close;
+ return (h);
+}
+
+bstreamhandle
+open_file_write_stream(char *fname)
+{
+ bstreamhandle h;
+ int fd;
+
+ str_errno = 0;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0)
+ return (NULL);
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_write = file_stream_write;
+ h->bstr_close = file_stream_close;
+ return (h);
+}
+
+bstreamhandle
+open_temp_file_stream(void)
+{
+ bstreamhandle h;
+ char *t;
+ int fd;
+
+ str_errno = 0;
+
+ t = (char *)get_tmp_name();
+
+ if (strlcat(t, "/cdXXXXXX", PATH_MAX) >= PATH_MAX)
+ return (NULL);
+
+ fd = mkstemp(t);
+
+ if (debug)
+ (void) printf("temp is: %s length: %d\n", t, strlen(t));
+
+ if (fd < 0)
+ return (NULL);
+ (void) unlink(t);
+
+ h = (bstreamhandle)my_zalloc(sizeof (*h));
+ h->bstr_fd = fd;
+ h->bstr_read = file_stream_read;
+ h->bstr_write = file_stream_write;
+ h->bstr_close = file_stream_close;
+ h->bstr_size = file_stream_size;
+ h->bstr_rewind = file_stream_rewind;
+
+ return (h);
+}
+
+int
+check_avail_temp_space(off_t req_size)
+{
+ char *t;
+ struct statvfs buf;
+ uint64_t free_size;
+
+
+ t = get_tmp_name();
+
+ if (statvfs(t, &buf) < 0)
+ return (0);
+
+ free_size = (uint64_t)buf.f_bfree;
+ free_size *= (uint64_t)buf.f_frsize;
+
+ if (free_size <= ((uint64_t)req_size))
+ return (0);
+
+ /* path directory and there is enough free space */
+ return (1);
+}
+
+
+char *
+get_tmp_name(void)
+{
+ char *t;
+ char *envptr;
+
+ t = (char *)my_zalloc(PATH_MAX);
+
+ /*
+ * generate temp directory path based on this order:
+ * user specified (-m option), temp env variable,
+ * and finally /tmp if nothing is found.
+ */
+
+ if (alt_tmp_dir) {
+
+ /* copy and leave room for temp filename */
+
+ (void) strlcpy(t, alt_tmp_dir, PATH_MAX - 10);
+ } else {
+ envptr = getenv("TMPDIR");
+ if (envptr != NULL) {
+ (void) strlcpy(t, envptr, PATH_MAX - 10);
+ } else {
+ (void) strlcpy(t, "/tmp", 5);
+ }
+ }
+
+ /*
+ * no need to check if path is valid. statvfs will catch
+ * it later and fail with a proper error message.
+ */
+
+ return (t);
+}
diff --git a/usr/src/cmd/cdrw/bstream.h b/usr/src/cmd/cdrw/bstream.h
new file mode 100644
index 0000000000..a2d2cbbbab
--- /dev/null
+++ b/usr/src/cmd/cdrw/bstream.h
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _BSTREAM_H
+#define _BSTREAM_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+struct _bstr_hndl {
+ int bstr_fd;
+ int (*bstr_read)(struct _bstr_hndl *h, uchar_t *buf, off_t size);
+ int (*bstr_write)(struct _bstr_hndl *h, uchar_t *buf, off_t size);
+ int (*bstr_close)(struct _bstr_hndl *h);
+ int (*bstr_size)(struct _bstr_hndl *h, off_t *size);
+ void (*bstr_rewind)(struct _bstr_hndl *h);
+ void *bstr_private;
+};
+typedef struct _bstr_hndl *bstreamhandle;
+
+extern int str_errno;
+/*
+ * str_errno values
+ */
+#define STR_ERR_NO_ERR 0
+#define STR_ERR_NO_REG_FILE 1
+#define STR_ERR_NO_READ_STDIN 2
+#define STR_ERR_AU_READ_ERR 3
+#define STR_ERR_AU_UNSUPPORTED_FORMAT 4
+#define STR_ERR_AU_BAD_HEADER 5
+#define STR_ERR_WAV_READ_ERR 6
+#define STR_ERR_WAV_UNSUPPORTED_FORMAT 7
+#define STR_ERR_WAV_BAD_HEADER 8
+
+bstreamhandle open_stdin_read_stream();
+bstreamhandle open_file_read_stream(char *file);
+bstreamhandle open_au_read_stream(char *fname);
+bstreamhandle open_wav_read_stream(char *fname);
+bstreamhandle open_aur_read_stream(char *fname);
+bstreamhandle open_au_write_stream(char *fname);
+bstreamhandle open_wav_write_stream(char *fname);
+bstreamhandle open_aur_write_stream(char *fname);
+bstreamhandle open_file_write_stream(char *fname);
+bstreamhandle open_temp_file_stream(void);
+
+char *str_errno_to_string(int serrno);
+int check_avail_temp_space(off_t req_size);
+char *get_tmp_name(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BSTREAM_H */
diff --git a/usr/src/cmd/cdrw/byteorder.h b/usr/src/cmd/cdrw/byteorder.h
new file mode 100644
index 0000000000..c978f4bf8a
--- /dev/null
+++ b/usr/src/cmd/cdrw/byteorder.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#define swap32(x) \
+((uint32_t)((((uint32_t)(x) & 0x000000ff) << 24) | \
+(((uint32_t)(x) & 0x0000ff00) << 8) | \
+(((uint32_t)(x) & 0x00ff0000) >> 8) | \
+(((uint32_t)(x) & 0xff000000) >> 24)))
+
+#define swap16(x) \
+((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+(((uint16_t)(x) & 0xff00) >> 8)))
+
+/*
+ * endianess issues
+ */
+#if defined(_BIG_ENDIAN)
+#define CPU_TO_LE32(x) swap32(x)
+#define CPU_TO_LE16(x) swap16(x)
+#define CPU_TO_BE32(x) (x)
+#define CPU_TO_BE16(x) (x)
+#else
+#define CPU_TO_LE32(x) (x)
+#define CPU_TO_LE16(x) (x)
+#define CPU_TO_BE32(x) swap32(x)
+#define CPU_TO_BE16(x) swap16(x)
+#endif
+
+#define LE32_TO_CPU(x) CPU_TO_LE32(x)
+#define LE16_TO_CPU(x) CPU_TO_LE16(x)
+#define BE32_TO_CPU(x) CPU_TO_BE32(x)
+#define BE16_TO_CPU(x) CPU_TO_BE16(x)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BYTEORDER_H */
diff --git a/usr/src/cmd/cdrw/copycd.c b/usr/src/cmd/cdrw/copycd.c
new file mode 100644
index 0000000000..21bab5c87b
--- /dev/null
+++ b/usr/src/cmd/cdrw/copycd.c
@@ -0,0 +1,427 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include "main.h"
+#include "util.h"
+#include "misc_scsi.h"
+#include "mmc.h"
+#include "bstream.h"
+#include "device.h"
+#include "msgs.h"
+#include "transport.h"
+
+struct t_data {
+ bstreamhandle h;
+ struct track_info ti;
+};
+
+int read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h);
+
+#define READ_BURST 24 /* < 64K in all cases */
+
+/*
+ * This reads the data off of a cd while updating the progress indicator.
+ * We want to do this in smaller chunks since some CD drives have
+ * problems with larger reads.
+ */
+static int
+read_data_track(cd_device *dev, struct track_info *ti, bstreamhandle h)
+{
+ int blksize;
+ uint32_t blks_read, cblk, read_chunk, read_size;
+ uchar_t *buf;
+ int ret, sav;
+ int link_blks_count;
+
+ buf = NULL;
+ ret = 0;
+
+ /*
+ * the last link_blks_count blocks may not exist or be completely
+ * filled. We need to record the amount to avoid bailing out if
+ * they cannot be read.
+ */
+
+ if (dev->d_blksize == 512) {
+ blksize = 512;
+ link_blks_count = 8;
+ } else {
+ blksize = 2048;
+ link_blks_count = 2;
+ }
+
+ buf = (uchar_t *)my_zalloc(READ_BURST * blksize);
+
+ print_n_flush(gettext("Reading track %d..."), ti->ti_track_no);
+
+ if (verbose)
+ print_n_flush("Track size is %u...", ti->ti_track_size);
+
+ init_progress();
+ cblk = ti->ti_start_address;
+ blks_read = 0;
+ while (blks_read < ti->ti_track_size) {
+ /* Last few are special */
+ read_chunk = ti->ti_track_size - blks_read - link_blks_count;
+ read_chunk = (read_chunk > READ_BURST) ? READ_BURST :
+ read_chunk;
+ if (read_chunk == 0) {
+ /* Time for last link blocks */
+ read_chunk = link_blks_count;
+ }
+ read_size = read_chunk * blksize;
+ if (read10(dev->d_fd, cblk, read_chunk, buf, read_size)) {
+ if (h->bstr_write(h, buf, read_size) != read_size) {
+ goto read_data_track_failed;
+ }
+ } else {
+ if (blks_read !=
+ (ti->ti_track_size - link_blks_count)) {
+ goto read_data_track_failed;
+ } else {
+ /* Read can fail for last link sectors */
+ errno = 0;
+ }
+ }
+ blks_read += read_chunk;
+ cblk += read_chunk;
+ (void) progress((void *)(ti->ti_track_size), blks_read);
+ }
+ /* l10n_NOTE : 'done' as in "Reading track 1...done" */
+ (void) str_print(gettext("done.\n"), progress_pos);
+ ret = 1;
+read_data_track_failed:
+ sav = errno;
+
+ free(buf);
+ errno = sav;
+ return (ret);
+}
+
+static void
+ensure_media_space(uint32_t total_nblks, uchar_t end_tno)
+{
+ off_t nblks_avail;
+ uint_t bsize;
+
+ get_media_type(target->d_fd);
+
+ if (use_media_stated_capacity) {
+ nblks_avail = get_last_possible_lba(target);
+
+ if (nblks_avail <= 0) {
+
+ /* most newer drives use READ FORMAT CAPACITY */
+ nblks_avail = read_format_capacity(target->d_fd,
+ &bsize);
+
+ /* if both methods fail no choice but to bail out */
+ if (nblks_avail <= 0) {
+
+ err_msg(gettext(
+ "Cannot find out media capacity.\n"));
+ exit(1);
+ }
+ }
+ } else {
+ if (device_type == CD_RW) {
+ nblks_avail = MAX_CD_BLKS;
+ } else {
+ /*
+ * For DVD drives use read_format_capacity as default
+ * retrieve the media size, it can be 3.6, 3.9, 4.2,
+ * 4.7, or 9.2 GB
+ */
+ nblks_avail =
+ read_format_capacity(target->d_fd, &bsize);
+
+ /* sanity check. if not reasonable default to 4.7 GB */
+ if (nblks_avail < MAX_CD_BLKS) {
+ nblks_avail = MAX_DVD_BLKS;
+ }
+ }
+ }
+
+ if ((total_nblks + end_tno*300) > nblks_avail) {
+ err_msg(gettext("Not enough space on the media.\n"));
+ if (debug) {
+ (void) printf("Need %u only found %u \n",
+ (total_nblks + end_tno*300),
+ (uint32_t)nblks_avail);
+ }
+
+ exit(1);
+ }
+}
+
+/*
+ * This copies both audio and data CDs. It first reads the TOC of the source CD
+ * and creates a temp file with the CD image. After this is completed it creates
+ * the target CD using TAO mode.
+ */
+void
+copy_cd(void)
+{
+ cd_device *src;
+ char *p;
+ uchar_t *toc, end_tno;
+ int blksize, i;
+ int audio_cd, data_cd;
+ uint32_t total_nblks;
+ int ret;
+ struct t_data *tlist;
+
+ print_n_flush(gettext("Analyzing source CD..."));
+ (void) check_device(target,
+ CHECK_DEVICE_NOT_WRITABLE|EXIT_IF_CHECK_FAILED);
+
+ /* if source drive is specified on the command line */
+
+ if (copy_src) {
+ p = my_zalloc(PATH_MAX);
+ if (lookup_device(copy_src, p) == 0) {
+ err_msg(gettext("Cannot find device %s"), copy_src);
+ err_msg(gettext(" or no media in the drive\n"));
+ exit(1);
+ }
+ src = get_device(copy_src, p);
+ if (src == NULL) {
+ err_msg(gettext("Unable to open %s\n"), copy_src);
+ exit(1);
+ }
+ free(p);
+ } else {
+ /* source is same as target drive */
+ src = target;
+ }
+
+ (void) check_device(src, CHECK_TYPE_NOT_CDROM | CHECK_NO_MEDIA |
+ CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED);
+
+ toc = (uchar_t *)my_zalloc(4);
+ if (!read_toc(src->d_fd, 0, 0, 4, toc)) {
+ err_msg(gettext("Cannot read table of contents\n"));
+ exit(1);
+ }
+ end_tno = toc[3];
+ free(toc);
+ tlist = (struct t_data *)my_zalloc(end_tno * sizeof (struct t_data));
+
+ audio_cd = data_cd = 0;
+ total_nblks = 0;
+
+ /* build track information so we can copy it over */
+ for (i = 1; i <= end_tno; i++) {
+ struct track_info *ti;
+
+ ti = &tlist[i - 1].ti;
+ if (!build_track_info(src, i, ti)) {
+ err_msg(gettext(
+ "Cannot get information for track %d\n"), i);
+ exit(1);
+ }
+ total_nblks += ti->ti_track_size;
+ if (ti->ti_track_mode & 4)
+ data_cd = 1;
+ else
+ audio_cd = 1;
+
+ /* Now some sanity checks on the track information */
+ if ((ti->ti_flags & TI_SESSION_NO_VALID) &&
+ (ti->ti_session_no != 1)) {
+ err_msg(
+ gettext("Copying multisession CD is not supported\n"));
+ exit(1);
+ }
+ if ((ti->ti_flags & TI_PACKET_MODE) ||
+ (ti->ti_flags & TI_BLANK_TRACK) ||
+ (ti->ti_flags & TI_DAMAGED_TRACK) ||
+ (data_cd && audio_cd) || (ti->ti_data_mode == 2)) {
+
+ err_msg(gettext("CD format is not supported\n"));
+ exit(1);
+ }
+ if ((ti->ti_flags & TI_NWA_VALID) &&
+ (ti->ti_nwa != 0xffffffff)) {
+ err_msg(gettext("Cannot copy incomplete discs\n"));
+ exit(1);
+ }
+ }
+ /* l10n_NOTE : 'done' as in "Analyzing source CD...done" */
+ (void) printf(gettext("done.\n"));
+
+ if (data_cd) {
+ blksize = 2048;
+ } else {
+ /* audio cd */
+ blksize = 2352;
+ }
+
+ /* In case of audio CDs, build_track_info() returns 2352 sized nblks */
+ if (src->d_blksize == 512 && data_cd) {
+ total_nblks /= 4;
+ }
+ (void) printf(gettext("\nCopying %d %s track%s : %ld kbytes\n\n"),
+ end_tno, (audio_cd == 1) ? gettext("audio") : gettext("data"),
+ (end_tno > 1) ? "s" : "", (long)((total_nblks*blksize)/1024));
+
+ if (!check_avail_temp_space(total_nblks*blksize)) {
+ err_msg(gettext("Not enough space in temporary directory\n"));
+ err_msg(
+ gettext("Use -m to specify alternate temporary directory\n"));
+ exit(1);
+ }
+
+ /*
+ * If we can check available space on the target media at this
+ * Stage, then it is always better. We cannot check DVD+R(W)
+ * as this media may be formatted and not blank.
+ */
+ if (target && (src != target) && (device_type != DVD_PLUS) &&
+ (device_type != DVD_PLUS_W) && (!check_device(target,
+ CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK))) {
+ ensure_media_space(total_nblks, end_tno);
+ }
+
+ /* for each track */
+ for (i = 1; i <= end_tno; i++) {
+ tlist[i - 1].h = open_temp_file_stream();
+ if (tlist[i - 1].h == NULL) {
+ err_msg(gettext("Cannot create temporary file : %s\n"),
+ get_err_str());
+ exit(1);
+ }
+
+ if (audio_cd)
+ ret = read_audio_track(src, &tlist[i - 1].ti,
+ tlist[i - 1].h);
+ else
+ ret = read_data_track(src, &tlist[i - 1].ti,
+ tlist[i - 1].h);
+ if (ret == 0) {
+ err_msg(gettext("Error reading track %d : %s\n"), i,
+ get_err_str());
+ if (debug)
+ (void) printf("%x %x %x %x\n", uscsi_status,
+ SENSE_KEY(rqbuf), ASC(rqbuf), ASCQ(rqbuf));
+ exit(1);
+ }
+ }
+
+ /*
+ * We've finished copying the CD. If source and destination are the same
+ * or they where not specified then eject the disk and wait for a new
+ * disk to be inserted.
+ */
+
+ while ((target == NULL) ||
+ check_device(target, CHECK_NO_MEDIA|CHECK_MEDIA_IS_NOT_BLANK)) {
+ if (target != NULL) {
+ (void) eject_media(target);
+ }
+
+ (void) printf("\n");
+ print_n_flush(
+ gettext("Insert a blank media in the drive and press Enter."));
+ (void) fflush(stdin);
+ if (target) {
+ fini_device(target);
+ target = NULL;
+ }
+ (void) getchar();
+ (void) sleep(4);
+ (void) setup_target(SCAN_WRITERS);
+ }
+ (void) printf("\n");
+ (void) setreuid(ruid, 0);
+
+ if ((device_type != DVD_PLUS) && (device_type != DVD_PLUS_W)) {
+ ensure_media_space(total_nblks, end_tno);
+ write_init(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA);
+ }
+
+ /* for each track */
+ for (i = 0; i < end_tno; i++) {
+ /*
+ * DVD's dont contain tracks and need to be written in DAO
+ * mode.
+ */
+ if (device_type != CD_RW) {
+ if (end_tno > 1) {
+ err_msg(gettext(
+ "Media state is not suitable for this"
+ " write mode.\n"));
+ }
+ write_mode = DAO_MODE;
+
+ /*
+ * DVD-R(W) and DVD+R needs to have space reserved
+ * prior to writing.
+ */
+ if ((device_type == DVD_MINUS) ||
+ (device_type == DVD_PLUS)) {
+ if (!set_reservation(target->d_fd,
+ total_nblks + 1)) {
+ (void) printf(gettext(
+ "Setting reservation failed\n"));
+ exit(1);
+ }
+ }
+ }
+
+ write_next_track(audio_cd ? TRACK_MODE_AUDIO : TRACK_MODE_DATA,
+ tlist[i].h);
+
+ /*
+ * Running in simulation mode and writing several tracks is
+ * useless so bail after the first track is done.
+ */
+
+ if (simulation && (end_tno != 1)) {
+ (void) printf(gettext(
+ "Simulation mode : skipping remaining tracks\n"));
+ break;
+ }
+ }
+
+ write_fini();
+ /* close the temp file handles */
+ for (i = 0; i < end_tno; i++)
+ (tlist[i].h)->bstr_close(tlist[i].h);
+ free(tlist);
+ fini_device(target);
+ exit(0);
+}
diff --git a/usr/src/cmd/cdrw/dae.c b/usr/src/cmd/cdrw/dae.c
new file mode 100644
index 0000000000..cc02b5ae0b
--- /dev/null
+++ b/usr/src/cmd/cdrw/dae.c
@@ -0,0 +1,342 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <signal.h>
+
+#include "bstream.h"
+#include "util.h"
+#include "misc_scsi.h"
+#include "device.h"
+#include "main.h"
+#include "msgs.h"
+
+#define BLOCK_SIZE 2352
+#define READ_BURST_SIZE 200
+#define SMALL_READ_BURST_SIZE 24 /* < 64K in all cases */
+#define READ_OVERLAP 7
+#define BLOCKS_COMPARE 3
+
+static int abort_read;
+
+/*
+ * These are routines for extracting audio from a cd. During
+ * extraction we will also convert the audio type from the
+ * CD to the audio type specified on the command line. This
+ * handles both newer CD drives which support the MMC2 standard
+ * and older Sun Toshiba drives which need jitter correction.
+ */
+
+static bstreamhandle
+open_audio_for_extraction(char *fname)
+{
+ int at;
+ char *ext;
+
+ if (audio_type == AUDIO_TYPE_NONE) {
+ ext = (char *)(strrchr(fname, '.'));
+ if (ext) {
+ ext++;
+ }
+ if ((ext == NULL) || ((at = get_audio_type(ext)) == -1)) {
+ err_msg(gettext(
+ "Cannot understand file extension for %s\n"),
+ fname);
+ exit(1);
+ }
+ } else {
+ at = audio_type;
+ }
+ if (at == AUDIO_TYPE_SUN)
+ return (open_au_write_stream(fname));
+ if (at == AUDIO_TYPE_WAV)
+ return (open_wav_write_stream(fname));
+ if (at == AUDIO_TYPE_CDA)
+ return (open_file_write_stream(fname));
+ if (at == AUDIO_TYPE_AUR)
+ return (open_aur_write_stream(fname));
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void
+extract_signal_handler(int sig, siginfo_t *info, void *context)
+{
+ abort_read = 1;
+}
+
+/*
+ * Older drives use different data buffer and m:s:f channels to transmit audio
+ * information. These channels may not be in sync with each other with the
+ * maximum disparity being the size of the data buffer. So handling is needed
+ * to keep these two channels in sync.
+ */
+
+static int
+handle_jitter(uchar_t *buf, uchar_t *last_end)
+{
+ int i;
+ for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE); i >= 0; i -= 4) {
+ if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i,
+ BLOCK_SIZE * BLOCKS_COMPARE) == 0) {
+ return (i + (BLOCK_SIZE * BLOCKS_COMPARE));
+ }
+ }
+ for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE);
+ i < 2*READ_OVERLAP*BLOCK_SIZE; i += 4) {
+ if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i,
+ BLOCK_SIZE * BLOCKS_COMPARE) == 0) {
+ return (i + (BLOCK_SIZE * BLOCKS_COMPARE));
+ }
+ }
+ return (-1);
+}
+
+int
+read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h)
+{
+ uint32_t blocks_to_write, blocks_to_read, blks_to_overlap;
+ uint32_t start_blk, end_blk, c_blk;
+ uint32_t read_burst_size;
+ uchar_t *tmp, *buf, *prev, *previous_end;
+ int ret, off;
+ struct sigaction sv;
+ struct sigaction oldsv;
+
+ ret = 0;
+ abort_read = 0;
+
+ /*
+ * It is good to do small sized I/Os as we have seen many devices
+ * choke with large I/Os. But if the device does not support
+ * reading accurate CDDA then we have to do overlapped I/Os
+ * and reducing size might affect performance. So use small
+ * I/O size if device supports accurate CDDA.
+ */
+ if (dev->d_cap & DEV_CAP_ACCURATE_CDDA) {
+ read_burst_size = SMALL_READ_BURST_SIZE;
+ } else {
+ read_burst_size = READ_BURST_SIZE;
+ }
+ buf = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size);
+ prev = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size);
+ start_blk = ti->ti_start_address;
+ end_blk = ti->ti_start_address + ti->ti_track_size - 1;
+
+ /* Even when we need jitter correction, this will be 0 1st time */
+ blks_to_overlap = 0;
+ off = 0;
+
+ /* set up signal handler to write audio TOC if ^C is pressed */
+ sv.sa_handler = extract_signal_handler;
+ (void) sigemptyset(&sv.sa_mask);
+ sv.sa_flags = 0;
+ (void) sigaction(SIGINT, &sv, &oldsv);
+
+ if ((dev->d_cap & DEV_CAP_EXTRACT_CDDA) == 0) {
+ err_msg(gettext("Audio extraction method unknown for %s\n"),
+ dev->d_name ? dev->d_name : gettext("CD drive"));
+ exit(1);
+ }
+
+ /* if the speed option given, try to change the speed */
+ if ((requested_speed != 0) && !cflag) {
+ if (verbose)
+ (void) printf(gettext("Trying to set speed to %dX.\n"),
+ requested_speed);
+ if (dev->d_speed_ctrl(dev, SET_READ_SPEED,
+ requested_speed) == 0) {
+
+ err_msg(gettext("Unable to set speed.\n"));
+ exit(1);
+ }
+ if (verbose) {
+ int speed;
+ speed = dev->d_speed_ctrl(dev, GET_READ_SPEED, 0);
+ if (speed == requested_speed) {
+ (void) printf(gettext("Speed set to %dX.\n"),
+ speed);
+ } else {
+ (void) printf(gettext(
+ "Speed set to closest approximation "));
+
+ (void) printf(gettext(
+ "of %dX allowed by device (%dX).\n"),
+ requested_speed, speed);
+ }
+ }
+ }
+
+ print_n_flush(
+ gettext("Extracting audio from track %d..."), ti->ti_track_no);
+ init_progress();
+
+ if (debug)
+ (void) printf("\nStarting: %d Ending: %d\n",
+ start_blk, end_blk);
+
+ blocks_to_write = 0;
+
+ for (c_blk = start_blk; c_blk < end_blk; c_blk += blocks_to_write) {
+ /* update progress indicator */
+ (void) progress((void *) (end_blk - start_blk),
+ (int64_t)(c_blk - start_blk));
+ blocks_to_read = end_blk - c_blk + blks_to_overlap;
+
+ /*
+ * Make sure we don't read more blocks than the maximum
+ * burst size.
+ */
+
+ if (blocks_to_read > read_burst_size)
+ blocks_to_read = read_burst_size;
+
+ if (dev->d_read_audio(dev, c_blk - blks_to_overlap,
+ blocks_to_read, buf) == 0)
+ goto read_audio_track_done;
+
+ /*
+ * This drive supports accurate audio extraction don't
+ * do jitter correction.
+ */
+ if ((c_blk == start_blk) ||
+ (dev->d_cap & DEV_CAP_ACCURATE_CDDA)) {
+ blocks_to_write = blocks_to_read;
+ previous_end = buf + (blocks_to_write * BLOCK_SIZE);
+ goto skip_jitter_correction;
+ }
+
+ if (c_blk == start_blk)
+ blks_to_overlap = 0;
+ else
+ blks_to_overlap = READ_OVERLAP;
+ off = handle_jitter(buf, previous_end);
+ if (off == -1) {
+ if (debug)
+ (void) printf(
+ "jitter control failed\n");
+
+ /* recover if jitter correction failed */
+ off = BLOCK_SIZE * BLOCKS_COMPARE;
+ }
+
+ blocks_to_write = blocks_to_read - blks_to_overlap;
+
+ while ((off + (blocks_to_write*BLOCK_SIZE)) >
+ (blocks_to_read * BLOCK_SIZE)) {
+ blocks_to_write--;
+ }
+
+ if ((blocks_to_write + c_blk) > end_blk) {
+ blocks_to_write = end_blk - c_blk;
+ }
+
+ if (blocks_to_write == 0) {
+ c_blk = end_blk - 1;
+ blocks_to_write = 1;
+ (void) memset(&buf[off], 0, off % BLOCK_SIZE);
+ }
+
+ previous_end = buf + off + blocks_to_write * BLOCK_SIZE;
+skip_jitter_correction:
+ (void) memcpy(prev, buf, read_burst_size * BLOCK_SIZE);
+ if (h->bstr_write(h, &buf[off], blocks_to_write*BLOCK_SIZE)
+ < 0)
+ goto read_audio_track_done;
+ tmp = buf;
+ buf = prev;
+ prev = tmp;
+
+ if (abort_read == 1)
+ goto read_audio_track_done;
+ }
+
+ ret = 1;
+ (void) str_print(gettext("done.\n"), progress_pos);
+
+read_audio_track_done:
+ (void) sigaction(SIGINT, &oldsv, (struct sigaction *)0);
+
+ free(buf);
+ free(prev);
+ return (ret);
+}
+
+void
+extract_audio(void)
+{
+ bstreamhandle h;
+ struct track_info *ti;
+
+ (void) check_device(target, CHECK_NO_MEDIA | CHECK_DEVICE_NOT_READY |
+ EXIT_IF_CHECK_FAILED);
+
+ ti = (struct track_info *)my_zalloc(sizeof (*ti));
+ if (!build_track_info(target, extract_track_no, ti)) {
+ err_msg(gettext("Cannot get track information for track %d\n"),
+ extract_track_no);
+ exit(1);
+ }
+
+ /* Verify track */
+ if ((ti->ti_track_size == 0) || ((ti->ti_flags & TI_NWA_VALID) &&
+ (ti->ti_start_address == ti->ti_nwa))) {
+ err_msg(gettext("Track %d is empty\n"), extract_track_no);
+ exit(1);
+ }
+ if (ti->ti_track_mode & 4) {
+ err_msg(gettext("Track %d is not an audio track\n"),
+ extract_track_no);
+ exit(1);
+ }
+ if (ti->ti_data_mode == 2) {
+ err_msg(gettext("Track format is not supported\n"));
+ exit(1);
+ }
+
+ h = open_audio_for_extraction(extract_file);
+ if (h == NULL) {
+ err_msg(gettext("Cannot open %s:%s\n"), extract_file,
+ get_err_str());
+ exit(1);
+ }
+ if (read_audio_track(target, ti, h) == 0) {
+ err_msg(gettext("Extract audio failed\n"));
+ h->bstr_close(h);
+ exit(1);
+ }
+ if (h->bstr_close(h) != 0) {
+ err_msg(gettext("Error closing audio stream : %s\n"),
+ get_err_str());
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/usr/src/cmd/cdrw/device.c b/usr/src/cmd/cdrw/device.c
new file mode 100644
index 0000000000..2442486c72
--- /dev/null
+++ b/usr/src/cmd/cdrw/device.c
@@ -0,0 +1,853 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <volmgt.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/dkio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <limits.h>
+
+#include "transport.h"
+#include "mmc.h"
+#include "device.h"
+#include "util.h"
+#include "msgs.h"
+#include "misc_scsi.h"
+#include "toshiba.h"
+#include "main.h"
+
+/*
+ * Old sun drives have a vendor specific mode page for setting/getting speed.
+ * Also they use a different method for extracting audio.
+ * We have the device inquiry strings at this time. This is used to enable
+ * us to use older sun drives to extract audio.
+ */
+static int
+is_old_sun_drive(cd_device *dev)
+{
+ /*
+ * If we have a SONY CDU 561, CDU 8012, or TOSHIBA model with XMa we
+ * need to handle these drives a bit differently.
+ */
+ if (strncmp("SONY", (const char *)&dev->d_inq[8], 4) == 0) {
+ if (strncmp("CDU 561", (const char *)&dev->d_inq[16], 7) == 0)
+ return (1);
+ if (strncmp("CDU-8012", (const char *)&dev->d_inq[16], 8) == 0)
+ return (1);
+ }
+
+ if ((strncmp("TOSHIBA", (const char *)&dev->d_inq[8], 7) == 0) &&
+ (strncmp("XM", (const char *)&dev->d_inq[16], 2) == 0)) {
+
+ char product_id[17];
+
+ /* Changing speed is not allowed for 32X TOSHIBA drives */
+ if (strncmp("SUN32XCD", (const char *)&dev->d_inq[24], 8) == 0)
+ dev->d_cap |= DEV_CAP_SETTING_SPEED_NOT_ALLOWED;
+ (void) strncpy(product_id, (const char *)&dev->d_inq[16], 16);
+ product_id[16] = 0;
+ if (strstr(product_id, "SUN") != NULL)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * returns a cd_device handle for a node returned by lookup_device()
+ * also takes the user supplied name and stores it inside the node
+ */
+cd_device *
+get_device(char *user_supplied, char *node)
+{
+ cd_device *dev;
+ int fd;
+ uchar_t *cap;
+ char devnode[PATH_MAX];
+ int size;
+ struct dk_minfo mediainfo;
+ int use_cd_speed = 0;
+
+ /*
+ * we need to resolve any link paths to avoid fake files
+ * such as /dev/rdsk/../../export/file.
+ */
+
+ TRACE(traceall_msg("get_device(%s, %s)\n", user_supplied ?
+ user_supplied : "<nil>", node ? node : "<nil>"));
+
+ size = resolvepath(node, devnode, PATH_MAX);
+ if ((size <= 0) || (size >= (PATH_MAX - 1)))
+ return (NULL);
+
+ /* resolvepath may not return a null terminated string */
+ devnode[size] = '\0';
+
+
+ /* the device node must be in /devices/ or /vol/dev/rdsk */
+
+ if ((strncmp(devnode, "/devices/", 9) != 0) &&
+ (strncmp(devnode, "/vol/dev/rdsk", 13) != 0))
+ return (NULL);
+ /*
+ * Since we are currently running with the user euid it is
+ * safe to try to open the file without checking access.
+ */
+
+ fd = open(devnode, O_RDONLY|O_NDELAY);
+
+ if (fd < 0) {
+ TRACE(traceall_msg("Cannot open %s: %s\n", node,
+ strerror(errno)));
+ return (NULL);
+ }
+
+ dev = (cd_device *)my_zalloc(sizeof (cd_device));
+
+ dev->d_node = (char *)my_zalloc(strlen(devnode) + 1);
+ (void) strcpy(dev->d_node, devnode);
+
+ dev->d_fd = fd;
+
+ dev->d_inq = (uchar_t *)my_zalloc(INQUIRY_DATA_LENGTH);
+
+ if (!inquiry(fd, dev->d_inq)) {
+ TRACE(traceall_msg("Inquiry failed on device %s\n", node));
+ if (debug) {
+ (void) printf("USCSI ioctl failed %d\n",
+ uscsi_error);
+ }
+ free(dev->d_inq);
+ free(dev->d_node);
+ (void) close(dev->d_fd);
+ free(dev);
+ return (NULL);
+ }
+
+ if (debug) {
+ cap = (uchar_t *)my_zalloc(18);
+ (void) printf("Checking device type\n");
+ if (get_mode_page(fd, 0x2A, 0, 8, cap)) {
+ if (cap[2] & 0x10)
+ (void) printf("DVD-R read support\n");
+ if (cap[3] & 0x10)
+ (void) printf("DVD-R write support\n");
+ if (cap[5] & 0x4)
+ (void) printf("R-W supported\n");
+ if (cap[2] & 0x20)
+ (void) printf("DVD-RAM read supported\n");
+ if (cap[3] & 0x20)
+ (void) printf("DVD-RAM write supported\n");
+ } else {
+ (void) printf("Could not read mode page 2A! \n");
+ }
+ free(cap);
+ }
+
+ /* Detect if it's a Lite-ON drive with a streaming CD problem */
+ if ((strncmp("LITE-ON", (const char *)&dev->d_inq[8], 7) == 0) &&
+ (strncmp("LTR-48", (const char *)&dev->d_inq[16], 6) == 0)) {
+ use_cd_speed = 1;
+ }
+
+ cap = (uchar_t *)my_zalloc(8);
+ if (is_old_sun_drive(dev)) {
+ dev->d_read_audio = toshiba_read_audio;
+ dev->d_speed_ctrl = toshiba_speed_ctrl;
+ } else if (use_cd_speed) {
+ /*
+ * Work around for Lite-on FW bug in which rt_speed_ctrl
+ * doesn't work correctly.
+ */
+ dev->d_speed_ctrl = cd_speed_ctrl;
+ } else {
+ /*
+ * If feature 8 (see GET CONF MMC command) is supported,
+ * READ CD will work and will return jitter free audio data.
+ * Otherwise look at page code 2A for this info.
+ */
+ if (get_configuration(fd, 0x1E, 8, cap)) {
+ dev->d_read_audio = read_audio_through_read_cd;
+ dev->d_cap |= DEV_CAP_ACCURATE_CDDA;
+ } else if (get_mode_page(fd, 0x2A, 0, 8, cap)) {
+ if (cap[5] & 1) {
+ dev->d_read_audio = read_audio_through_read_cd;
+ if (cap[5] & 2)
+ dev->d_cap |= DEV_CAP_ACCURATE_CDDA;
+ }
+ }
+ /*
+ * If feature 0x0107 is suported then Real-time streaming
+ * commands can be used for speed control. Otherwise try
+ * SET CD SPEED.
+ */
+ if (get_configuration(fd, 0x0107, 8, cap)) {
+ dev->d_speed_ctrl = rt_streaming_ctrl;
+ if (debug)
+ (void) printf("using rt speed ctrl\n");
+ } else {
+ dev->d_speed_ctrl = cd_speed_ctrl;
+ if (debug)
+ (void) printf("using cd speed ctrl\n");
+ }
+ }
+ if (dev->d_read_audio != NULL)
+ dev->d_cap |= DEV_CAP_EXTRACT_CDDA;
+
+ dev->d_blksize = 0;
+
+ /*
+ * Find the block size of the device so we can translate
+ * the reads/writes to the device blocksize.
+ */
+
+ if (ioctl(fd, DKIOCGMEDIAINFO, &mediainfo) < 0) {
+
+ /*
+ * If DKIOCGMEDIAINFO fails we'll try to get
+ * the blocksize from the device itself.
+ */
+
+ if (debug)
+ (void) printf("DKIOCGMEDIAINFO failed\n");
+
+ if (read_capacity(fd, cap))
+ dev->d_blksize = read_scsi32(cap + 4);
+ } else {
+
+ dev->d_blksize = mediainfo.dki_lbsize;
+ }
+
+ if (debug) {
+ uint_t bsize;
+
+ (void) printf("blocksize = %d\n", dev->d_blksize);
+ (void) printf("read_format_capacity = %d \n",
+ read_format_capacity(fd, &bsize));
+ }
+
+ /*
+ * Some devices will return invalid blocksizes. ie. Toshiba
+ * drives will return 2352 when an audio CD is inserted.
+ * Older Sun drives will use 512 byte block sizes. All newer
+ * drives should have 2k blocksizes.
+ */
+ if (((dev->d_blksize != 512) && (dev->d_blksize != 2048))) {
+ if (is_old_sun_drive(dev)) {
+ dev->d_blksize = 512;
+ } else {
+ dev->d_blksize = 2048;
+ }
+ if (debug)
+ (void) printf(" switching to %d\n", dev->d_blksize);
+ }
+
+ free(cap);
+ if (user_supplied) {
+ dev->d_name = (char *)my_zalloc(strlen(user_supplied) + 1);
+ (void) strcpy(dev->d_name, user_supplied);
+ }
+ TRACE(traceall_msg("Got device %s\n", node));
+ return (dev);
+}
+
+void
+fini_device(cd_device *dev)
+{
+ free(dev->d_inq);
+ free(dev->d_node);
+ (void) close(dev->d_fd);
+ if (dev->d_name)
+ free(dev->d_name);
+ free(dev);
+}
+
+static int
+vol_name_to_dev_node(char *vname, char *found)
+{
+ struct stat statbuf;
+ char *p1;
+ int i;
+
+ if (vname == NULL)
+ return (0);
+ if (vol_running)
+ (void) volmgt_check(vname);
+ p1 = media_findname(vname);
+ if (p1 == NULL)
+ return (0);
+ if (stat(p1, &statbuf) < 0) {
+ free(p1);
+ return (0);
+ }
+ if (statbuf.st_mode & S_IFDIR) {
+ for (i = 0; i < 16; i++) {
+ (void) snprintf(found, PATH_MAX, "%s/s%d", p1, i);
+ if (access(found, F_OK) >= 0)
+ break;
+ }
+ if (i == 16) {
+ free(p1);
+ return (0);
+ }
+ } else {
+ (void) strlcpy(found, p1, PATH_MAX);
+ }
+ free(p1);
+ return (1);
+}
+
+/*
+ * Searches for volume manager's equivalent char device for the
+ * supplied pathname which is of the form of /dev/rdsk/cxtxdxsx
+ */
+static int
+vol_lookup(char *supplied, char *found)
+{
+ char tmpstr[PATH_MAX], tmpstr1[PATH_MAX], *p;
+ int i, ret;
+
+ (void) strlcpy(tmpstr, supplied, PATH_MAX);
+ if ((p = volmgt_symname(tmpstr)) == NULL) {
+ if (strrchr(tmpstr, 's') == NULL)
+ return (0);
+ *((char *)(strrchr(tmpstr, 's') + 1)) = 0;
+ for (i = 0; i < 16; i++) {
+ (void) snprintf(tmpstr1, PATH_MAX, "%s%d", tmpstr, i);
+ if ((p = volmgt_symname(tmpstr1)) != NULL)
+ break;
+ }
+ if (p == NULL)
+ return (0);
+ }
+ ret = vol_name_to_dev_node(p, found);
+ free(p);
+ return (ret);
+}
+
+/*
+ * Builds an open()able device path from a user supplied node which can be
+ * of the * form of /dev/[r]dsk/cxtxdx[sx] or cxtxdx[sx] or volmgt-name like
+ * cdrom[n]
+ * returns the path found in 'found' and returns 1. Otherwise returns 0.
+ */
+int
+lookup_device(char *supplied, char *found)
+{
+ struct stat statbuf;
+ int fd;
+ char tmpstr[PATH_MAX];
+
+ /* If everything is fine and proper, no need to analyze */
+ if ((stat(supplied, &statbuf) == 0) && (statbuf.st_mode & S_IFCHR) &&
+ ((fd = open(supplied, O_RDONLY|O_NDELAY)) >= 0)) {
+ (void) close(fd);
+ (void) strlcpy(found, supplied, PATH_MAX);
+ return (1);
+ }
+ if (strncmp(supplied, "/dev/rdsk/", 10) == 0)
+ return (vol_lookup(supplied, found));
+ if (strncmp(supplied, "/dev/dsk/", 9) == 0) {
+ (void) snprintf(tmpstr, PATH_MAX, "/dev/rdsk/%s",
+ (char *)strrchr(supplied, '/'));
+
+ if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
+ (void) close(fd);
+ (void) strlcpy(found, supplied, PATH_MAX);
+ return (1);
+ }
+ if ((access(tmpstr, F_OK) == 0) && vol_running)
+ return (vol_lookup(tmpstr, found));
+ else
+ return (0);
+ }
+ if ((strncmp(supplied, "cdrom", 5) != 0) &&
+ (strlen(supplied) < 32)) {
+ (void) snprintf(tmpstr, sizeof (tmpstr), "/dev/rdsk/%s",
+ supplied);
+ if (access(tmpstr, F_OK) < 0) {
+ (void) strcat(tmpstr, "s2");
+ }
+ if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
+ (void) close(fd);
+ (void) strlcpy(found, tmpstr, PATH_MAX);
+ return (1);
+ }
+ if ((access(tmpstr, F_OK) == 0) && vol_running)
+ return (vol_lookup(tmpstr, found));
+ }
+ return (vol_name_to_dev_node(supplied, found));
+}
+
+/*
+ * Opens the device node name passed and returns 1 (true) if the
+ * device is a CD.
+ */
+
+static int
+is_cd(char *node)
+{
+ int fd;
+ struct dk_cinfo cinfo;
+ int ret = 1;
+
+ fd = open(node, O_RDONLY|O_NDELAY);
+ if (fd < 0) {
+ ret = 0;
+ } else if (ioctl(fd, DKIOCINFO, &cinfo) < 0) {
+ ret = 0;
+ } else if (cinfo.dki_ctype != DKC_CDROM) {
+ ret = 0;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+ return (ret);
+}
+
+static void
+print_header(void)
+{
+ /* l10n_NOTE : Column spacing should be kept same */
+ (void) printf(gettext(" Node Connected Device"));
+ /* l10n_NOTE : Column spacing should be kept same */
+ (void) printf(gettext(" Device type\n"));
+ (void) printf(
+ "----------------------+--------------------------------");
+ (void) printf("+-----------------\n");
+}
+
+/*
+ * returns the number of writers or CD/DVD-roms found and the path of
+ * the first device found depending on the mode argument.
+ * possible mode values are:
+ * SCAN_ALL_CDS Scan all CD/DVD devices. Return first CD-RW found.
+ * SCAN_WRITERS Scan all CD-RW devices. Return first one found.
+ * SCAN_LISTDEVS List all devices found.
+ */
+int
+scan_for_cd_device(int mode, cd_device **found)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ char sdev[PATH_MAX], dev[PATH_MAX];
+ cd_device *t_dev;
+ int writers_found = 0;
+ int header_printed = 0;
+ int is_writer;
+ int retry = 0;
+ int total_devices_found;
+ int total_devices_found_last_time = 0;
+ int defer = 0;
+
+#define MAX_RETRIES_FOR_SCANNING 5
+
+ TRACE(traceall_msg("scan_for_cd_devices (mode=%d) called\n", mode));
+
+ if (mode) {
+ if (vol_running)
+ defer = 1;
+ (void) printf(gettext("Looking for CD devices...\n"));
+ }
+try_again:
+
+ dir = opendir("/dev/rdsk");
+ if (dir == NULL)
+ return (0);
+
+ writers_found = 0;
+ total_devices_found = 0;
+ while ((dirent = readdir(dir)) != NULL) {
+ if (dirent->d_name[0] == '.')
+ continue;
+ (void) snprintf(sdev, PATH_MAX, "/dev/rdsk/%s",
+ dirent->d_name);
+ if (strcmp("s2", (char *)strrchr(sdev, 's')) != 0)
+ continue;
+ if (!lookup_device(sdev, dev))
+ continue;
+ if (!is_cd(dev))
+ continue;
+ if ((t_dev = get_device(NULL, dev)) == NULL) {
+ continue;
+ }
+ total_devices_found++;
+
+ is_writer = !(check_device(t_dev, CHECK_DEVICE_NOT_WRITABLE));
+
+ if (is_writer) {
+ writers_found++;
+
+ if ((writers_found == 1) && (mode != SCAN_LISTDEVS)) {
+ *found = t_dev;
+ }
+
+ } else if ((mode == SCAN_ALL_CDS) && (writers_found == 0) &&
+ (total_devices_found == 1) && found) {
+
+ /* We found a CD-ROM or DVD-ROM */
+ *found = t_dev;
+ }
+
+ if ((mode == SCAN_LISTDEVS) && (!defer)) {
+ char *sn;
+
+ sn = volmgt_symname(sdev);
+ if (!header_printed) {
+ print_header();
+ header_printed = 1;
+ }
+ /* show vendor, model, firmware rev and device type */
+ (void) printf(" %-21.21s| %.8s %.16s %.4s | %s%s\n",
+ sn ? sn : sdev, &t_dev->d_inq[8],
+ &t_dev->d_inq[16], &t_dev->d_inq[32],
+ gettext("CD Reader"),
+ is_writer ? gettext("/Writer") : "");
+ if (sn)
+ free(sn);
+ }
+ if ((found != NULL) && ((*found) != t_dev))
+ fini_device(t_dev);
+ }
+
+ (void) closedir(dir);
+
+
+ /*
+ * If volume manager is running we'll do a retry in case the
+ * user has just inserted media, This should allow vold
+ * enough time to create the device links. If we cannot
+ * find any devices, or we find any new devices we'll retry
+ * again up to MAX_RETRIES_FOR_SCANNING
+ */
+
+ retry++;
+
+ if ((retry <= MAX_RETRIES_FOR_SCANNING) && vol_running) {
+ if (defer || ((mode != SCAN_LISTDEVS) &&
+ (writers_found == 0))) {
+
+ if ((total_devices_found == 0) ||
+ (total_devices_found !=
+ total_devices_found_last_time)) {
+
+ /* before we rescan remove the device found */
+ if (total_devices_found != 0 && t_dev) {
+ fini_device(t_dev);
+ }
+
+ total_devices_found_last_time =
+ total_devices_found;
+ (void) sleep(2);
+ goto try_again;
+ } else {
+
+ /* Do the printing this time */
+ defer = 0;
+ goto try_again;
+
+ }
+
+ }
+ }
+ if ((mode & SCAN_WRITERS) || writers_found)
+ return (writers_found);
+ else
+ return (total_devices_found);
+}
+
+/*
+ * Check device for various conditions/capabilities
+ * If EXIT_IF_CHECK_FAILED set in cond then it will also exit after
+ * printing a message.
+ */
+int
+check_device(cd_device *dev, int cond)
+{
+ uchar_t *disc_info, disc_status = 0, erasable = 0;
+ uchar_t page_code[4];
+ char *errmsg = NULL;
+
+ if ((errmsg == NULL) && (cond & CHECK_TYPE_NOT_CDROM) &&
+ ((dev->d_inq[0] & 0x1f) != 5)) {
+ errmsg =
+ gettext("Specified device does not appear to be a CDROM");
+ }
+
+ if ((errmsg == NULL) && (cond & CHECK_DEVICE_NOT_READY) &&
+ !test_unit_ready(dev->d_fd)) {
+ errmsg = gettext("Device not ready");
+ }
+
+ /* Look at the capabilities page for this information */
+ if ((errmsg == NULL) && (cond & CHECK_DEVICE_NOT_WRITABLE)) {
+ if (!get_mode_page(dev->d_fd, 0x2a, 0, 4, page_code) ||
+ ((page_code[3] & 1) == 0)) {
+ errmsg = gettext("Target device is not a CD writer");
+ }
+ }
+
+ if ((errmsg == NULL) && (cond & CHECK_NO_MEDIA)) {
+ if (!test_unit_ready(dev->d_fd) && (uscsi_status == 2) &&
+ ((RQBUFLEN - rqresid) >= 14) &&
+ ((SENSE_KEY(rqbuf) & 0x0f) == 2) && (ASC(rqbuf) == 0x3A) &&
+ ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
+ (ASCQ(rqbuf) == 2))) {
+ /* medium not present */
+ errmsg = gettext("No media in device");
+ }
+ }
+
+
+
+ /* Issue READ DISC INFORMATION mmc command */
+ if ((errmsg == NULL) && ((cond & CHECK_MEDIA_IS_NOT_BLANK) ||
+ (cond & CHECK_MEDIA_IS_NOT_WRITABLE) ||
+ (cond & CHECK_MEDIA_IS_NOT_ERASABLE))) {
+
+ disc_info = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
+ if (!read_disc_info(dev->d_fd, disc_info)) {
+ errmsg = gettext("Cannot obtain disc information");
+ } else {
+ disc_status = disc_info[2] & 0x03;
+ erasable = disc_info[2] & 0x10;
+ }
+ free(disc_info);
+ if (errmsg == NULL) {
+ if (!erasable && (cond & CHECK_MEDIA_IS_NOT_ERASABLE))
+ errmsg = gettext(
+ "Media in the device is not erasable");
+ else if ((disc_status != 0) &&
+ (cond & CHECK_MEDIA_IS_NOT_BLANK))
+ errmsg = gettext(
+ "Media in the device is not blank");
+ else if ((disc_status == 2) &&
+ (cond & CHECK_MEDIA_IS_NOT_WRITABLE) &&
+ ((device_type != DVD_PLUS_W) &&
+ (device_type != DVD_PLUS)))
+ errmsg = gettext(
+ "Media in the device is not writable");
+ }
+ }
+
+ if (errmsg) {
+ if (cond & EXIT_IF_CHECK_FAILED) {
+ err_msg("%s.\n", errmsg);
+ exit(1);
+ }
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Generic routine for writing whatever the next track is and taking
+ * care of the progress bar. Mode tells the track type (audio or data).
+ * Data from track is taken from the byte stream h
+ */
+void
+write_next_track(int mode, bstreamhandle h)
+{
+ struct track_info *ti;
+ struct trackio_error *te;
+ off_t size;
+
+ ti = (struct track_info *)my_zalloc(TRACK_INFO_SIZE);
+ if ((build_track_info(target, -1, ti) == 0) ||
+ ((ti->ti_flags & TI_NWA_VALID) == 0)) {
+ if ((device_type == DVD_PLUS) || (device_type ==
+ DVD_PLUS_W)) {
+ ti->ti_flags |= TI_NWA_VALID;
+ } else {
+ err_msg(gettext(
+ "Cannot get writable address for the media.\n"));
+ exit(1);
+ }
+ }
+ if (ti->ti_nwa != ti->ti_start_address) {
+ err_msg(gettext(
+ "Media state is not suitable for this write mode.\n"));
+ exit(1);
+ }
+ if (mode == TRACK_MODE_DATA) {
+ if (!(ti->ti_track_mode & 4)) {
+ /* Write track depends upon this bit */
+ ti->ti_track_mode |= TRACK_MODE_DATA;
+ }
+ }
+ size = 0;
+ h->bstr_size(h, &size);
+ h->bstr_rewind(h);
+ te = (struct trackio_error *)my_zalloc(sizeof (*te));
+
+ print_n_flush(gettext("Writing track %d..."), (int)ti->ti_track_no);
+ init_progress();
+ if (!write_track(target, ti, h, progress, (void *)size, te)) {
+ if (te->err_type == TRACKIO_ERR_USER_ABORT) {
+ (void) str_print(gettext("Aborted.\n"), progress_pos);
+ } else {
+ if (device_type != DVD_PLUS_W) {
+ /* l10n_NOTE : 'failed' as in Writing Track...failed */
+ (void) str_print(gettext("failed.\n"),
+ progress_pos);
+ }
+ }
+ }
+ /* l10n_NOTE : 'done' as in "Writing track 1...done" */
+ (void) str_print(gettext("done.\n"), progress_pos);
+ free(ti);
+ free(te);
+}
+
+void
+list(void)
+{
+ if (scan_for_cd_device(SCAN_LISTDEVS, NULL) == 0) {
+ if (vol_running) {
+ err_msg(gettext(
+ "No CD writers found or no media in the drive.\n"));
+ } else {
+ if (cur_uid != 0) {
+ err_msg(gettext(
+ "Volume manager is not running.\n"));
+ err_msg(gettext(
+"Please start volume manager or run cdrw as root to access all devices.\n"));
+ } else {
+ err_msg(gettext("No CD writers found.\n"));
+ }
+ }
+ }
+ exit(0);
+}
+
+void
+get_media_type(int fd)
+{
+ uchar_t cap[DVD_CONFIG_SIZE];
+ uint_t i;
+
+
+ (void) memset(cap, 0, DVD_CONFIG_SIZE);
+ if (get_configuration(fd, 0, DVD_CONFIG_SIZE, cap)) {
+ if (debug && verbose) {
+ (void) printf("\nprofile = ");
+
+ for (i = 7; i < 0x20; i += 4)
+ (void) printf(" 0x%x", cap[i]);
+ (void) printf("\n");
+ }
+
+ switch (cap[7]) {
+ case 0x8: /* CD-ROM */
+ if (debug)
+ (void) printf("CD-ROM found\n");
+ /*
+ * To avoid regression issues, treat as
+ * A cdrw, we will check the writable
+ * mode page to see if the media is
+ * actually writable.
+ */
+ device_type = CD_RW;
+ break;
+
+ case 0x9: /* CD-R */
+ if (debug)
+ (void) printf("CD-R found\n");
+ device_type = CD_RW;
+ break;
+
+ case 0x10: /* DVD-ROM */
+ /*
+ * Have seen drives return DVD+RW media
+ * DVD-ROM, so try treating it as a DVD+RW
+ * profile. checking for writable media
+ * is done through mode page 5.
+ */
+ if (debug)
+ (void) printf("DVD-ROM found\n");
+ device_type = DVD_PLUS_W;
+ break;
+
+ case 0xA: /* CD-RW */
+ if (debug)
+ (void) printf("CD-RW found\n");
+ device_type = CD_RW;
+ break;
+
+ case 0x11: /* DVD-R */
+ if (debug)
+ (void) printf("DVD-R found\n");
+ device_type = DVD_MINUS;
+ break;
+
+ case 0x12: /* DVD-RAM */
+ if (debug)
+ (void) printf("DVD-RAM found\n");
+ /* treat as CD-RW, may be a legacy drive */
+ device_type = CD_RW;
+ break;
+
+ case 0x13: /* DVD-RW restricted overwrite */
+ case 0x14: /* DVD-RW sequencial */
+ if (debug)
+ (void) printf("DVD-RW found\n");
+ device_type = DVD_MINUS;
+ break;
+
+ case 0x1A: /* DVD+RW */
+ if (debug)
+ (void) printf("DVD+RW found\n");
+
+ device_type = DVD_PLUS_W;
+ break;
+ case 0x1B: /* DVD+R */
+ if (debug)
+ (void) printf("DVD+R found\n");
+ device_type = DVD_PLUS;
+ break;
+
+ default:
+ if (debug)
+ (void) printf(
+ "unknown drive found\n type = 0x%x",
+ cap[7]);
+ /*
+ * Treat as CD_RW to avoid regression, may
+ * be a legacy drive.
+ */
+ device_type = CD_RW;
+ }
+ }
+}
diff --git a/usr/src/cmd/cdrw/device.h b/usr/src/cmd/cdrw/device.h
new file mode 100644
index 0000000000..0cfb9a0774
--- /dev/null
+++ b/usr/src/cmd/cdrw/device.h
@@ -0,0 +1,85 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DEVICE_H
+#define _DEVICE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#include "bstream.h"
+
+#define DEFAULT_CAPACITY (74*60*75)
+
+#define DEV_CAP_EXTRACT_CDDA 1
+#define DEV_CAP_ACCURATE_CDDA 2
+#define DEV_CAP_SETTING_SPEED_NOT_ALLOWED 4
+
+typedef struct _device {
+ char *d_node;
+ char *d_name;
+ int d_fd;
+ uint_t d_blksize;
+ uchar_t *d_inq;
+ uint32_t d_cap;
+ int (*d_read_audio)(struct _device *dev, uint_t start_blk,
+ uint_t nblks, uchar_t *buf);
+ int (*d_speed_ctrl)(struct _device *dev, int cmd,
+ int speed);
+} cd_device;
+
+/*
+ * Speed commands
+ */
+#define GET_READ_SPEED 1
+#define SET_READ_SPEED 2
+#define GET_WRITE_SPEED 3
+#define SET_WRITE_SPEED 4
+
+#define SCAN_ALL_CDS 0x00 /* scan and return all cd devices */
+#define SCAN_WRITERS 0x01 /* select only writable devices */
+#define SCAN_LISTDEVS 0x02 /* print out device listing */
+
+#define DVD_CONFIG_SIZE 0x20
+
+cd_device *get_device(char *user_supplied, char *node);
+int lookup_device(char *supplied, char *found);
+void fini_device(cd_device *dev);
+int scan_for_cd_device(int mode, cd_device **found);
+void write_next_track(int mode, bstreamhandle h);
+int check_device(cd_device *dev, int cond);
+void get_media_type(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEVICE_H */
diff --git a/usr/src/cmd/cdrw/dumpinfo.c b/usr/src/cmd/cdrw/dumpinfo.c
new file mode 100644
index 0000000000..586339a13e
--- /dev/null
+++ b/usr/src/cmd/cdrw/dumpinfo.c
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libintl.h>
+
+#include "msgs.h"
+#include "mmc.h"
+#include "misc_scsi.h"
+#include "device.h"
+#include "main.h"
+#include "util.h"
+#include "toshiba.h"
+
+void
+info(void)
+{
+ uchar_t *toc, *p;
+ int ret, toc_size;
+ char *msg;
+ struct track_info *ti;
+
+ msg = gettext("Cannot read Table of contents\n");
+
+ get_media_type(target->d_fd);
+
+ (void) printf(gettext("\nDevice : %.8s %.16s\n"),
+ &target->d_inq[8], &target->d_inq[16]);
+ (void) printf(gettext("Firmware : Rev. %.4s (%.12s)\n"),
+ &target->d_inq[32], &target->d_inq[36]);
+
+ if (check_device(target, CHECK_DEVICE_NOT_READY)) {
+ (void) check_device(target, CHECK_NO_MEDIA |
+ EXIT_IF_CHECK_FAILED);
+ (void) check_device(target, CHECK_DEVICE_NOT_READY |
+ EXIT_IF_CHECK_FAILED);
+ }
+ if (!check_device(target, CHECK_MEDIA_IS_NOT_BLANK)) {
+ (void) printf(gettext("Media is blank\n"));
+ exit(0);
+ }
+
+ /* Find out the number of entries in the toc */
+ toc = (uchar_t *)my_zalloc(12);
+ if (!read_toc(target->d_fd, 0, 1, 4, toc)) {
+ err_msg(msg);
+ } else {
+ toc_size = 256*toc[0] + toc[1] + 2;
+ free(toc);
+
+ /* allocate enough space for each track entry */
+ toc = (uchar_t *)my_zalloc(toc_size);
+
+ if (!read_toc(target->d_fd, 0, 1, toc_size, toc)) {
+ err_msg(msg);
+ exit(1);
+ }
+ (void) printf("\n");
+
+ /* l10n_NOTE : Preserve column numbers of '|' character */
+ (void) printf(gettext("Track No. |Type |Start address\n"));
+ (void) printf("----------+--------+-------------\n");
+
+
+ /* look at each track and display it's type. */
+
+ for (p = &toc[4]; p < (toc + toc_size); p += 8) {
+ if (p[2] != 0xAA)
+ (void) printf(" %-3d |", p[2]);
+ else
+ (void) printf("Leadout |");
+ (void) printf("%s |", (p[1] & 4) ? gettext("Data ") :
+ gettext("Audio"));
+ (void) printf("%u\n", read_scsi32(&p[4]));
+ }
+ }
+ (void) printf("\n");
+ ret = read_toc(target->d_fd, 1, 0, 12, toc);
+ if ((ret == 0) || (toc[1] != 0x0a))
+ /* For ATAPI drives or old Toshiba drives */
+ ret = read_toc_as_per_8020(target->d_fd, 1, 0, 12, toc);
+
+ if (ret && (toc[1] == 0x0a)) {
+ (void) printf(gettext("Last session start address: %u\n"),
+ read_scsi32(&toc[8]));
+ }
+ free(toc);
+ ti = (struct track_info *)my_zalloc(sizeof (struct track_info));
+
+ if (build_track_info(target, -1, ti) && (ti->ti_flags & TI_NWA_VALID)) {
+ (void) printf(gettext("Next writable address: %u\n"),
+ ti->ti_nwa);
+ }
+ free(ti);
+ exit(0);
+}
diff --git a/usr/src/cmd/cdrw/main.c b/usr/src/cmd/cdrw/main.c
new file mode 100644
index 0000000000..d10cb1a54b
--- /dev/null
+++ b/usr/src/cmd/cdrw/main.c
@@ -0,0 +1,399 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <volmgt.h>
+
+#include "msgs.h"
+#include "device.h"
+#include "util.h"
+#include "main.h"
+#include "options.h"
+#include "mmc.h"
+#include "misc_scsi.h"
+
+/*
+ * global flags
+ */
+int debug = 0;
+int use_media_stated_capacity = 0;
+int keep_disc_open = 0;
+int requested_speed = 0;
+int simulation = 0;
+int verbose = 0;
+char *image_file = NULL;
+char *blanking_type = NULL;
+int audio_type = AUDIO_TYPE_NONE;
+int extract_track_no = 0;
+char *extract_file = NULL;
+char *alt_tmp_dir = NULL;
+char *copy_src = NULL;
+int vol_running = 0;
+int cflag = 0;
+int tflag = 0;
+uid_t ruid, cur_uid;
+
+/*
+ * global variables
+ */
+cd_device *target = NULL; /* Default target device */
+static char *tgtdev = NULL;
+int device_type = CD_RW; /* Default to CD/RW */
+int write_mode = TAO_MODE; /* Default to track at once */
+
+static void
+print_usage(void)
+{
+ err_msg(gettext("USAGE:\n"));
+ err_msg(gettext("\tcdrw -i [ -vSCO ] [ -d device ] [ -p speed ]"));
+ err_msg(gettext(" [ image-file ]\n"));
+ err_msg(gettext("\tcdrw -a [ -vSCO ] [ -d device ] [ -p speed ]"));
+ err_msg(gettext(" [ -T audio-type ] audio-file1 audio-file2 ...\n"));
+ err_msg(gettext("\tcdrw -x [ -v ] [ -d device ] [ -T audio-type ]"));
+ err_msg(gettext(" track-number audio-file\n"));
+ err_msg(gettext("\tcdrw -c [ -cSC ] [ -d device ] [ -p speed ]"));
+ err_msg(gettext(" [ -m tmp-dir ] [ -s src-device ]\n"));
+ err_msg(
+ gettext("\tcdrw -b [ -v ] [ -d device ] all | session | fast\n"));
+ err_msg(gettext("\tcdrw -M [ -v ] [ -d device ]\n"));
+ err_msg(gettext("\tcdrw -L [v] [ -d device ]\n"));
+ err_msg(gettext("\tcdrw -l [ -v ]\n"));
+ err_msg(gettext("\tcdrw -h\n"));
+
+ exit(2);
+}
+
+static void
+check_invalid_option(options *specified, char *opstr)
+{
+ options c_op;
+ int ret;
+
+ set_options_mask(&c_op, opstr);
+ if ((ret = compare_options_mask(&c_op, specified)) != 0) {
+ err_msg(
+ gettext("Option %c is not defined for this operation.\n"),
+ (char)ret);
+ print_usage();
+ }
+}
+
+int
+setup_target(int flag)
+{
+ char *devpath;
+
+ if (tgtdev != NULL) {
+ devpath = (char *)my_zalloc(PATH_MAX);
+ if (lookup_device(tgtdev, devpath)) {
+ target = get_device(tgtdev, devpath);
+ }
+ free(devpath);
+ if (target == NULL) {
+ return (0);
+ }
+ return (1);
+ }
+ return (scan_for_cd_device(flag, &target));
+}
+
+void
+main(int argc, char **argv)
+{
+ int c;
+ int operations;
+ options specified_ops;
+ int aflag, iflag, Mflag, Lflag, lflag, bflag, xflag;
+ int ret;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ ruid = getuid();
+ cur_uid = geteuid();
+
+ if (check_auth(ruid) != 1) {
+ err_msg(gettext(
+ "Authorization failed, Cannot access disks.\n"));
+ exit(1);
+ }
+
+ if ((cur_uid == 0) && (ruid != 0)) {
+ priv_change_needed = 1;
+ lower_priv();
+ }
+
+ vol_running = volmgt_running();
+
+ tgtdev = NULL;
+ operations = 0;
+ set_options_mask(&specified_ops, "");
+ iflag = Mflag = Lflag = lflag = bflag = aflag = xflag = cflag = 0;
+
+ while ((c = getopt(argc, argv, "abcCd:hiLlm:MOp:s:ST:vVx")) != EOF) {
+ add_option(&specified_ops, c);
+ switch (c) {
+ case 'a':
+ aflag = 1;
+ operations++;
+ break;
+ case 'b':
+ bflag = 1;
+ operations++;
+ break;
+ case 'c':
+ cflag = 1;
+ operations++;
+ break;
+ case 'C':
+ use_media_stated_capacity = 1;
+ break;
+ case 'd':
+ tgtdev = optarg;
+ break;
+ case 'h':
+ print_usage(); /* will not return */
+ break;
+ case 'i':
+ iflag = 1;
+ operations++;
+ break;
+ case 'L':
+ Lflag = 1;
+ operations++;
+ break;
+ case 'l':
+ lflag = 1;
+ operations++;
+ break;
+ case 'm':
+ alt_tmp_dir = optarg;
+ break;
+ case 'M':
+ Mflag = 1;
+ operations++;
+ break;
+ case 'O':
+ keep_disc_open = 1;
+ break;
+ case 'p':
+ requested_speed = atoi(optarg);
+ break;
+ case 's':
+ copy_src = optarg;
+ break;
+ case 'S':
+ simulation++;
+ break;
+ case 'T':
+ audio_type = get_audio_type(optarg);
+ if (audio_type == -1) {
+ err_msg(gettext("Unknown audio type %s\n"),
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'V':
+ /*
+ * more verbose. this will print out debug comments
+ */
+
+ debug++;
+ break;
+ case 'x':
+ xflag++;
+ operations++;
+ break;
+ default:
+ print_usage();
+ }
+ }
+ if (operations == 0) {
+ err_msg(gettext("No operation specified.\n"));
+ exit(1);
+ }
+ if (operations != 1) {
+ err_msg(gettext("More than one operation specified.\n"));
+ exit(1);
+ }
+
+ if (lflag) {
+ check_invalid_option(&specified_ops, "lhvV");
+ list();
+ }
+
+ /*
+ * we'll allow the user to specify the source device (-s) when
+ * extracting audio.
+ */
+
+ if (xflag && copy_src)
+ tgtdev = copy_src;
+
+ /*
+ * This will scan for all CD devices when xflag or Mflag
+ * (extract audio, list toc) commands are used, providing
+ * no CD-RW devices are found. Since these commands can
+ * be used without a CD writer.
+ */
+
+ if (xflag || Mflag) {
+ ret = setup_target(SCAN_ALL_CDS);
+ } else {
+ ret = setup_target(SCAN_WRITERS);
+ }
+
+ if (ret == 0) {
+
+ if (tgtdev != NULL) {
+ err_msg(gettext(
+ "Cannot find device %s.\n"), tgtdev);
+
+ }
+
+ if (vol_running) {
+ err_msg(gettext(
+ "No CD writers found or no media in the drive.\n"));
+ } else {
+ if (cur_uid != 0) {
+ err_msg(gettext(
+ "Volume manager is not running.\n"));
+ err_msg(gettext(
+"Please start volume manager or run cdrw as root to access all devices.\n"));
+ } else {
+ err_msg(gettext(
+ "No CD writers found.\n"));
+ }
+ }
+ exit(1);
+
+ } else if (ret != 1) {
+ err_msg(gettext("More than one CD device found.\n"));
+ err_msg(gettext("Specify one using -d option.\n"));
+ err_msg(gettext(
+ "Or use -l option to list all the CD devices found\n"));
+ exit(1);
+ }
+ (void) check_device(target, CHECK_TYPE_NOT_CDROM|EXIT_IF_CHECK_FAILED);
+
+ if (check_device(target, CHECK_NO_MEDIA) == 0) {
+ int retry;
+ for (retry = 0; retry < 5; retry++) {
+ if (check_device(target, CHECK_DEVICE_NOT_READY) == 0)
+ break;
+ (void) sleep(3);
+ }
+ }
+
+ if (aflag) {
+ check_invalid_option(&specified_ops, "ahvSCOdpTV");
+ if (optind == argc) {
+ err_msg(gettext("No audio files specified.\n"));
+ exit(1);
+ }
+ write_audio(argv, optind, argc);
+ }
+ if (Mflag) {
+ check_invalid_option(&specified_ops, "MhvdV");
+ info();
+ }
+ if (iflag) {
+ check_invalid_option(&specified_ops, "ihvSCOdpV");
+ if (optind == (argc - 1)) {
+ image_file = argv[optind];
+ write_image();
+ }
+ if (optind == argc)
+ write_image();
+ err_msg(gettext("Command line parsing error.\n"));
+ err_msg(gettext("Only one image-file can be specified.\n"));
+ exit(1);
+ }
+ if (bflag) {
+ check_invalid_option(&specified_ops, "bhvdV");
+ if (optind != (argc - 1)) {
+ err_msg(gettext("Command line parsing error.\n"));
+ print_usage();
+ }
+ blanking_type = argv[argc - 1];
+ blank();
+ }
+ if (xflag) {
+ check_invalid_option(&specified_ops, "xhpvdsTV");
+ if (optind != (argc - 2)) {
+ err_msg(gettext("Command line parsing error.\n"));
+ print_usage();
+ }
+ extract_track_no = atoi(argv[argc - 2]);
+ extract_file = argv[argc - 1];
+ extract_audio();
+ }
+ if (cflag) {
+ check_invalid_option(&specified_ops, "chvSCdpmsV");
+ copy_cd();
+ }
+
+ /*
+ * Open a closed disk, we do this by erasing the track tail
+ * and then re-finalizing with an open leadout.
+ */
+ if (Lflag) {
+ check_invalid_option(&specified_ops, "LvdV");
+ (void) check_device(target, CHECK_NO_MEDIA |
+ CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED);
+
+ /* no need to erase blank media */
+ if (!check_device(target, CHECK_MEDIA_IS_NOT_BLANK))
+ exit(0);
+
+ blanking_type = "leadout";
+ blank();
+
+ write_init(TRACK_MODE_DATA);
+ (void) close_track(target->d_fd, 0, 1, 1);
+ (void) finalize(target);
+ (void) printf(gettext("done.\n"));
+ exit(0);
+ }
+
+}
diff --git a/usr/src/cmd/cdrw/main.h b/usr/src/cmd/cdrw/main.h
new file mode 100644
index 0000000000..d35eb40d24
--- /dev/null
+++ b/usr/src/cmd/cdrw/main.h
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MAIN_H
+#define _MAIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "device.h"
+
+extern int debug;
+
+extern int use_media_stated_capacity;
+extern int keep_disc_open;
+extern int requested_speed;
+extern int simulation;
+extern int verbose;
+extern char *image_file;
+extern char *blanking_type;
+extern int audio_type;
+extern cd_device *target; /* Default target device */
+extern int extract_track_no;
+extern char *extract_file;
+extern char *alt_tmp_dir;
+extern char *copy_src;
+extern int vol_running;
+extern int cflag, tflag;
+extern uid_t ruid, cur_uid;
+extern int device_type;
+extern int write_mode;
+
+#define TAO_MODE 0
+#define DAO_MODE 1 /* not implimented for CD yet only DVD */
+
+#define CD_RW 1 /* CD_RW/CD-R */
+#define DVD_MINUS 2 /* DVD-RW/DVD-R */
+
+/*
+ * DVD+RW is listed differently from DVD+R since DVD+RW requires
+ * that we format the media priot to writing, this cannot be
+ * done for DVD+R since it is write once media, we treat the
+ * media as pre-formatted.
+ */
+#define DVD_PLUS 3 /* DVD+R */
+#define DVD_PLUS_W 4 /* DVD+RW */
+
+#define ALL 0 /* erase the complete media, slow */
+#define FAST 1 /* only erases the leadin and TOC */
+#define SESSION 6 /* erases the last session */
+#define LEADOUT 5 /* erases the leadout of the media */
+#define CLEAR 1 /* same as fast, used for fixing media */
+
+int setup_target(int flag);
+
+void info(void);
+void list(void);
+void write_image(void);
+void blank(void);
+void write_audio(char **argv, int start_argc, int argc);
+void extract_audio(void);
+void copy_cd(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MAIN_H */
diff --git a/usr/src/cmd/cdrw/misc_scsi.c b/usr/src/cmd/cdrw/misc_scsi.c
new file mode 100644
index 0000000000..c6d7f43c1e
--- /dev/null
+++ b/usr/src/cmd/cdrw/misc_scsi.c
@@ -0,0 +1,839 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/dkio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/time.h>
+
+#include "mmc.h"
+#include "util.h"
+#include "misc_scsi.h"
+#include "transport.h"
+#include "main.h"
+#include "toshiba.h"
+#include "msgs.h"
+
+uint32_t
+read_scsi32(void *addr)
+{
+ uchar_t *ad = (uchar_t *)addr;
+ uint32_t ret;
+
+ ret = ((((uint32_t)ad[0]) << 24) | (((uint32_t)ad[1]) << 16) |
+ (((uint32_t)ad[2]) << 8) | ad[3]);
+ return (ret);
+}
+
+uint16_t
+read_scsi16(void *addr)
+{
+ uchar_t *ad = (uchar_t *)addr;
+ uint16_t ret;
+
+ ret = ((((uint16_t)ad[0]) << 8) | ad[1]);
+ return (ret);
+}
+
+void
+load_scsi32(void *addr, uint32_t v)
+{
+ uchar_t *ad = (uchar_t *)addr;
+
+ ad[0] = (uchar_t)(v >> 24);
+ ad[1] = (uchar_t)(v >> 16);
+ ad[2] = (uchar_t)(v >> 8);
+ ad[3] = (uchar_t)v;
+}
+
+void
+load_scsi16(void *addr, uint16_t v)
+{
+ uchar_t *ad = (uchar_t *)addr;
+ ad[0] = (uchar_t)(v >> 8);
+ ad[1] = (uchar_t)v;
+}
+/*
+ * will get the mode page only i.e. will strip off the header.
+ */
+int
+get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer)
+{
+ int ret;
+ uchar_t byte2, *buf;
+ uint_t header_len, page_len, copy_cnt;
+
+ byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
+ buf = (uchar_t *)my_zalloc(256);
+
+ /* Ask 254 bytes only to make our IDE driver happy */
+ ret = mode_sense(fd, byte2, 1, 254, buf);
+ if (ret == 0) {
+ free(buf);
+ return (0);
+ }
+
+ header_len = 8 + read_scsi16(&buf[6]);
+ page_len = buf[header_len + 1] + 2;
+
+ copy_cnt = (page_len > buf_len) ? buf_len : page_len;
+ (void) memcpy(buffer, &buf[header_len], copy_cnt);
+ free(buf);
+
+ return (1);
+}
+
+/*
+ * will take care of adding mode header and any extra bytes at the end.
+ */
+int
+set_mode_page(int fd, uchar_t *buffer)
+{
+ int ret;
+ uchar_t *buf;
+ uint_t total, p_len;
+
+ p_len = buffer[1] + 2;
+ total = p_len + 8;
+ buf = (uchar_t *)my_zalloc(total);
+
+ (void) memcpy(&buf[8], buffer, p_len);
+ if (debug) {
+ int i;
+
+ (void) printf("MODE: [");
+ for (i = 0; i < p_len; i++) {
+ (void) printf("0x%02x ", (uchar_t)buffer[i]);
+ }
+
+ (void) printf("]\n");
+ }
+ ret = mode_select(fd, total, buf);
+ free(buf);
+
+ return (ret);
+}
+
+/*
+ * Builds track information database for track trackno. If trackno is
+ * -1, builds the database for next blank track.
+ */
+int
+build_track_info(cd_device *dev, int trackno, struct track_info *t_info)
+{
+ uchar_t *ti;
+ uchar_t toc[20]; /* 2 entries + 4 byte header */
+ int ret;
+
+ (void) memset(t_info, 0, sizeof (*t_info));
+ /* 1st try READ TRACK INFORMATION */
+ ti = (uchar_t *)my_zalloc(TRACK_INFO_SIZE);
+ t_info->ti_track_no = trackno;
+
+ /* Gererate faked information for writing to DVD */
+ if (device_type != CD_RW) {
+ uint_t bsize;
+
+ t_info->ti_flags = 0x3000;
+ t_info->ti_track_no = 1;
+ t_info->ti_session_no = 1;
+ t_info->ti_track_mode = 0x4;
+ t_info->ti_data_mode = 1;
+ t_info->ti_start_address = 0;
+
+ /* only 1 track on DVD make it max size */
+ t_info->ti_track_size = read_format_capacity(target->d_fd,
+ &bsize);
+ if (t_info->ti_track_size < MAX_CD_BLKS) {
+ t_info->ti_track_size = MAX_DVD_BLKS;
+ }
+
+ t_info->ti_nwa = 0;
+ t_info->ti_lra = 0;
+ t_info->ti_packet_size = 0x10;
+ t_info->ti_free_blocks = 0;
+ }
+
+ if (read_track_info(dev->d_fd, trackno, ti)) {
+
+ if (debug)
+ (void) printf("using read_track_info for TOC \n");
+
+ t_info->ti_track_no = ti[2];
+ t_info->ti_session_no = ti[3];
+ t_info->ti_flags = (ti[6] >> 4) & 0xf;
+ t_info->ti_flags |= (uint32_t)(ti[5] & 0xf0);
+ t_info->ti_flags |= (uint32_t)(ti[7]) << 8;
+ t_info->ti_flags |= TI_SESSION_NO_VALID | TI_FREE_BLOCKS_VALID;
+ t_info->ti_track_mode = ti[5] & 0xf;
+ if ((ti[6] & 0xf) == 0xf)
+ t_info->ti_data_mode = 0xff;
+ else
+ t_info->ti_data_mode = ti[6] & 0xf;
+ t_info->ti_start_address = read_scsi32(&ti[8]);
+ t_info->ti_nwa = read_scsi32(&ti[12]);
+ t_info->ti_free_blocks = read_scsi32(&ti[16]);
+ t_info->ti_packet_size = read_scsi32(&ti[20]);
+ t_info->ti_track_size = read_scsi32(&ti[24]);
+ t_info->ti_lra = read_scsi32(&ti[28]);
+ free(ti);
+ return (1);
+ }
+ /* READ TRACK INFORMATION not supported, try other options */
+ free(ti);
+ /*
+ * We can get info for next blank track if READ TRACK INFO is not
+ * supported.
+ */
+ if (trackno == -1)
+ return (0);
+
+ if (debug)
+ (void) printf("using READ_TOC for TOC\n");
+
+ /* Try Read TOC */
+ if (!read_toc(dev->d_fd, 0, trackno, 20, toc)) {
+ return (0);
+ }
+ t_info->ti_start_address = read_scsi32(&toc[8]);
+ t_info->ti_track_mode = toc[5] & 0xf;
+ t_info->ti_track_size = read_scsi32(&toc[16]) - read_scsi32(&toc[8]);
+ t_info->ti_data_mode = get_data_mode(dev->d_fd, read_scsi32(&toc[8]));
+
+ /* Numbers for audio tracks are always in 2K chunks */
+ if ((dev->d_blksize == 512) && ((t_info->ti_track_mode & 4) == 0)) {
+ t_info->ti_start_address /= 4;
+ t_info->ti_track_size /= 4;
+ }
+
+ /* Now find out the session thing */
+ ret = read_toc(dev->d_fd, 1, trackno, 12, toc);
+
+ /*
+ * Make sure that the call succeeds and returns the requested
+ * TOC size correctly.
+ */
+
+ if ((ret == 0) || (toc[1] != 0x0a)) {
+
+ /* For ATAPI drives or old Toshiba drives */
+ ret = read_toc_as_per_8020(dev->d_fd, 1, trackno, 12, toc);
+ }
+ /* If this goes through well TOC length will always be 0x0a */
+ if (ret && (toc[1] == 0x0a)) {
+ if (trackno >= toc[6]) {
+ t_info->ti_session_no = toc[3];
+ t_info->ti_flags |= TI_SESSION_NO_VALID;
+ }
+ /*
+ * This might be the last track of this session. If so,
+ * exclude the leadout and next lead in.
+ */
+ if (trackno == (toc[6] - 1)) {
+ /*
+ * 1.5 Min leadout + 1 min. leadin + 2 sec. pre-gap.
+ * For 2nd+ leadout it will be 0.5 min. But currently
+ * there is no direct way. And it will not happen
+ * for any normal case.
+ *
+ * 75 frames/sec, 60 sec/min, so leadin gap is
+ * ((1.5 +1)*60 + 2)*75 = 11400 frames (blocks)
+ */
+ t_info->ti_track_size -= 11400;
+ }
+ }
+ return (1);
+}
+
+uchar_t
+get_data_mode(int fd, uint32_t lba)
+{
+ int ret;
+ uchar_t *buf;
+ uchar_t mode;
+
+ buf = (uchar_t *)my_zalloc(8);
+ ret = read_header(fd, lba, buf);
+ if (ret == 0)
+ mode = 0xff;
+ else
+ mode = buf[0];
+ free(buf);
+ return (mode);
+}
+
+/*
+ * Set page code 5 for TAO mode.
+ */
+int
+prepare_for_write(cd_device *dev, int track_mode, int test_write,
+ int keep_disc_open)
+{
+ uchar_t *buf;
+ int no_err;
+ int reset_device;
+
+ if ((write_mode == DAO_MODE) && keep_disc_open) {
+ (void) printf(gettext(
+ "Multi-session is not supported on DVD media\n"));
+ exit(1);
+ }
+
+ if ((write_mode == DAO_MODE) && debug) {
+ (void) printf("Preparing to write in DAO\n");
+ }
+
+ (void) start_stop(dev->d_fd, 1);
+ /* Some drives do not support this command but still do it */
+ (void) rezero_unit(dev->d_fd);
+
+ buf = (uchar_t *)my_zalloc(64);
+
+ no_err = get_mode_page(dev->d_fd, 5, 0, 64, buf);
+ if (no_err)
+ no_err = ((buf[1] + 2) > 64) ? 0 : 1;
+ /*
+ * If the device is already in simulation mode and again a
+ * simulation is requested, then set the device in non-simulation
+ * 1st and then take it to simulation mode. This will flush any
+ * previous fake state in the drive.
+ */
+ if (no_err && test_write && (buf[2] & 0x10)) {
+ reset_device = 1;
+ } else {
+ reset_device = 0;
+ }
+ if (no_err != 0) {
+ buf[0] &= 0x3f;
+
+ /* set TAO or DAO writing mode */
+ buf[2] = (write_mode == TAO_MODE)?1:2;
+
+ /* set simulation flag */
+ if (test_write && (!reset_device)) {
+ buf[2] |= 0x10;
+ } else {
+ buf[2] &= ~0x10;
+ }
+
+ /* Turn on HW buffer underrun protection (BUFE) */
+ if (!test_write) {
+ buf[2] |= 0x40;
+ }
+
+ /* set track mode type */
+ if (device_type == CD_RW) {
+ buf[3] = track_mode & 0x0f; /* ctrl nibble */
+ } else {
+ buf[3] = 5; /* always 5 for DVD */
+ }
+
+ if (keep_disc_open) {
+ buf[3] |= 0xc0; /* Allow more sessions */
+ }
+
+ /* Select track type (audio or data) */
+ if (track_mode == TRACK_MODE_DATA) {
+ buf[4] = 8; /* 2048 byte sector */
+ } else {
+ buf[4] = 0; /* 2352 byte sector */
+ }
+ buf[7] = buf[8] = 0;
+
+ /* Need to clear these fields for setting into DAO */
+ if (write_mode == DAO_MODE)
+ buf[5] = buf[15] = 0;
+
+ /* print out mode for detailed log */
+ if (debug && verbose) {
+ int i;
+
+ (void) printf("setting = [ ");
+ for (i = 0; i < 15; i++)
+ (void) printf("0x%x ", buf[i]);
+ (void) printf("]\n");
+ }
+
+ no_err = set_mode_page(dev->d_fd, buf);
+
+ if (no_err && reset_device) {
+ /* Turn the test write bit back on */
+ buf[2] |= 0x10;
+ no_err = set_mode_page(dev->d_fd, buf);
+ }
+
+ /*
+ * Since BUFE is the only optional flag we are
+ * setting we will try to turn it off if the command
+ * fails.
+ */
+ if (!no_err) {
+ /*
+ * Some old drives may not support HW
+ * buffer underrun protection, try again
+ * after turning it off.
+ */
+ if (debug)
+ (void) printf("Turning off BUFE\n");
+ buf[2] &= ~0x40;
+ no_err = set_mode_page(dev->d_fd, buf);
+ }
+ }
+
+ free(buf);
+ return (no_err);
+}
+
+/*
+ * Close session. This will write TOC.
+ */
+int
+finalize(cd_device *dev)
+{
+ uchar_t *di;
+ int count, ret, err;
+ int immediate;
+ int finalize_max;
+
+ /*
+ * For ATAPI devices we will use the immediate mode and will
+ * poll the command for completion so that this command may
+ * not hog the channel. But for SCSI, we will use the treditional
+ * way of issuing the command with a large enough timeout. This
+ * is done because immediate mode was designed for ATAPI and some
+ * SCSI RW drives might not be even tested with it.
+ */
+ if ((dev->d_inq[2] & 7) != 0) {
+ /* SCSI device */
+ immediate = 0;
+ } else {
+ /* non-SCSI (e.g ATAPI) device */
+ immediate = 1;
+ }
+
+ /* We need to close track before close session */
+ if (device_type == DVD_PLUS) {
+ if (!close_track(dev->d_fd, 0, 0, immediate))
+ return (0);
+ }
+
+ if (!close_track(dev->d_fd, 0, 1, immediate)) {
+ /*
+ * For DVD-RW close track is not well defined
+ * some drives dont like it, others want us
+ * to close track before closing the session.
+ * NOTE that for MMC specification it is not mandatory
+ * to support close track.
+ */
+ if (device_type == DVD_MINUS) {
+ if (!close_track(dev->d_fd, 1, 0, immediate)) {
+ return (0);
+ } else {
+ /* command is already done */
+ if (!immediate)
+ return (1);
+ }
+ } else {
+ return (0);
+ }
+ } else {
+ if (!immediate)
+ return (1);
+ }
+ if (immediate) {
+ (void) sleep(10);
+
+ di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
+ err = 0;
+
+ if (device_type == CD_RW) {
+ /* Finalization should not take more than 6 minutes */
+ finalize_max = FINALIZE_TIMEOUT;
+ } else {
+ /* some DVD-RW drives take longer than 6 minutes */
+ finalize_max = FINALIZE_TIMEOUT*2;
+ }
+
+ for (count = 0; count < finalize_max; count++) {
+ ret = read_disc_info(dev->d_fd, di);
+ if (ret != 0)
+ break;
+ if (uscsi_status != 2)
+ err = 1;
+ if (SENSE_KEY(rqbuf) == 2) {
+ /* not ready but not becoming ready */
+ if (ASC(rqbuf) != 4)
+ err = 1;
+ } else if (SENSE_KEY(rqbuf) == 5) {
+ /* illegal mode for this track */
+ if (ASC(rqbuf) != 0x64)
+ err = 1;
+ } else {
+ err = 1;
+ }
+ if (err == 1) {
+ if (debug) {
+ (void) printf("Finalization failed\n");
+ (void) printf("%x %x %x %x\n",
+ uscsi_status, SENSE_KEY(rqbuf),
+ ASC(rqbuf), ASCQ(rqbuf));
+ }
+ free(di);
+ return (0);
+ }
+ if (uscsi_status == 2) {
+ int i;
+ /* illegal field in command packet */
+ if (ASC(rqbuf) == 0x24) {
+ /* print it out! */
+ (void) printf("\n");
+ for (i = 0; i < 18; i++)
+ (void) printf("%x ",
+ (unsigned)(rqbuf[i]));
+ (void) printf("\n");
+ }
+ }
+ (void) sleep(5);
+ }
+ free(di);
+ }
+ return (ret);
+}
+
+/*
+ * Find out media capacity.
+ */
+int
+get_last_possible_lba(cd_device *dev)
+{
+ uchar_t *di;
+ int cap;
+
+ di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
+ if (!read_disc_info(dev->d_fd, di)) {
+ free(di);
+ return (0);
+ }
+ if ((di[21] != 0) && (di[21] != 0xff)) {
+ cap = ((di[21] * 60) + di[22]) * 75;
+ } else {
+ cap = 0;
+ }
+
+ free(di);
+ return (cap);
+}
+
+int
+read_audio_through_read_cd(cd_device *dev, uint_t start_lba, uint_t nblks,
+ uchar_t *buf)
+{
+ int retry;
+ int ret;
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = read_cd(dev->d_fd, (uint32_t)start_lba, (uint16_t)nblks,
+ 1, buf, (uint32_t)(nblks * 2352));
+ if (ret)
+ break;
+ }
+ return (ret);
+}
+
+int
+eject_media(cd_device *dev)
+{
+ if (vol_running) {
+ /* If there is a media, try using DKIOCEJECT 1st */
+ if (check_device(dev, CHECK_NO_MEDIA) == 0) {
+ if (ioctl(dev->d_fd, DKIOCEJECT, 0) == 0) {
+ return (1);
+ }
+ }
+ }
+ if (load_unload(dev->d_fd, 0) == 0) {
+ /* if eject fails */
+ if ((uscsi_status == 2) && (ASC(rqbuf) == 0x53)) {
+ /*
+ * check that eject is not blocked on the device
+ */
+ if (!prevent_allow_mr(dev->d_fd, 1))
+ return (0);
+ return (load_unload(dev->d_fd, 0));
+ }
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Get CD speed from Page code 2A. since GET PERFORMANCE is not supported
+ * (which is already checked before) this mode page *will* have the speed.
+ */
+static uint16_t
+i_cd_speed_read(cd_device *dev, int cmd)
+{
+ uchar_t *mp2a;
+ uint16_t rate;
+
+ mp2a = (uchar_t *)my_zalloc(PAGE_CODE_2A_SIZE);
+ if (get_mode_page(dev->d_fd, 0x2A, 0, PAGE_CODE_2A_SIZE,
+ mp2a) == 0) {
+ rate = 0;
+ } else {
+ if (cmd == GET_READ_SPEED) {
+ rate = ((uint16_t)mp2a[14] << 8) | mp2a[15];
+ } else {
+ rate = ((uint16_t)mp2a[20] << 8) | mp2a[21];
+ }
+ }
+ free(mp2a);
+ return (rate);
+}
+
+/*
+ * CD speed related functions (ioctl style) for drives which do not support
+ * real time streaming.
+ */
+int
+cd_speed_ctrl(cd_device *dev, int cmd, int speed)
+{
+ uint16_t rate;
+
+ if ((cmd == GET_READ_SPEED) || (cmd == GET_WRITE_SPEED))
+ return (XFER_RATE_TO_SPEED(i_cd_speed_read(dev, cmd)));
+ if (cmd == SET_READ_SPEED) {
+ rate = i_cd_speed_read(dev, GET_WRITE_SPEED);
+ return (set_cd_speed(dev->d_fd, SPEED_TO_XFER_RATE(speed),
+ rate));
+ }
+ if (cmd == SET_WRITE_SPEED) {
+ rate = i_cd_speed_read(dev, GET_READ_SPEED);
+ return (set_cd_speed(dev->d_fd, rate,
+ SPEED_TO_XFER_RATE(speed)));
+ }
+ return (0);
+}
+
+/*
+ * cd speed related functions for drives which support RT-streaming
+ */
+int
+rt_streaming_ctrl(cd_device *dev, int cmd, int speed)
+{
+ uchar_t *perf, *str;
+ int write_perf;
+ int ret;
+ uint16_t perf_got;
+
+ write_perf = 0;
+ if ((cmd == GET_WRITE_SPEED) || (cmd == SET_READ_SPEED))
+ write_perf = 1;
+ perf = (uchar_t *)my_zalloc(GET_PERF_DATA_LEN);
+ if (!get_performance(dev->d_fd, write_perf, perf)) {
+ ret = 0;
+ goto end_rsc;
+ }
+ perf_got = (uint16_t)read_scsi32(&perf[20]);
+ if ((cmd == GET_READ_SPEED) || (cmd == GET_WRITE_SPEED)) {
+ ret = XFER_RATE_TO_SPEED(perf_got);
+ goto end_rsc;
+ }
+ str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN);
+ (void) memcpy(&str[8], &perf[16], 4);
+ load_scsi32(&str[16], 1000);
+ load_scsi32(&str[24], 1000);
+ if (cmd == SET_WRITE_SPEED) {
+ load_scsi32(&str[12], (uint32_t)perf_got);
+ load_scsi32(&str[20], (uint32_t)SPEED_TO_XFER_RATE(speed));
+ } else {
+ load_scsi32(&str[20], (uint32_t)perf_got);
+ load_scsi32(&str[12], (uint32_t)SPEED_TO_XFER_RATE(speed));
+ }
+ ret = set_streaming(dev->d_fd, str);
+ free(str);
+
+ /* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
+ if (ret == 0) {
+ if (debug)
+ (void) printf(" real time speed control"
+ " failed, using CD speed control\n");
+
+ dev->d_speed_ctrl = cd_speed_ctrl;
+ ret = dev->d_speed_ctrl(dev, cmd, speed);
+ }
+
+end_rsc:
+ free(perf);
+ return (ret);
+}
+
+/*
+ * Initialize device for track-at-once mode of writing. All of the data will
+ * need to be written to the track without interruption.
+ * This initialized TAO by setting page code 5 and speed.
+ */
+void
+write_init(int mode)
+{
+ (void) printf(gettext("Initializing device"));
+ if (simulation)
+ (void) printf(gettext("(Simulation mode)"));
+ print_n_flush("...");
+
+ get_media_type(target->d_fd);
+
+ /* DVD- requires DAO mode */
+ if (device_type == DVD_MINUS) {
+ write_mode = DAO_MODE;
+ }
+
+ /* For debug, print out device config information */
+ if (debug) {
+ int i;
+ uchar_t cap[80];
+
+ if (get_configuration(target->d_fd, 0, 80, cap))
+ (void) printf("Drive profile = ");
+ for (i = 10; i < 70; i += 8)
+ (void) printf(" 0x%x", cap[i]);
+ (void) printf("\n");
+ }
+
+ /* DVD+ and DVD- have no support for AUDIO, bail out */
+ if ((mode == TRACK_MODE_AUDIO) && (device_type != CD_RW)) {
+ err_msg(gettext("Audio mode is only supported for CD media\n"));
+ exit(1);
+ }
+
+ if (!prepare_for_write(target, mode, simulation, keep_disc_open)) {
+ /* l10n_NOTE : 'failed' as in Initializing device...failed */
+ (void) printf(gettext("failed.\n"));
+ err_msg(gettext("Cannot initialize device for write\n"));
+ exit(1);
+ }
+ /* l10n_NOTE : 'done' as in "Initializing device...done" */
+ (void) printf(gettext("done.\n"));
+
+ /* if speed change option was used (-p) then try to set the speed */
+ if (requested_speed != 0) {
+ if (verbose)
+ (void) printf(gettext("Trying to set speed to %dX.\n"),
+ requested_speed);
+ if (target->d_speed_ctrl(target, SET_WRITE_SPEED,
+ requested_speed) == 0) {
+ err_msg(gettext("Unable to set speed.\n"));
+ exit(1);
+ }
+ if (verbose) {
+ int speed;
+ speed = target->d_speed_ctrl(target,
+ GET_WRITE_SPEED, 0);
+ if (speed == requested_speed) {
+ (void) printf(gettext("Speed set to %dX.\n"),
+ speed);
+ } else {
+ (void) printf(
+ gettext("Speed set to closest approximation "
+ "of %dX allowed by device (%dX).\n"),
+ requested_speed, speed);
+ }
+ }
+ }
+}
+
+void
+write_fini(void)
+{
+ print_n_flush(gettext("Finalizing (Can take several minutes)..."));
+ /* Some drives don't like this while in test write mode */
+ if (!simulation) {
+ if (!finalize(target)) {
+ /*
+ * It is possible that the drive is busy writing the
+ * buffered portion. So do not get upset yet.
+ */
+ (void) sleep(10);
+ if (!finalize(target)) {
+ if (debug) {
+ (void) printf("status %x, %x/%x/%x\n",
+ uscsi_status, SENSE_KEY(rqbuf),
+ ASC(rqbuf), ASCQ(rqbuf));
+ }
+
+ if ((device_type == DVD_MINUS) &&
+ (SENSE_KEY(rqbuf) == 5)) {
+
+ if (verbose) {
+ (void) printf(
+ "skipping finalizing\n");
+ }
+ } else {
+
+ /* l10n_NOTE : 'failed' as in finishing up...failed */
+ (void) printf(gettext("failed.\n"));
+
+ err_msg(gettext(
+ "Could not finalize the disc.\n"));
+ exit(1);
+ }
+
+
+ }
+ }
+ if (vol_running) {
+ (void) eject_media(target);
+ }
+ } else if (check_device(target, CHECK_MEDIA_IS_NOT_BLANK)) {
+ /*
+ * Some drives such as the pioneer A04 will retain a
+ * ghost TOC after a simulation write is done. The
+ * media will actually be blank, but the drive will
+ * report a TOC. There is currently no other way to
+ * re-initialize the media other than ejecting or
+ * to ask the drive to clear the leadout. The laser
+ * is currently off so nothing is written to the
+ * media (on a good behaving drive).
+ * NOTE that a device reset does not work to make
+ * the drive re-initialize the media.
+ */
+
+ if (!vol_running) {
+ blanking_type = "clear";
+ blank();
+ }
+
+ }
+ /* l10n_NOTE : 'done' as in "Finishing up...done" */
+ (void) printf(gettext("done.\n"));
+}
diff --git a/usr/src/cmd/cdrw/misc_scsi.h b/usr/src/cmd/cdrw/misc_scsi.h
new file mode 100644
index 0000000000..b59efa3a28
--- /dev/null
+++ b/usr/src/cmd/cdrw/misc_scsi.h
@@ -0,0 +1,113 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MISC_SCSI_H
+#define _MISC_SCSI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "device.h"
+
+struct track_info {
+ uint32_t ti_flags; /* flags, see below */
+ int ti_track_no; /* Track number */
+ int ti_session_no; /* session no. 0 if cannot find that */
+ uchar_t ti_track_mode; /* track ctrl nibble, see READ TOC */
+ uchar_t ti_data_mode; /* Mode 0,1,2 or FF */
+ uint32_t ti_start_address; /* Start LBA */
+ uint32_t ti_track_size; /* Size in blocks */
+ uint32_t ti_packet_size; /* If a packet written track */
+ uint32_t ti_free_blocks; /* For an incomplete track */
+ uint32_t ti_lra; /* LBA of Last written user datablock */
+ uint32_t ti_nwa; /* Next writable address */
+};
+
+/*
+ * track_info_flags
+ */
+#define TI_FIXED_PACKET 1
+#define TI_PACKET_MODE 2
+#define TI_BLANK_TRACK 4
+#define TI_RESERVED_TRACK 8
+#define TI_COPY 0x10
+#define TI_DAMAGED_TRACK 0x20
+#define TI_NWA_VALID 0x100
+#define TI_LRA_VALID 0x200
+#define TI_SESSION_NO_VALID 0x1000
+#define TI_FREE_BLOCKS_VALID 0x2000
+
+/*
+ * Track mode nibble
+ */
+#define TRACK_MODE_DATA 0x06
+#define TRACK_MODE_AUDIO 0x02
+
+/* 74 minutes, each second is 75 blocks */
+#define MAX_CD_BLKS (74*60*75)
+#define MAX_DVD_BLKS 2295100
+
+/*
+ * Some devices just multiply speed by 176. But more accurate ones
+ * multiply speed by 176.4.
+ */
+#define XFER_RATE_TO_SPEED(r) ((r) % 176 ? ((((r)*10)+5)/1764) : (r) / 176)
+#define SPEED_TO_XFER_RATE(s) ((((s)*1764)+5)/10)
+
+#define FINALIZE_TIMEOUT (6 * 12) /* Six minutes */
+
+uint32_t read_scsi32(void *addr);
+uint16_t read_scsi16(void *addr);
+void load_scsi32(void *addr, uint32_t val);
+void load_scsi16(void *addr, uint16_t val);
+
+int get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer);
+int set_mode_page(int fd, uchar_t *buffer);
+int build_track_info(cd_device *dev, int trackno, struct track_info *t_info);
+uchar_t get_data_mode(int fd, uint32_t lba);
+int prepare_for_write(cd_device *dev, int track_mode, int test_write,
+ int keep_disc_open);
+int finalize(cd_device *dev);
+int get_last_possible_lba(cd_device *dev);
+int read_audio_through_read_cd(cd_device *dev, uint_t start_lba, uint_t nblks,
+ uchar_t *buf);
+int eject_media(cd_device *dev);
+int cd_speed_ctrl(cd_device *dev, int cmd, int speed);
+int rt_streaming_ctrl(cd_device *dev, int cmd, int speed);
+
+void tao_init(int mode);
+void tao_fini(void);
+void write_init(int mode);
+void write_fini(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MISC_SCSI_H */
diff --git a/usr/src/cmd/cdrw/mmc.c b/usr/src/cmd/cdrw/mmc.c
new file mode 100644
index 0000000000..6a342f50d7
--- /dev/null
+++ b/usr/src/cmd/cdrw/mmc.c
@@ -0,0 +1,664 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "transport.h"
+#include "mmc.h"
+#include "util.h"
+#include "main.h"
+
+int
+test_unit_ready(int fd)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ /* give length of cdb structure */
+ scmd->uscsi_cdblen = 6;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+inquiry(int fd, uchar_t *inq)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = INQUIRY_CMD;
+ scmd->uscsi_cdb[4] = INQUIRY_DATA_LENGTH;
+ scmd->uscsi_cdblen = 6;
+ scmd->uscsi_bufaddr = (char *)inq;
+ scmd->uscsi_buflen = INQUIRY_DATA_LENGTH;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read_capacity(int fd, uchar_t *capbuf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_CAP_CMD;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)capbuf;
+ scmd->uscsi_buflen = 8;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_buflen = page_len;
+ scmd->uscsi_bufaddr = (char *)buffer;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0xa;
+ scmd->uscsi_cdb[0] = MODE_SENSE_10_CMD;
+ if (dbd) {
+ /* don't return any block descriptors */
+ scmd->uscsi_cdb[1] = 0x8;
+ }
+ /* the page code we want */
+ scmd->uscsi_cdb[2] = pc;
+ /* allocation length */
+ scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff;
+ scmd->uscsi_cdb[8] = page_len & 0xff;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+mode_select(int fd, int page_len, uchar_t *buffer)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
+ scmd->uscsi_buflen = page_len;
+ scmd->uscsi_bufaddr = (char *)buffer;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0xa;
+
+ /* mode select (10) command */
+ scmd->uscsi_cdb[0] = MODE_SELECT_10_CMD;
+ scmd->uscsi_cdb[1] = 0x10;
+
+ /* parameter list length */
+ scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff;
+ scmd->uscsi_cdb[8] = page_len & 0xff;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read_track_info(int fd, int trackno, uchar_t *ti)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_TRACK_CMD;
+
+ /* tell device we are giving it a track number */
+ scmd->uscsi_cdb[1] = 1;
+
+ /* track number to read */
+ if (trackno == -1)
+ if (device_type == CD_RW) {
+ ((uchar_t *)scmd->uscsi_cdb)[5] = 0xff;
+ } else {
+ /* only 1 track is allowed on DVD media */
+ scmd->uscsi_cdb[1] = 0;
+ ((uchar_t *)scmd->uscsi_cdb)[5] = 0;
+ }
+ else
+ scmd->uscsi_cdb[5] = (uchar_t)trackno;
+
+ scmd->uscsi_cdb[8] = TRACK_INFO_SIZE;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)ti;
+ scmd->uscsi_buflen = TRACK_INFO_SIZE;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_TOC_CMD;
+ scmd->uscsi_cdb[2] = format & 0xf;
+ scmd->uscsi_cdb[6] = trackno;
+ scmd->uscsi_cdb[8] = buflen & 0xff;
+ scmd->uscsi_cdb[7] = (buflen >> 8) & 0xff;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = buflen;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+
+ /* Fix for old SONY drives */
+ if ((format == 0) && (buflen == 4) && (buf[0] == 0) && (buf[1] == 2)) {
+ uint16_t toc_size;
+
+ toc_size = (((uint16_t)(buf[3] + 1)) * 8) + 2;
+ load_scsi16(buf, toc_size);
+ }
+ return (1);
+}
+
+int
+read_header(int fd, uint32_t lba, uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_HDR_CMD;
+
+ /* Logical block address */
+ load_scsi32(&scmd->uscsi_cdb[2], lba);
+
+ /* allocation length */
+ scmd->uscsi_cdb[8] = 8;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = 8;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read_disc_info(int fd, uchar_t *di)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_INFO_CMD;
+ scmd->uscsi_cdb[8] = DISC_INFO_BLOCK_SIZE;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)di;
+ scmd->uscsi_buflen = DISC_INFO_BLOCK_SIZE;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = GET_CONFIG_CMD;
+ if (feature == 0)
+ scmd->uscsi_cdb[1] = 0x2;
+ else
+ scmd->uscsi_cdb[1] = 0x1;
+ scmd->uscsi_cdb[1] = 0x2;
+ scmd->uscsi_cdb[2] = (feature >> 8) & 0xff;
+ scmd->uscsi_cdb[3] = feature & 0xff;
+ scmd->uscsi_cdb[7] = (bufsize >> 8) & 0xff;
+ scmd->uscsi_cdb[8] = bufsize & 0xff;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = bufsize;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
+ uint32_t bufsize)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = READ_10_CMD;
+ load_scsi32(&scmd->uscsi_cdb[2], start_blk);
+ scmd->uscsi_cdb[8] = nblk & 0xff;
+ scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = bufsize;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+write10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
+ uint32_t bufsize)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
+ /*
+ * Some DVD drives take longer to write than
+ * the standard time, since they tend to generate
+ * the media TOC on the fly when the cache is full
+ */
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT * 3;
+ scmd->uscsi_cdb[0] = WRITE_10_CMD;
+ load_scsi32(&scmd->uscsi_cdb[2], start_blk);
+ scmd->uscsi_cdb[8] = nblk & 0xff;
+ scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = bufsize;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+close_track(int fd, int trackno, int close_session, int immediate)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_cdb[0] = CLOSE_TRACK_CMD;
+ if (immediate) {
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[1] = 1;
+ } else {
+ scmd->uscsi_timeout = 240;
+ }
+ if ((close_session) || (device_type == DVD_PLUS) ||
+ (device_type == DVD_PLUS_W)) {
+ /* close the session */
+ scmd->uscsi_cdb[2] = 2;
+
+ } else {
+ /* Close the track but leave session open */
+ scmd->uscsi_cdb[2] = 1;
+ scmd->uscsi_cdb[5] = trackno & 0xff;
+ }
+
+ /*
+ * DVD+R media are already formatted, we are using
+ * a special case to notify that drive to close
+ * track/session and null-fill the remaining space.
+ */
+ if (device_type == DVD_PLUS) {
+ scmd->uscsi_cdb[5] = 1; /* only 1 track */
+
+ if (close_session) {
+ scmd->uscsi_cdb[2] = 6; /* session */
+ } else {
+ scmd->uscsi_cdb[2] = 1; /* track */
+ }
+ }
+
+ scmd->uscsi_cdblen = 10;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+blank_disc(int fd, int type, int immediate)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+
+ if (immediate) {
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[1] = 0x10;
+ } else {
+ scmd->uscsi_timeout = 0x12c0;
+ }
+ ((uchar_t *)scmd->uscsi_cdb)[0] = BLANK_CMD;
+
+ /* tell it to blank the last session or all of the disk */
+ scmd->uscsi_cdb[1] |= type & 0x07;
+ scmd->uscsi_cdblen = 12;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+read_cd(int fd, uint32_t start_blk, uint16_t nblk, uchar_t sector_type,
+ uchar_t *buf, uint32_t bufsize)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ ((uchar_t *)scmd->uscsi_cdb)[0] = READ_CD_CMD;
+ scmd->uscsi_cdb[1] = (sector_type & 0x7) << 2;
+ scmd->uscsi_cdb[5] = start_blk & 0xff;
+ scmd->uscsi_cdb[4] = (start_blk >> 8) & 0xff;
+ scmd->uscsi_cdb[3] = (start_blk >> 16) & 0xff;
+ scmd->uscsi_cdb[2] = (start_blk >> 24) & 0xff;
+ scmd->uscsi_cdb[8] = nblk & 0xff;
+ scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
+ scmd->uscsi_cdb[9] = 0x10;
+ scmd->uscsi_cdblen = 12;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = bufsize;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+load_unload(int fd, int load)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = START_STOP_CMD;
+ if (load == 0) {
+ /* unload medium */
+ scmd->uscsi_cdb[4] = 2;
+ } else {
+ /* load medium */
+ scmd->uscsi_cdb[4] = 3;
+ }
+ scmd->uscsi_cdblen = 6;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+prevent_allow_mr(int fd, int op)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = PREVENT_ALLOW_CMD;
+ if (!op) { /* prevent */
+ scmd->uscsi_cdb[4] = 1;
+ }
+ scmd->uscsi_cdblen = 6;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+set_cd_speed(int fd, uint16_t read_speed, uint16_t write_speed)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0xc;
+ ((uchar_t *)scmd->uscsi_cdb)[0] = SET_CD_SPEED;
+ scmd->uscsi_cdb[2] = (read_speed >> 8) & 0xff;
+ scmd->uscsi_cdb[3] = read_speed & 0xff;
+ scmd->uscsi_cdb[4] = (write_speed >> 8) & 0xff;
+ scmd->uscsi_cdb[5] = write_speed & 0xff;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+get_performance(int fd, int get_write_performance, uchar_t *perf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_buflen = GET_PERF_DATA_LEN;
+ scmd->uscsi_bufaddr = (char *)perf;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0xc;
+ ((uchar_t *)scmd->uscsi_cdb)[0] = GET_PERFORMANCE_CMD;
+ scmd->uscsi_cdb[1] = 0x10;
+ if (get_write_performance)
+ scmd->uscsi_cdb[1] |= 4;
+ scmd->uscsi_cdb[9] = 2;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+set_streaming(int fd, uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
+ scmd->uscsi_buflen = SET_STREAM_DATA_LEN;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0xc;
+ ((uchar_t *)scmd->uscsi_cdb)[0] = STREAM_CMD;
+ scmd->uscsi_cdb[10] = SET_STREAM_DATA_LEN;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+rezero_unit(int fd)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0x6;
+ scmd->uscsi_cdb[0] = REZERO_UNIT_CMD;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+start_stop(int fd, int start)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 0x6;
+ scmd->uscsi_cdb[0] = START_STOP_CMD;
+ if (start) {
+ scmd->uscsi_cdb[4] = 1;
+ }
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+int
+flush_cache(int fd)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_cdb[0] = SYNC_CACHE_CMD;
+ if (device_type != CD_RW) {
+ scmd->uscsi_cdb[1] = 0x2; /* Immediate */
+ }
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * used for DVD- to reserve the size we want to write.
+ * This is used by the drive to generate a TOC.
+ */
+int
+set_reservation(int fd, ulong_t size)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdb[0] = SET_RESERVATION_CMD;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_cdb[5] = (uchar_t)(size >> 24);
+ scmd->uscsi_cdb[6] = (uchar_t)(size >> 16);
+ scmd->uscsi_cdb[7] = (uchar_t)(size >> 8);
+ scmd->uscsi_cdb[8] = (uchar_t)size;
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Used for DVD+RW media to prepare the disk to write.
+ * It will also be used for packet mode writing when
+ * it becomes supported.
+ */
+int
+format_media(int fd)
+{
+ struct uscsi_cmd *scmd;
+ uchar_t buf[20];
+
+ (void) memset(buf, 0, 20);
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+
+ scmd->uscsi_cdblen = 12;
+ scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD;
+ scmd->uscsi_cdb[8] = 0x14; /* buffer length */
+ scmd->uscsi_buflen = 20;
+ scmd->uscsi_bufaddr = (char *)buf;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+
+ /* RE-use cap structure */
+
+ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 6;
+ scmd->uscsi_cdb[0] = FORMAT_UNIT_CMD;
+ /* full format */
+ scmd->uscsi_cdb[1] = 0x11;
+ scmd->uscsi_buflen = 12;
+ buf[1] = 0x82; /* immediate and FOV */
+ buf[3] = 8; /* descriptor length */
+ buf[8] = 0x98; /* type = 26 DVD+RW format */
+ buf[10] = 0;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+ return (1);
+}
+
+
+/*
+ * Prefered method of reading the media size. This is
+ * the only supported method on several newer drives.
+ */
+uint32_t
+read_format_capacity(int fd, uint_t *bsize)
+{
+ struct uscsi_cmd *scmd;
+ uint32_t filesize;
+ char buf[20];
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
+ scmd->uscsi_cdblen = 12;
+ scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD;
+ scmd->uscsi_cdb[8] = 0x14;
+ scmd->uscsi_buflen = 20;
+ scmd->uscsi_bufaddr = buf;
+
+ if ((uscsi_error = uscsi(fd, scmd)) < 0)
+ return (0);
+
+ filesize = (uint32_t)(((uchar_t)buf[4] << 24) +
+ ((uchar_t)buf[5] << 16) + ((uchar_t)buf[6] << 8) + (uchar_t)buf[7]);
+
+ *bsize = (uint16_t)(((uchar_t)buf[10] << 8) + (uchar_t)buf[11]);
+
+ return (filesize);
+}
diff --git a/usr/src/cmd/cdrw/mmc.h b/usr/src/cmd/cdrw/mmc.h
new file mode 100644
index 0000000000..8215f3759c
--- /dev/null
+++ b/usr/src/cmd/cdrw/mmc.h
@@ -0,0 +1,114 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MMC_H
+#define _MMC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SENSE_DATA_SIZE 16
+#define PAGE_CODE_2A_SIZE 26
+#define TRACK_INFO_SIZE 36
+#define DISC_INFO_BLOCK_SIZE 32
+#define INQUIRY_DATA_LENGTH 96
+#define GET_PERF_DATA_LEN 24
+#define SET_STREAM_DATA_LEN 28
+
+#define DEFAULT_SCSI_TIMEOUT 60
+
+int test_unit_ready(int fd);
+int inquiry(int fd, uchar_t *inq);
+int read_capacity(int fd, uchar_t *capbuf);
+int read_track_info(int fd, int trackno, uchar_t *ti);
+int mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer);
+int mode_select(int fd, int page_len, uchar_t *buffer);
+int read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf);
+int read_disc_info(int fd, uchar_t *di);
+int get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf);
+int read10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
+ uint32_t bufsize);
+int write10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
+ uint32_t bufsize);
+int close_track(int fd, int trackno, int close_session, int immediate);
+int blank_disc(int fd, int type, int immediate);
+int read_cd(int fd, uint32_t start_blk, uint16_t nblk, uchar_t sector_type,
+ uchar_t *buf, uint32_t bufsize);
+int load_unload(int fd, int load);
+int prevent_allow_mr(int fd, int op);
+int read_header(int fd, uint32_t lba, uchar_t *buf);
+int set_cd_speed(int fd, uint16_t read_speed, uint16_t write_speed);
+int get_performance(int fd, int get_write_performance, uchar_t *perf);
+int set_streaming(int fd, uchar_t *buf);
+int rezero_unit(int fd);
+int start_stop(int fd, int start);
+int flush_cache(int fd);
+int set_reservation(int fd, ulong_t size);
+int format_media(int fd);
+uint32_t read_format_capacity(int fd, uint_t *bsize);
+
+int uscsi_error; /* used for debugging failed uscsi */
+
+#define REZERO_UNIT_CMD 0x01
+#define FORMAT_UNIT_CMD 0x04
+#define INQUIRY_CMD 0x12
+#define MODE_SELECT_6_CMD 0x15
+#define MODE_SENSE_6_CMD 0x1A
+#define START_STOP_CMD 0x1B
+#define PREVENT_ALLOW_CMD 0x1E
+#define READ_FORMAT_CAP_CMD 0x23
+#define READ_CAP_CMD 0x25
+#define READ_10_CMD 0x28
+#define WRITE_10_CMD 0x2A
+#define SYNC_CACHE_CMD 0x35
+#define READ_TOC_CMD 0x43
+#define MODE_SELECT_10_CMD 0x55
+#define MODE_SENSE_10_CMD 0x5A
+#define READ_HDR_CMD 0x44
+#define GET_CONFIG_CMD 0x46
+
+#define READ_INFO_CMD 0x51
+#define READ_TRACK_CMD 0x52
+#define SET_RESERVATION_CMD 0x53
+#define CLOSE_TRACK_CMD 0x5B
+
+#define BLANK_CMD 0xA1
+#define GET_PERFORMANCE_CMD 0xAC
+#define READ_DVD_STRUCTURE 0xAD
+#define READ_CD_CMD 0xBE
+#define SET_CD_SPEED 0xBB
+
+#define STREAM_CMD 0xB6
+#define READ_AUDIO_CMD 0xD8
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MMC_H */
diff --git a/usr/src/cmd/cdrw/msgs.c b/usr/src/cmd/cdrw/msgs.c
new file mode 100644
index 0000000000..11a5679f14
--- /dev/null
+++ b/usr/src/cmd/cdrw/msgs.c
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "msgs.h"
+
+#ifdef APPTRACE
+FILE *tracestream = stderr;
+#endif
+
+/*PRINTFLIKE1*/
+void
+err_msg(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#ifdef APPTRACE
+/*PRINTFLIKE1*/
+void
+traceall_msg(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(tracestream, fmt, ap);
+ va_end(ap);
+}
+#endif
+
+/*PRINTFLIKE1*/
+void
+print_n_flush(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ (void) vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ (void) fflush(stdout);
+}
diff --git a/usr/src/cmd/cdrw/msgs.h b/usr/src/cmd/cdrw/msgs.h
new file mode 100644
index 0000000000..d038bf186c
--- /dev/null
+++ b/usr/src/cmd/cdrw/msgs.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MSGS_H
+#define _MSGS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+#ifdef APPTRACE
+#define TRACE(func) func
+extern FILE *tracestream;
+void traceall_msg(char *fmt, ...);
+#else
+#define TRACE(func)
+#endif
+
+void err_msg(char *fmt, ...);
+void print_n_flush(char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGS_H */
diff --git a/usr/src/cmd/cdrw/options.c b/usr/src/cmd/cdrw/options.c
new file mode 100644
index 0000000000..e72b68c327
--- /dev/null
+++ b/usr/src/cmd/cdrw/options.c
@@ -0,0 +1,87 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+
+#include "options.h"
+
+static uchar_t bitlocation[8] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80};
+
+/*
+ * Create a bytemask to store the command line options.
+ */
+void
+set_options_mask(options *msk, char *str)
+{
+ int i;
+ (void) memset(msk, 0, sizeof (*msk));
+ for (i = 0; str[i] != 0; i++) {
+ add_option(msk, str[i]);
+ }
+}
+
+void
+add_option(options *msk, char option)
+{
+ uint_t loc;
+ loc = (uint_t)option;
+ loc &= 0x7f;
+ /* put option into the correct bucket */
+ msk->bitmap[loc >> 3] |= bitlocation[loc & 7];
+}
+
+/*
+ * Compare the bytemask of the command line options used with
+ * acceptable options. If an invalid option is found use it as
+ * the return value.
+ */
+int
+compare_options_mask(options *msk, options *specified)
+{
+ int i, j;
+ uchar_t bmap = 0;
+
+ for (i = 0; i < 16; i++) {
+ if (msk->bitmap[i] == specified->bitmap[i])
+ continue;
+ bmap = msk->bitmap[i] | specified->bitmap[i];
+ bmap ^= msk->bitmap[i];
+ if (bmap)
+ break;
+ }
+ if (i == 16) {
+ /* no invalid options found */
+ return (0);
+ }
+
+ for (j = 0; j < 8; j++) {
+ if (bmap & bitlocation[j])
+ break;
+ }
+ return ((i*8) + j);
+}
diff --git a/usr/src/cmd/cdrw/options.h b/usr/src/cmd/cdrw/options.h
new file mode 100644
index 0000000000..7fd1633189
--- /dev/null
+++ b/usr/src/cmd/cdrw/options.h
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+typedef struct option_mask {
+ uchar_t bitmap[16];
+} options;
+
+void set_options_mask(options *msk, char *str);
+void add_option(options *msk, char option);
+int compare_options_mask(options *msk, options *specified);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OPTIONS_H */
diff --git a/usr/src/cmd/cdrw/toshiba.c b/usr/src/cmd/cdrw/toshiba.c
new file mode 100644
index 0000000000..a620b722f3
--- /dev/null
+++ b/usr/src/cmd/cdrw/toshiba.c
@@ -0,0 +1,186 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <libintl.h>
+
+#include "transport.h"
+#include "toshiba.h"
+#include "device.h"
+#include "misc_scsi.h"
+#include "util.h"
+#include "main.h"
+#include "msgs.h"
+#include "mmc.h"
+
+static int speed_tbl[4] = { 1, 2, 12, 4 };
+static uchar_t rev_speed_tbl[16] = { 0, 0, 1, 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0 };
+
+/*
+ * These are commands for using older Sun Toshiba drives. These
+ * commands are needed for reading CD TOC and audio extraction
+ * and changing speeds (used for audio extraction).
+ */
+
+int
+read_toc_as_per_8020(int fd, int format, int trackno, int buflen, uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = 60;
+ scmd->uscsi_cdb[0] = READ_TOC_CMD;
+ scmd->uscsi_cdb[6] = trackno;
+ scmd->uscsi_cdb[8] = buflen & 0xff;
+ scmd->uscsi_cdb[7] = (buflen >> 8) & 0xff;
+ scmd->uscsi_cdb[9] = (format << 6) & 0xc0;
+ scmd->uscsi_cdblen = 10;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = buflen;
+ if (uscsi(fd, scmd) < 0)
+ return (0);
+ return (1);
+}
+
+int
+toshiba_read_audio(cd_device *target, uint_t start_blk, uint_t nblk,
+ uchar_t *buf)
+{
+ struct uscsi_cmd *scmd;
+ int ret, retry;
+
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = 60;
+ ((uchar_t *)scmd->uscsi_cdb)[0] = READ_AUDIO_CMD;
+ scmd->uscsi_cdb[5] = start_blk & 0xff;
+ scmd->uscsi_cdb[4] = (start_blk >> 8) & 0xff;
+ scmd->uscsi_cdb[3] = (start_blk >> 16) & 0xff;
+ scmd->uscsi_cdb[2] = (start_blk >> 24) & 0xff;
+ scmd->uscsi_cdb[9] = nblk & 0xff;
+ scmd->uscsi_cdb[8] = (nblk >> 8) & 0xff;
+ scmd->uscsi_cdb[7] = (nblk >> 16) & 0xff;
+ scmd->uscsi_cdb[6] = (nblk >> 24) & 0xff;
+ scmd->uscsi_cdblen = 12;
+ scmd->uscsi_bufaddr = (char *)buf;
+ scmd->uscsi_buflen = nblk*2352;
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = uscsi(target->d_fd, scmd);
+ if (ret >= 0)
+ break;
+ }
+
+ if (ret < 0)
+ return (0);
+ return (1);
+}
+
+int
+toshiba_speed_ctrl(cd_device *dev, int cmd, int speed)
+{
+ uchar_t *mpage;
+ struct uscsi_cmd *scmd;
+ int ret;
+
+ if ((cmd == GET_WRITE_SPEED) || (cmd == SET_WRITE_SPEED)) {
+ if (debug) {
+ (void) printf("toshiba_speed_ctrl: WRONG CMD %d\n",
+ cmd);
+ }
+ return (0);
+ }
+
+ if (cmd == SET_READ_SPEED) {
+ if (dev->d_cap & DEV_CAP_SETTING_SPEED_NOT_ALLOWED) {
+ if (verbose)
+ err_msg(gettext(
+ "Cannot set speed on this device.\n"));
+ return (0);
+ }
+ if (speed == 32) {
+ if (strncmp("SUN32XCD",
+ (const char *)&dev->d_inq[24], 8) == 0)
+ return (1);
+ }
+ if ((speed != 1) && (speed != 2) && (speed != 4) &&
+ (speed != 12)) {
+ if (verbose)
+ err_msg(gettext(
+ "%dx speed is not supported by the device.\n"));
+ return (0);
+ }
+ }
+
+ ret = 0;
+ mpage = (uchar_t *)my_zalloc(16);
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
+ scmd->uscsi_timeout = 60;
+ scmd->uscsi_cdblen = 6;
+ scmd->uscsi_bufaddr = (char *)mpage;
+ scmd->uscsi_buflen = 16;
+ /* 6 byte mode sense for older drives */
+ scmd->uscsi_cdb[0] = MODE_SENSE_6_CMD;
+ scmd->uscsi_cdb[2] = 0x31;
+ scmd->uscsi_cdb[4] = 16;
+ if (uscsi(dev->d_fd, scmd) < 0)
+ goto end_speed_ctrl;
+ if (cmd == GET_READ_SPEED) {
+ ret = speed_tbl[mpage[14] & 0x3];
+ goto end_speed_ctrl;
+ }
+ if (cmd == SET_READ_SPEED) {
+ (void) memset(mpage, 0, 9);
+ mpage[3] = 8;
+ mpage[12] = 0x31;
+ mpage[13] = 2;
+ mpage[14] = rev_speed_tbl[speed];
+ scmd = get_uscsi_cmd();
+ scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
+ scmd->uscsi_timeout = 60;
+ scmd->uscsi_cdblen = 6;
+ scmd->uscsi_bufaddr = (char *)mpage;
+ scmd->uscsi_buflen = 16;
+ /* 6 byte mode sense command for older drives */
+ scmd->uscsi_cdb[0] = MODE_SELECT_6_CMD;
+ scmd->uscsi_cdb[1] = 0x10;
+ scmd->uscsi_cdb[4] = 16;
+ if (uscsi(dev->d_fd, scmd) < 0)
+ goto end_speed_ctrl;
+ ret = 1;
+ }
+end_speed_ctrl:
+ free(mpage);
+ return (ret);
+}
diff --git a/usr/src/cmd/cdrw/toshiba.h b/usr/src/cmd/cdrw/toshiba.h
new file mode 100644
index 0000000000..c77289ae8f
--- /dev/null
+++ b/usr/src/cmd/cdrw/toshiba.h
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _TOSHIBA_H
+#define _TOSHIBA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "device.h"
+
+int read_toc_as_per_8020(int fd, int format, int trackno, int buflen,
+ uchar_t *buf);
+int toshiba_read_audio(cd_device *target, uint_t start_blk, uint_t nblk,
+ uchar_t *buf);
+int toshiba_speed_ctrl(cd_device *dev, int cmd, int speed);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TOSHIBA_H */
diff --git a/usr/src/cmd/cdrw/trackio.c b/usr/src/cmd/cdrw/trackio.c
new file mode 100644
index 0000000000..017cc45e14
--- /dev/null
+++ b/usr/src/cmd/cdrw/trackio.c
@@ -0,0 +1,579 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "device.h"
+#include "bstream.h"
+#include "trackio.h"
+#include "util.h"
+#include "mmc.h"
+#include "transport.h"
+#include "misc_scsi.h"
+#include "main.h"
+
+/*
+ * tio data
+ */
+static struct iobuf tio_iobs[NIOBS];
+static uchar_t tio_synch_initialized, tio_abort, tio_done;
+static int tio_errno;
+static mutex_t tio_mutex;
+static cond_t tio_cond;
+static int tio_fd, tio_trackno;
+static int tio_got_ctrl_c;
+
+/*
+ * Progress call back data.
+ */
+static mutex_t pcb_mutex;
+static cond_t pcb_cond;
+static uchar_t pcb_user_abort, pcb_done, pcb_synch_initialized;
+static uint_t pcb_completed_io_size;
+static int (*pcb_cb)(void *, int64_t);
+static void *pcb_arg;
+
+static void
+fini_tio_data(void)
+{
+ int i;
+ for (i = 0; i < NIOBS; i++) {
+ if (tio_iobs[i].iob_buf) {
+ free(tio_iobs[i].iob_buf);
+ tio_iobs[i].iob_buf = NULL;
+ }
+ }
+ if (tio_synch_initialized == 1) {
+ (void) mutex_destroy(&tio_mutex);
+ (void) cond_destroy(&tio_cond);
+ tio_synch_initialized = 0;
+ }
+ tio_abort = tio_done = 0;
+}
+
+static void
+init_tio_data(int bsize)
+{
+ int i;
+
+ (void) memset(tio_iobs, 0, sizeof (tio_iobs));
+ for (i = 0; i < NIOBS; i++) {
+ tio_iobs[i].iob_buf = (uchar_t *)my_zalloc(bsize);
+ tio_iobs[i].iob_total_size = bsize;
+ tio_iobs[i].iob_state = IOBS_EMPTY;
+ }
+ (void) mutex_init(&tio_mutex, USYNC_THREAD, 0);
+ (void) cond_init(&tio_cond, USYNC_THREAD, 0);
+ tio_synch_initialized = 1;
+ tio_abort = tio_done = 0;
+ tio_got_ctrl_c = 0;
+}
+
+static void
+init_pcb_data(void)
+{
+ (void) mutex_init(&pcb_mutex, USYNC_THREAD, 0);
+ (void) cond_init(&pcb_cond, USYNC_THREAD, 0);
+ pcb_user_abort = pcb_done = 0;
+ pcb_completed_io_size = 0;
+ pcb_synch_initialized = 1;
+}
+
+static void
+fini_pcb_data(void)
+{
+ if (pcb_synch_initialized == 1) {
+ (void) mutex_destroy(&pcb_mutex);
+ (void) cond_destroy(&pcb_cond);
+ pcb_synch_initialized = 0;
+ }
+ pcb_user_abort = pcb_done = 0;
+ pcb_completed_io_size = 0;
+}
+
+/* ARGSUSED */
+static void *
+write_to_cd(void *arg)
+{
+ int i;
+
+ i = 0;
+#ifndef lint
+ while (1) {
+#endif
+ (void) mutex_lock(&tio_mutex);
+ while ((tio_iobs[i].iob_state != IOBS_READY) &&
+ (tio_abort == 0)) {
+ /* Wait for buffer to become ready */
+ (void) cond_wait(&tio_cond, &tio_mutex);
+ }
+ if (tio_abort == 1) {
+ /* Do a flush cache before aborting */
+ (void) flush_cache(tio_fd);
+ (void) mutex_unlock(&tio_mutex);
+ thr_exit((void *)1);
+ }
+ tio_iobs[i].iob_state = IOBS_UNDER_DEVICE_IO;
+
+ /* If no more data, then close the track */
+ if (tio_iobs[i].iob_data_size == 0) {
+ int retry = 20;
+
+ /* Some drives misbehave if flush_cache is not done */
+ (void) flush_cache(tio_fd);
+
+ if (write_mode == TAO_MODE) {
+ /* Its important to try hard to close track */
+ if (simulation)
+ retry = 5;
+
+ for (; retry > 0; retry--) {
+
+ /* OK to hold mutex when close_track */
+ if (close_track(tio_fd,
+ tio_trackno, 0, 0))
+ break;
+
+ (void) sleep(1);
+ }
+ }
+
+ /* Some drives don't allow close track in test write */
+ if ((retry == 0) && (simulation == 0)) {
+ if (errno)
+ tio_errno = errno;
+ else
+ tio_errno = -1;
+ }
+
+ tio_done = 1;
+ (void) cond_broadcast(&tio_cond);
+ (void) mutex_unlock(&tio_mutex);
+ thr_exit((void *)0);
+ }
+
+ (void) mutex_unlock(&tio_mutex);
+
+ if (!write10(tio_fd, tio_iobs[i].iob_start_blk,
+ tio_iobs[i].iob_nblks, tio_iobs[i].iob_buf,
+ tio_iobs[i].iob_data_size)) {
+
+ int err = errno;
+ (void) mutex_lock(&tio_mutex);
+ if (err)
+ tio_errno = err;
+ else
+ tio_errno = -1;
+ (void) cond_broadcast(&tio_cond);
+ (void) mutex_unlock(&tio_mutex);
+ thr_exit((void *)2);
+ }
+
+ (void) mutex_lock(&tio_mutex);
+ tio_iobs[i].iob_state = IOBS_EMPTY;
+ (void) cond_broadcast(&tio_cond);
+ (void) mutex_unlock(&tio_mutex);
+ i++;
+ if (i == NIOBS)
+ i = 0;
+#ifndef lint
+ }
+#endif
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void *
+progress_callback(void *arg)
+{
+ int ret;
+
+pc_again:
+ (void) mutex_lock(&pcb_mutex);
+ if (!pcb_done) {
+ (void) cond_wait(&pcb_cond, &pcb_mutex);
+ }
+ if (pcb_done) {
+ (void) mutex_unlock(&pcb_mutex);
+ if (tio_got_ctrl_c) {
+ pcb_cb(pcb_arg, 0xFFFFFFFF);
+ }
+ thr_exit((void *)0);
+ }
+ (void) mutex_unlock(&pcb_mutex);
+ ret = pcb_cb(pcb_arg, pcb_completed_io_size);
+ if (ret != 0) {
+ (void) mutex_lock(&pcb_mutex);
+ pcb_user_abort = (uchar_t)ret;
+ (void) mutex_unlock(&pcb_mutex);
+ thr_exit((void *)0);
+ }
+#ifdef lint
+ return (NULL);
+#else
+ goto pc_again;
+#endif
+}
+
+/* ARGSUSED */
+static void
+trackio_sig_handler(int i)
+{
+ /* Dont need mutex as it is only modified here */
+ tio_got_ctrl_c = 1;
+ (void) signal(SIGINT, trackio_sig_handler);
+}
+
+int
+write_track(cd_device *dev, struct track_info *ti, bstreamhandle h,
+ int (*cb)(void *, int64_t), void *arg, struct trackio_error *te)
+{
+ int blksize, i, sz_read, rem;
+ uint32_t start_b;
+ thread_t tio_thread, pc_thread;
+ int write_cd_thr_created;
+ int progress_callback_thr_created;
+ int signal_handler_installed;
+ int retval;
+ void (*ohandler)(int);
+
+ write_cd_thr_created = progress_callback_thr_created = 0;
+ signal_handler_installed = retval = 0;
+
+ if (ti->ti_track_mode & 4)
+ blksize = DATA_TRACK_BLKSIZE;
+ else
+ blksize = AUDIO_TRACK_BLKSIZE;
+
+ /* Initialize buffers */
+ init_tio_data(NBLKS_PER_BUF*blksize);
+
+ /* Fill in all buffers before starting */
+ start_b = ti->ti_start_address;
+
+ /*
+ * Start filling initial buffer to ensure that there is plenty of
+ * data when writing begins.
+ */
+ for (i = 0; i < NIOBS; i++) {
+ sz_read = h->bstr_read(h, tio_iobs[i].iob_buf,
+ tio_iobs[i].iob_total_size);
+
+
+ /*
+ * We need to read the source file into the buffer and make
+ * sure that the data in the buffer is a multiple of the
+ * blocksize (data or audio blocksize). iob_total_size is a
+ * multiple of the blocksize so this case should only be
+ * encountered at EOF or from piped input.
+ */
+ while ((rem = (sz_read % blksize)) != 0) {
+ int ret;
+
+ /*
+ * rem contains the amount of data past the previous
+ * block boundry. we need to subtract it from the
+ * blocksize to get the amount needed to reach the
+ * next block boundry.
+ */
+
+ if ((sz_read + (blksize - rem)) >
+ tio_iobs[i].iob_total_size) {
+
+ /*
+ * This should not occur, but we are trying to
+ * write past the end of the buffer. return
+ * with an error.
+ */
+ sz_read = -1;
+ break;
+ }
+
+ /*
+ * Try to continue reading in case the data is being
+ * piped in.
+ */
+ ret = h->bstr_read(h, &tio_iobs[i].iob_buf[sz_read],
+ (blksize - rem));
+
+ if (ret < 0) {
+ sz_read = ret;
+ break;
+ }
+
+ /*
+ * No more data. We need to make sure that we are
+ * aligned with the blocksize. so pad the rest of
+ * the buffer with 0s
+ */
+
+ if (ret == 0) {
+ ret = blksize - rem;
+ (void) memset(&tio_iobs[i].iob_buf[sz_read],
+ 0, ret);
+ }
+ sz_read += ret;
+ }
+
+ if (sz_read < 0) {
+
+ /* reading the source failed, clean up and return */
+ te->err_type = TRACKIO_ERR_SYSTEM;
+ te->te_errno = errno;
+ goto write_track_failed;
+ }
+
+ tio_iobs[i].iob_start_blk = start_b;
+ tio_iobs[i].iob_nblks = (sz_read/blksize);
+ start_b += tio_iobs[i].iob_nblks;
+ tio_iobs[i].iob_data_size = sz_read;
+ tio_iobs[i].iob_state = IOBS_READY;
+ if (sz_read == 0)
+ break;
+ }
+
+ tio_fd = dev->d_fd;
+ tio_trackno = ti->ti_track_no;
+
+ /* Install signal handler for CTRL-C */
+ ohandler = signal(SIGINT, trackio_sig_handler);
+ if (ohandler) {
+ signal_handler_installed = 1;
+ }
+
+ /* Create thread which will issue commands to write to device */
+ if (thr_create(0, 0, write_to_cd, NULL,
+ THR_BOUND | THR_NEW_LWP, &tio_thread) != 0) {
+ te->err_type = TRACKIO_ERR_SYSTEM;
+ te->te_errno = errno;
+ goto write_track_failed;
+ }
+ write_cd_thr_created = 1;
+
+ /* If caller specified a callback, create a thread to do callbacks */
+ if (cb != NULL) {
+ init_pcb_data();
+ pcb_cb = cb;
+ pcb_arg = arg;
+ if (thr_create(0, 0, progress_callback, NULL,
+ THR_BOUND | THR_NEW_LWP, &pc_thread) != 0) {
+ te->err_type = TRACKIO_ERR_SYSTEM;
+ te->te_errno = errno;
+ goto write_track_failed;
+ }
+ progress_callback_thr_created = 1;
+ }
+
+ i = 0;
+ while (sz_read != 0) {
+ (void) mutex_lock(&tio_mutex);
+ while ((tio_iobs[i].iob_state != IOBS_EMPTY) &&
+ (tio_errno == 0) && (pcb_user_abort == 0)) {
+
+ /* Do callbacks only if there is nothing else to do */
+ if (cb != NULL) {
+ (void) mutex_lock(&pcb_mutex);
+ (void) cond_broadcast(&pcb_cond);
+ (void) mutex_unlock(&pcb_mutex);
+ }
+
+ /* If user requested abort, bail out */
+ if (pcb_user_abort || tio_got_ctrl_c) {
+ break;
+ }
+ (void) cond_wait(&tio_cond, &tio_mutex);
+ }
+ if (pcb_user_abort || tio_got_ctrl_c) {
+ (void) mutex_unlock(&tio_mutex);
+ te->err_type = TRACKIO_ERR_USER_ABORT;
+ goto write_track_failed;
+ }
+ /*
+ * We've got a transport error, stop writing, save all
+ * of the error information and clean up the threads.
+ */
+ if (tio_errno != 0) {
+ (void) mutex_unlock(&tio_mutex);
+ te->err_type = TRACKIO_ERR_TRANSPORT;
+ te->te_errno = tio_errno;
+ te->status = uscsi_status;
+ if (uscsi_status == 2) {
+ te->key = SENSE_KEY(rqbuf) & 0xf;
+ te->asc = ASC(rqbuf);
+ te->ascq = ASCQ(rqbuf);
+ }
+ goto write_track_failed;
+ }
+ pcb_completed_io_size += tio_iobs[i].iob_data_size;
+ tio_iobs[i].iob_state = IOBS_UNDER_FILE_IO;
+ (void) mutex_unlock(&tio_mutex);
+
+ sz_read = h->bstr_read(h, tio_iobs[i].iob_buf,
+ tio_iobs[i].iob_total_size);
+
+ /*
+ * We need to read the source file into the buffer and make
+ * sure that the data in the buffer is a multiple of the
+ * blocksize (data or audio blocksize). this case should only
+ * be encountered at EOF or from piped input.
+ */
+
+ while ((rem = (sz_read % blksize)) != 0) {
+ int ret;
+
+
+ /*
+ * This should not occur, we are trying to write
+ * past the end of the buffer, return error.
+ */
+
+ if ((sz_read + (blksize - rem)) >
+ tio_iobs[i].iob_total_size) {
+
+ sz_read = -1;
+ break;
+ }
+
+ /*
+ * Try to continue reading in case the data is being
+ * piped in.
+ */
+
+ ret = h->bstr_read(h, &tio_iobs[i].iob_buf[sz_read],
+ (blksize - rem));
+
+ if (ret < 0) {
+ sz_read = ret;
+ break;
+ }
+
+ /*
+ * No more data. We need to make sure that we are
+ * aligned with the blocksize. so pad the rest of
+ * the buffer with 0s
+ */
+
+ if (ret == 0) {
+ /*
+ * rem contains the amount of data past the
+ * previous block boundry. we need to subtract
+ * it from the blocksize to get the amount
+ * needed to reach the next block boundry.
+ */
+ ret = blksize - rem;
+ (void) memset(&tio_iobs[i].iob_buf[sz_read],
+ 0, ret);
+ }
+ sz_read += ret;
+ }
+ if (sz_read < 0) {
+ te->err_type = TRACKIO_ERR_SYSTEM;
+ te->te_errno = errno;
+ goto write_track_failed;
+ }
+ (void) mutex_lock(&tio_mutex);
+ tio_iobs[i].iob_start_blk = start_b;
+ tio_iobs[i].iob_nblks = (sz_read/blksize);
+ start_b += tio_iobs[i].iob_nblks;
+ tio_iobs[i].iob_data_size = sz_read;
+ tio_iobs[i].iob_state = IOBS_READY;
+ (void) cond_broadcast(&tio_cond);
+ (void) mutex_unlock(&tio_mutex);
+ i++;
+ if (i == NIOBS)
+ i = 0;
+ }
+ (void) mutex_lock(&tio_mutex);
+ while ((tio_errno == 0) && (tio_done == 0)) {
+
+ /* Wait for track IO to complete */
+ (void) cond_wait(&tio_cond, &tio_mutex);
+ if (tio_errno != 0) {
+ te->err_type = TRACKIO_ERR_TRANSPORT;
+ te->te_errno = tio_errno;
+ te->status = uscsi_status;
+ if (uscsi_status == 2) {
+ te->key = SENSE_KEY(rqbuf) & 0xf;
+ te->asc = ASC(rqbuf);
+ te->ascq = ASCQ(rqbuf);
+ }
+ (void) mutex_unlock(&tio_mutex);
+ goto write_track_failed;
+ }
+ if (cb != NULL) {
+ while (tio_iobs[i].iob_state == IOBS_EMPTY) {
+ (void) mutex_lock(&pcb_mutex);
+ pcb_completed_io_size +=
+ tio_iobs[i].iob_data_size;
+ (void) cond_broadcast(&pcb_cond);
+ (void) mutex_unlock(&pcb_mutex);
+ i++;
+ if (i == NIOBS)
+ i = 0;
+ }
+ }
+ }
+ (void) mutex_unlock(&tio_mutex);
+ retval = 1;
+write_track_failed:
+ if (progress_callback_thr_created) {
+ if (thr_kill(pc_thread, 0) == 0) {
+ (void) mutex_lock(&pcb_mutex);
+
+ pcb_done = 1;
+ (void) cond_broadcast(&pcb_cond);
+ (void) mutex_unlock(&pcb_mutex);
+ (void) thr_join(pc_thread, NULL, NULL);
+ }
+ }
+ if (write_cd_thr_created) {
+ if (thr_kill(tio_thread, 0) == 0) {
+ (void) mutex_lock(&tio_mutex);
+ tio_abort = 1;
+ (void) cond_broadcast(&tio_cond);
+ (void) mutex_unlock(&tio_mutex);
+ (void) thr_join(tio_thread, NULL, NULL);
+ }
+ }
+
+ if (signal_handler_installed) {
+ (void) signal(SIGINT, ohandler);
+ }
+
+ fini_tio_data();
+ fini_pcb_data();
+ return (retval);
+}
diff --git a/usr/src/cmd/cdrw/trackio.h b/usr/src/cmd/cdrw/trackio.h
new file mode 100644
index 0000000000..611d2f8d17
--- /dev/null
+++ b/usr/src/cmd/cdrw/trackio.h
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TRACKIO_H
+#define _TRACKIO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include "bstream.h"
+#include "misc_scsi.h"
+
+struct trackio_error {
+ int err_type;
+/* File I/O errors */
+ int te_errno;
+/* Transport Errors */
+ uchar_t status, key, asc, ascq;
+};
+
+/*
+ * trackio error types
+ */
+#define TRACKIO_ERR_SYSTEM 1
+#define TRACKIO_ERR_TRANSPORT 2
+#define TRACKIO_ERR_USER_ABORT 3
+
+/*
+ * iob state
+ */
+#define IOBS_UNDER_FILE_IO 1
+#define IOBS_UNDER_DEVICE_IO 2
+#define IOBS_READY 3
+#define IOBS_EMPTY 4
+
+struct iobuf {
+ uchar_t *iob_buf;
+ uint32_t iob_total_size; /* total size of the buf */
+ uint32_t iob_data_size; /* size of the data in buf */
+ uint32_t iob_start_blk; /* starting block address on the device */
+ uint16_t iob_nblks; /* number of data blocks in this buf */
+ int iob_state; /* state of buf */
+};
+
+/* Use small buffers. Some drives do not behave well with large buffers */
+#define NIOBS 8
+#define NBLKS_PER_BUF 24 /* < 64K in all cases */
+#define DATA_TRACK_BLKSIZE 2048
+#define AUDIO_TRACK_BLKSIZE 2352
+
+int write_track(cd_device *dev, struct track_info *ti, bstreamhandle h,
+ int (*cb)(void *, int64_t), void *arg, struct
+ trackio_error *te);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRACKIO_H */
diff --git a/usr/src/cmd/cdrw/transport.c b/usr/src/cmd/cdrw/transport.c
new file mode 100644
index 0000000000..1f479eb74c
--- /dev/null
+++ b/usr/src/cmd/cdrw/transport.c
@@ -0,0 +1,224 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libintl.h>
+
+#include "transport.h"
+#include "main.h"
+#include "util.h"
+#include "mmc.h"
+
+char rqbuf[RQBUFLEN];
+uchar_t uscsi_status, rqstatus, rqresid;
+static struct uscsi_cmd uscmd;
+static char ucdb[16];
+static uint_t total_retries;
+
+struct uscsi_cmd *
+get_uscsi_cmd(void)
+{
+ (void) memset(&uscmd, 0, sizeof (uscmd));
+ (void) memset(ucdb, 0, 16);
+ uscmd.uscsi_cdb = ucdb;
+ return (&uscmd);
+}
+
+int
+uscsi(int fd, struct uscsi_cmd *scmd)
+{
+ int ret, global_rqsense;
+ int retries, max_retries;
+
+ /* set up for request sense extensions */
+ if (!(scmd->uscsi_flags & USCSI_RQENABLE)) {
+ scmd->uscsi_flags |= USCSI_RQENABLE;
+ scmd->uscsi_rqlen = RQBUFLEN;
+ scmd->uscsi_rqbuf = rqbuf;
+ global_rqsense = 1;
+ } else {
+ global_rqsense = 0;
+ }
+
+ /*
+ * Some DVD drives may have a delay for writing or sync cache, and
+ * read media info (done after syncing cache). This can take a
+ * significant number of time. Such as the Pioneer A0X which will
+ * generate TOC after the cache is full in the middle of writing.
+ */
+
+ if ((device_type != CD_RW) && ((scmd->uscsi_cdb[0] == WRITE_10_CMD) ||
+ (scmd->uscsi_cdb[0] == READ_INFO_CMD) || (scmd->uscsi_cdb[0] ==
+ SYNC_CACHE_CMD) || (scmd->uscsi_cdb[0] == CLOSE_TRACK_CMD))) {
+
+ max_retries = 500;
+ } else {
+ max_retries = 20;
+ }
+
+ /*
+ * The device may be busy or slow and fail with a not ready status.
+ * we'll allow a limited number of retries to give the drive time
+ * to recover.
+ */
+ for (retries = 0; retries < max_retries; retries++) {
+
+ scmd->uscsi_status = 0;
+
+ if (global_rqsense)
+ (void) memset(rqbuf, 0, RQBUFLEN);
+
+ if (debug && verbose) {
+ int i;
+
+ (void) printf("cmd:[");
+ for (i = 0; i < scmd->uscsi_cdblen; i++)
+ (void) printf("0x%02x ",
+ (uchar_t)scmd->uscsi_cdb[i]);
+ (void) printf("]\n");
+ }
+
+ /*
+ * We need to have root privledges in order to use
+ * uscsi commands on the device.
+ */
+
+ raise_priv();
+ ret = ioctl(fd, USCSICMD, scmd);
+ lower_priv();
+
+ /* maintain consistency in case of sgen */
+ if ((ret == 0) && (scmd->uscsi_status == 2)) {
+ ret = -1;
+ errno = EIO;
+ }
+
+ /* if error and extended request sense, retrieve errors */
+ if (global_rqsense && (ret < 0) && (scmd->uscsi_status == 2)) {
+ /*
+ * The drive is not ready to recieve commands but
+ * may be in the process of becoming ready.
+ * sleep for a short time then retry command.
+ * SENSE/ASC = 2/4 : not ready
+ * ASCQ = 0 Not Reportable.
+ * ASCQ = 1 Becoming ready.
+ * ASCQ = 4 FORMAT in progress.
+ * ASCQ = 7 Operation in progress.
+ * ASCQ = 8 Long write in progress.
+ */
+ if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
+ ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
+ (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
+ total_retries++;
+ (void) sleep(3);
+ continue;
+ }
+
+ /*
+ * we do not print this out under normal circumstances
+ * since we have BUFE enabled and do not want to alarm
+ * users with uneccessary messages.
+ */
+ if (debug) {
+ if ((SENSE_KEY(rqbuf) == 5) && (ASC(rqbuf) ==
+ 0x21) && (ASCQ(rqbuf) == 2)) {
+ (void) printf(gettext(
+ "Buffer underrun occurred! trying to recover...\n"));
+ }
+ }
+
+ /*
+ * long write operation in progress, ms_delay is
+ * used for some fast drives with a short drive
+ * buffer. Such as Pioneer DVD-RW drives. They will
+ * begin to generate TOC when the buffer is initially
+ * full, then resume operation a few minutes later
+ * with the buffer emptying quickly.
+ */
+ if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
+ (ASCQ(rqbuf) == 8)) {
+ total_retries++;
+ ms_delay(500);
+ continue;
+ }
+ /*
+ * Device is not ready to transmit or a device reset
+ * has occurred. wait for a short period of time then
+ * retry the command.
+ */
+ if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
+ (ASC(rqbuf) == 0x29))) {
+ (void) sleep(3);
+ total_retries++;
+ continue;
+ }
+ /*
+ * Blank Sense, we don't know what the error is or if
+ * the command succeeded, Hope for the best. Some
+ * drives return blank sense periodically and will
+ * fail if this is removed.
+ */
+ if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
+ (ASCQ(rqbuf) == 0)) {
+ ret = 0;
+ break;
+ }
+
+ if (debug) {
+ (void) printf("cmd: 0x%02x ret:%i status:%02x "
+ " sense: %02x ASC: %02x ASCQ:%02x\n",
+ (uchar_t)scmd->uscsi_cdb[0], ret,
+ scmd->uscsi_status,
+ (uchar_t)SENSE_KEY(rqbuf),
+ (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf));
+ }
+ }
+
+ /* no errors we'll return */
+ break;
+ }
+
+ /* store the error status for later debug printing */
+ if ((ret < 0) && (global_rqsense)) {
+ uscsi_status = scmd->uscsi_status;
+ rqstatus = scmd->uscsi_rqstatus;
+ rqresid = scmd->uscsi_rqresid;
+
+ }
+
+ if (debug && retries) {
+ (void) printf("total retries: %d\n", total_retries);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cdrw/transport.h b/usr/src/cmd/cdrw/transport.h
new file mode 100644
index 0000000000..373b6b916a
--- /dev/null
+++ b/usr/src/cmd/cdrw/transport.h
@@ -0,0 +1,54 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TRANSPORT_H
+#define _TRANSPORT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/types.h>
+
+#define SENSE_KEY(rqbuf) (rqbuf[2]) /* scsi error category */
+#define ASC(rqbuf) (rqbuf[12]) /* additional sense code */
+#define ASCQ(rqbuf) (rqbuf[13]) /* ASC qualifier */
+
+#define RQBUFLEN 32
+extern char rqbuf[RQBUFLEN];
+uchar_t uscsi_status, rqstatus, rqresid;
+
+struct uscsi_cmd *get_uscsi_cmd(void);
+int uscsi(int fd, struct uscsi_cmd *scmd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRANSPORT_H */
diff --git a/usr/src/cmd/cdrw/util.c b/usr/src/cmd/cdrw/util.c
new file mode 100644
index 0000000000..416552d3c9
--- /dev/null
+++ b/usr/src/cmd/cdrw/util.c
@@ -0,0 +1,293 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <time.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <secdb.h>
+
+#include "transport.h"
+#include "util.h"
+#include "mmc.h"
+#include "msgs.h"
+#include "misc_scsi.h"
+#include "main.h"
+#include "trackio.h"
+#include "bstream.h"
+
+char strbuf[81];
+int priv_change_needed = 0;
+
+void *
+my_zalloc(size_t size)
+{
+ void *ret;
+
+ ret = malloc(size);
+ if (ret == NULL) {
+
+ /* Lets wait a sec. and try again */
+ if (errno == EAGAIN) {
+ (void) sleep(1);
+ ret = malloc(size);
+ }
+
+ if (ret == NULL) {
+ (void) err_msg("%s\n", gettext(strerror(errno)));
+ (void) err_msg(gettext(
+ "Memory allocation failure, Exiting...\n"));
+ exit(1);
+ }
+ }
+ (void) memset(ret, 0, size);
+ return (ret);
+}
+
+/*
+ * Prints a string after going back pos number of steps.
+ * Mainly used to show %age complete.
+ */
+int
+str_print(char *str, int pos)
+{
+ if ((pos > 0) && (pos < 80)) {
+ (void) memset(strbuf, 8, pos);
+ strbuf[pos] = 0;
+ (void) printf(strbuf);
+ (void) memset(strbuf, ' ', pos);
+ strbuf[pos] = 0;
+ (void) printf(strbuf);
+ (void) memset(strbuf, 8, pos);
+ strbuf[pos] = 0;
+ (void) printf(strbuf);
+ }
+
+ (void) printf("%s", str);
+ (void) fflush(stdout);
+ return (strlen(str));
+}
+
+/*
+ * dump the trackio_error struct.
+ */
+void
+print_trackio_error(struct trackio_error *te)
+{
+ char *msg, *msg1;
+
+ msg = gettext("System could not supply data at the required rate.\n");
+ msg1 = gettext("Try using a lower speed for write\n");
+
+ switch (te->err_type) {
+ case TRACKIO_ERR_SYSTEM:
+ err_msg(gettext("System error: %s\n"), strerror(te->te_errno));
+ return;
+ case TRACKIO_ERR_TRANSPORT:
+ err_msg(gettext("Transport mechanism error:\n"));
+ if (te->status == 2) {
+ if ((te->key == 3) && (te->asc == 0x0c) &&
+ (te->ascq == 9)) {
+ err_msg(msg);
+ err_msg(msg1);
+ return;
+ }
+ if (te->key == 3) {
+ err_msg(gettext("Bad media.\n"));
+ return;
+ }
+ if (debug) {
+ err_msg("Sense key %x, asc/asq %x/%x\n",
+ te->key, te->asc, te->ascq);
+ } else {
+ err_msg(gettext("I/O error\n"));
+ }
+ return;
+ }
+ if (te->te_errno != 0)
+ err_msg("%s\n", strerror(te->te_errno));
+ return;
+ case TRACKIO_ERR_USER_ABORT:
+ err_msg(gettext("User abort.\n"));
+ return;
+ default:
+ err_msg(gettext("Unknown error type.\n"));
+ if (debug) {
+ err_msg("Trackio err type %d\n", te->err_type);
+ }
+ }
+}
+
+char *
+get_err_str(void)
+{
+ if (str_errno != 0)
+ return (str_errno_to_string(str_errno));
+ return (strerror(errno));
+}
+
+int
+get_audio_type(char *ext)
+{
+ if ((strcasecmp(ext, "au") == 0) ||
+ (strcasecmp(ext, "sun") == 0))
+ return (AUDIO_TYPE_SUN);
+ if ((strcasecmp(ext, "wav") == 0) ||
+ (strcasecmp(ext, "riff") == 0))
+ return (AUDIO_TYPE_WAV);
+ if (strcasecmp(ext, "cda") == 0)
+ return (AUDIO_TYPE_CDA);
+ if (strcasecmp(ext, "aur") == 0)
+ return (AUDIO_TYPE_AUR);
+
+ return (-1);
+}
+
+/*
+ * common routines for showing progress.
+ */
+
+int progress_pos;
+static uint64_t last_total;
+time_t tm;
+
+void
+init_progress(void)
+{
+ progress_pos = 0;
+ last_total = 0;
+ tm = time(NULL);
+}
+
+int
+progress(void *arg, int64_t completed)
+{
+ char s[BUFSIZE];
+ uint64_t total = (uintptr_t)arg & 0xffffffff;
+ if (completed == -1) {
+ /* Got ^C. Add 2 to progress pos to compensate for ^ and C */
+ progress_pos = str_print("(flushing ...)", progress_pos+2);
+ return (0);
+ }
+ if (total == 0) {
+ if (tm != time(NULL)) {
+ tm = time(NULL);
+ (void) snprintf(s, BUFSIZE,
+ gettext("%d bytes written"), completed);
+
+ progress_pos = str_print(s, progress_pos);
+ }
+ } else {
+ total = (((uint64_t)completed) * 100)/total;
+ if (total == last_total)
+ return (0);
+ last_total = total;
+ if (total > 100) {
+ /* There is clearly a miscalculation somewhere */
+ if (debug)
+ (void) printf("\nWrote more than 100 %% !!\n");
+ return (0);
+ }
+ if (total == 100) {
+ /* l10n_NOTE : 'done' as in "Writing track 1...done" */
+ (void) snprintf(s, BUFSIZE, gettext("done.\n"));
+ } else {
+ (void) snprintf(s, BUFSIZE, "%d %%", (uint_t)total);
+ }
+ progress_pos = str_print(s, progress_pos);
+ }
+ return (0);
+}
+
+void
+raise_priv(void)
+{
+ if (priv_change_needed && (cur_uid != 0)) {
+ if (seteuid(0) == 0)
+ cur_uid = 0;
+ }
+}
+
+void
+lower_priv(void)
+{
+ if (priv_change_needed && (cur_uid == 0)) {
+ if (seteuid(ruid) == 0)
+ cur_uid = ruid;
+ }
+}
+
+int
+check_auth(uid_t uid)
+{
+ struct passwd *pw;
+
+
+ pw = getpwuid(uid);
+
+ if (pw == NULL) {
+ /* fail if we cannot get password entry */
+ return (0);
+ }
+
+ /*
+ * check in the RBAC authority files to see if
+ * the user has permission to use CDRW
+ */
+ if (chkauthattr(CDRW_AUTH, pw->pw_name) != 1) {
+ /* user is not in database, return failure */
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+/*
+ * This will busy delay in ms milliseconds. Needed for cases
+ * where 1 sec wait is too long. This is needed for some newer
+ * drives which can empty the drive cache very quickly.
+ */
+void
+ms_delay(uint_t ms)
+{
+
+ hrtime_t start, req;
+
+ start = gethrtime();
+ req = start + ((hrtime_t)ms * 1000000);
+
+ while (gethrtime() < req)
+ yield();
+}
diff --git a/usr/src/cmd/cdrw/util.h b/usr/src/cmd/cdrw/util.h
new file mode 100644
index 0000000000..daa24c024c
--- /dev/null
+++ b/usr/src/cmd/cdrw/util.h
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include "device.h"
+#include "trackio.h"
+#include "bstream.h"
+
+#define EXIT_IF_CHECK_FAILED 0x80000000
+#define BUFSIZE 32
+/*
+ * Condition to be checked for a device
+ */
+#define CHECK_TYPE_NOT_CDROM 1
+#define CHECK_DEVICE_NOT_READY 2
+#define CHECK_DEVICE_NOT_WRITABLE 4
+#define CHECK_NO_MEDIA 8
+#define CHECK_MEDIA_IS_NOT_WRITABLE 0x10
+#define CHECK_MEDIA_IS_NOT_BLANK 0x20
+#define CHECK_MEDIA_IS_NOT_ERASABLE 0x40
+
+/*
+ * audio types
+ */
+#define AUDIO_TYPE_NONE 0
+#define AUDIO_TYPE_SUN 1
+#define AUDIO_TYPE_WAV 2
+#define AUDIO_TYPE_CDA 3
+#define AUDIO_TYPE_AUR 4
+
+extern int progress_pos;
+extern int priv_change_needed;
+
+void *my_zalloc(size_t size);
+int str_print(char *str, int pos);
+void print_trackio_error(struct trackio_error *te);
+char *get_err_str(void);
+int get_audio_type(char *ext);
+void init_progress(void);
+int progress(void *arg, int64_t completed);
+void raise_priv(void);
+void lower_priv(void);
+int check_auth(uid_t uid);
+void ms_delay(uint_t ms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTIL_H */
diff --git a/usr/src/cmd/cdrw/write_audio.c b/usr/src/cmd/cdrw/write_audio.c
new file mode 100644
index 0000000000..4d806965b1
--- /dev/null
+++ b/usr/src/cmd/cdrw/write_audio.c
@@ -0,0 +1,154 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <stdlib.h>
+#include <libintl.h>
+
+#include "bstream.h"
+#include "trackio.h"
+#include "misc_scsi.h"
+#include "util.h"
+#include "msgs.h"
+#include "main.h"
+#include "trackio.h"
+#include "mmc.h"
+
+static bstreamhandle
+open_audio(char *fname)
+{
+ int at;
+ char *ext;
+
+ /* No audio type specified, look at extension */
+ if (audio_type == AUDIO_TYPE_NONE) {
+ ext = (char *)(strrchr(fname, '.'));
+ if (ext) {
+ ext++;
+ }
+ if ((ext == NULL) || ((at = get_audio_type(ext)) == -1)) {
+ err_msg(gettext(
+ "Cannot understand file extension for %s\n"),
+ fname);
+ exit(1);
+ }
+ } else {
+ at = audio_type;
+ }
+ if (at == AUDIO_TYPE_SUN)
+ return (open_au_read_stream(fname));
+ if (at == AUDIO_TYPE_WAV)
+ return (open_wav_read_stream(fname));
+ if (at == AUDIO_TYPE_CDA)
+ return (open_file_read_stream(fname));
+ if (at == AUDIO_TYPE_AUR)
+ return (open_aur_read_stream(fname));
+ return (NULL);
+}
+
+void
+write_audio(char **argv, int start_argc, int argc)
+{
+ bstreamhandle *h_ptr;
+ int i, nfiles;
+ struct track_info *ti;
+ int blks_req, blks_avail;
+ off_t fsize;
+
+ /* number of tracks to write */
+ nfiles = argc - start_argc;
+ h_ptr = (bstreamhandle *)my_zalloc(nfiles * sizeof (bstreamhandle));
+ blks_req = 0;
+ for (i = 0; i < nfiles; i++) {
+ h_ptr[i] = open_audio(argv[start_argc + i]);
+ if (h_ptr[i] == NULL) {
+ err_msg(gettext("Cannot open %s: %s\n"),
+ argv[start_argc + i], get_err_str());
+ exit(1);
+ }
+ (void) (h_ptr[i])->bstr_size(h_ptr[i], &fsize);
+
+ /* 2352 bytes per block, 75 blocks per second */
+ blks_req += 150 + fsize/2352; /* 2 sec gap per track */
+ if (fsize % 2352)
+ blks_req++;
+ }
+ (void) check_device(target, CHECK_DEVICE_NOT_READY |
+ CHECK_DEVICE_NOT_WRITABLE | CHECK_MEDIA_IS_NOT_WRITABLE |
+ EXIT_IF_CHECK_FAILED);
+
+ /* Put the device in track-at-once mode */
+ write_init(TRACK_MODE_AUDIO);
+ ti = (struct track_info *)my_zalloc(TRACK_INFO_SIZE);
+
+ /* Build information for next invisible track, -1 */
+ if ((build_track_info(target, -1, ti) == 0) ||
+ ((ti->ti_flags & TI_NWA_VALID) == 0)) {
+ err_msg(gettext(
+ "Cannot get writable address for the media.\n"));
+ exit(1);
+ }
+ if (use_media_stated_capacity) {
+ blks_avail = get_last_possible_lba(target);
+ if (blks_avail == 0) {
+ err_msg(gettext("Cannot find out media capacity\n"));
+ exit(1);
+ }
+ /* LBA is always one less */
+ blks_avail++;
+ } else {
+ blks_avail = MAX_CD_BLKS;
+ }
+ /*
+ * Actual number of blocks available based on nwa (next writable
+ * address) since there may already be information on the disc.
+ */
+
+ blks_avail -= ti->ti_nwa;
+ if (blks_avail < blks_req) {
+ err_msg(gettext("Insufficient space on the media.\n"));
+ exit(1);
+ }
+ for (i = 0; i < nfiles; i++) {
+ write_next_track(TRACK_MODE_AUDIO, h_ptr[i]);
+ if (simulation && (nfiles != 1)) {
+ (void) printf(gettext(
+ "Simulation mode : skipping remaining tracks\n"));
+ break;
+ }
+ }
+ for (i = 0; i < nfiles; i++)
+ (h_ptr[i])->bstr_close(h_ptr[i]);
+ free(ti);
+ free(h_ptr);
+
+ write_fini();
+
+ fini_device(target);
+ exit(0);
+}
diff --git a/usr/src/cmd/cdrw/write_image.c b/usr/src/cmd/cdrw/write_image.c
new file mode 100644
index 0000000000..dc1569ae19
--- /dev/null
+++ b/usr/src/cmd/cdrw/write_image.c
@@ -0,0 +1,206 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <unistd.h>
+#include "trackio.h"
+#include "main.h"
+#include "util.h"
+#include "bstream.h"
+#include "misc_scsi.h"
+#include "msgs.h"
+#include "device.h"
+#include "mmc.h"
+#include "transport.h"
+
+void
+write_image(void)
+{
+ bstreamhandle h;
+ off_t size;
+ int no_size, ret;
+
+ get_media_type(target->d_fd);
+
+ /* DVD+RW does not have blanking and can be overwritten */
+ if (device_type != DVD_PLUS_W) {
+ (void) check_device(target, CHECK_DEVICE_NOT_READY |
+ CHECK_DEVICE_NOT_WRITABLE | CHECK_MEDIA_IS_NOT_WRITABLE |
+ EXIT_IF_CHECK_FAILED);
+ } else {
+ (void) check_device(target, CHECK_DEVICE_NOT_READY |
+ EXIT_IF_CHECK_FAILED);
+ }
+
+ write_init(TRACK_MODE_DATA);
+
+ if (image_file) {
+ h = open_file_read_stream(image_file);
+ } else {
+ h = open_stdin_read_stream();
+ }
+
+ if (h == NULL) {
+ err_msg(gettext("Cannot open %s: %s\n"),
+ image_file ? image_file : "stdin", get_err_str());
+ exit(1);
+ }
+ no_size = 0;
+ ret = h->bstr_size(h, &size);
+ if (ret == 0) {
+ if ((str_errno == STR_ERR_NO_REG_FILE)) {
+ no_size = 1;
+ } else {
+ err_msg(gettext("Cannot stat input file: %s\n"),
+ get_err_str());
+ exit(1);
+ }
+ }
+ if ((no_size == 0) && (size == 0)) {
+ err_msg(gettext("Input size(0) not valid\n"));
+ exit(1);
+ }
+ if (no_size == 0) {
+ off_t cap;
+ struct track_info *ti;
+ uint_t bsize;
+
+ ti = (struct track_info *)my_zalloc(sizeof (*ti));
+ if (write_mode == TAO_MODE)
+ if (!build_track_info(target, -1, ti)) {
+ err_msg(
+ gettext("Unable to find out writable address\n"));
+ exit(1);
+ }
+ if (use_media_stated_capacity) {
+ cap = get_last_possible_lba(target);
+ if (cap <= 0) {
+ cap = read_format_capacity(target->d_fd,
+ &bsize);
+ }
+ } else {
+ /*
+ * For DVD drives use read_format_capacity to retrieve
+ * the media size, it could be 3.6, 3.9, 4.2, 4.7, 9.2
+ */
+ if (device_type == CD_RW) {
+ cap = MAX_CD_BLKS;
+ } else {
+ /*
+ * For DVD drives use read_format_capacity to
+ * find media size, it can be 3.6, 3.9, 4.2,
+ * 4.7, 9.2
+ */
+ cap = read_format_capacity(target->d_fd,
+ &bsize);
+ /* sanity if not reasonable default to 4.7 GB */
+ if (cap < MAX_CD_BLKS)
+ cap = MAX_DVD_BLKS;
+ }
+ }
+ if (cap == 0) {
+ err_msg(gettext("Unable to find out media capacity\n"));
+ exit(1);
+ }
+ if (device_type == CD_RW)
+ cap = (cap + 1 - ti->ti_start_address) * 2048;
+ else
+ cap *= 2048 + 1;
+
+ if (size > cap) {
+ err_msg(gettext("Size required (%lld bytes) is greater "
+ "than available space (%lld bytes).\n"), size, cap);
+ exit(1);
+ }
+
+ if (device_type == DVD_MINUS) {
+ (void) printf(gettext("Preparing to write DVD\n"));
+
+ /* streamed file, we dont know the size to reserve */
+ if (no_size == 1) {
+ size = cap - 1;
+ }
+
+ /* DAO requires that we reserve the size to write */
+ if (debug)
+ (void) printf(
+ "DAO_MODE:reserving track size of = 0x%x\n",
+ (uint32_t)(size/2048));
+
+ if (!set_reservation(target->d_fd, size/2048)) {
+ (void) printf(gettext(
+ "Setting reservation failed\n"));
+ exit(1);
+ }
+ } else if (device_type == DVD_PLUS_W) {
+ /*
+ * DVD+RW requires that we format the media before
+ * writing.
+ */
+ (void) print_n_flush(gettext("Formatting media..."));
+ if (!format_media(target->d_fd)) {
+ (void) printf(gettext(
+ "Could not format media\n"));
+ exit(1);
+ } else {
+ int counter;
+ uchar_t *di;
+
+ /* poll until format is done */
+ di = (uchar_t *)my_zalloc(DISC_INFO_BLOCK_SIZE);
+ (void) sleep(10);
+ for (counter = 0; counter < 200; counter++) {
+
+ ret = read_disc_info(target->d_fd, di);
+
+ if ((SENSE_KEY(rqbuf) == 2) &&
+ (ASC(rqbuf) == 4)) {
+ (void) print_n_flush(".");
+ (void) sleep(5);
+ } else {
+ break;
+ }
+ }
+ }
+
+ (void) printf(gettext("done\n"));
+ }
+
+
+ free(ti);
+ }
+
+ write_next_track(TRACK_MODE_DATA, h);
+
+ h->bstr_close(h);
+ write_fini();
+ fini_device(target);
+ exit(0);
+}