diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cdrw | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cdrw')
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); +} |
