summaryrefslogtreecommitdiff
path: root/readom
diff options
context:
space:
mode:
Diffstat (limited to 'readom')
-rw-r--r--readom/CMakeLists.txt19
-rw-r--r--readom/io.c193
-rw-r--r--readom/readom.1497
-rw-r--r--readom/readom.c2194
4 files changed, 2903 insertions, 0 deletions
diff --git a/readom/CMakeLists.txt b/readom/CMakeLists.txt
new file mode 100644
index 0000000..81536d8
--- /dev/null
+++ b/readom/CMakeLists.txt
@@ -0,0 +1,19 @@
+PROJECT (READECD C)
+INCLUDE_DIRECTORIES(../include ../wodim ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include)
+INCLUDE(../include/AddScgBits.cmake)
+INCLUDE(../include/AddSchilyBits.cmake)
+
+AUX_SOURCE_DIRECTORY(. readom_SRCS)
+
+LINK_DIRECTORIES(../librols ../libusal ../wodim)
+
+ADD_DEFINITIONS(-DCLONE_WRITE)
+
+ADD_EXECUTABLE (readom ${readom_SRCS})
+TARGET_LINK_LIBRARIES(readom wodimstuff ${EXTRA_LIBS})
+SET_TARGET_PROPERTIES(readom PROPERTIES SKIP_BUILD_RPATH TRUE)
+
+INSTALL(TARGETS readom DESTINATION bin)
+INSTALL(FILES
+readom.1
+DESTINATION ${MANSUBDIR}/man1)
diff --git a/readom/io.c b/readom/io.c
new file mode 100644
index 0000000..364e0ed
--- /dev/null
+++ b/readom/io.c
@@ -0,0 +1,193 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)io.c 1.19 98/10/10 Copyright 1988 J. Schilling */
+/*
+ * Copyright (c) 1988 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <standard.h>
+#include <vadefs.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <utypes.h>
+#include <schily.h>
+#include <ctype.h>
+
+struct disk {
+ int dummy;
+};
+
+static char *skipwhite(const char *);
+static void prt_std(char *, long, long, long, struct disk *);
+BOOL cvt_std(char *, long *, long, long, struct disk *);
+extern BOOL getvalue(char *, long *, long, long,
+ void (*)(char *, long, long, long, struct disk *),
+ BOOL (*)(char *, long *, long, long, struct disk *),
+ struct disk *);
+extern BOOL getlong(char *, long *, long, long);
+extern BOOL getint(char *, int *, int, int);
+extern BOOL yes(char *, ...);
+
+static
+char *skipwhite(const char *s)
+{
+ register const Uchar *p = (const Uchar *)s;
+
+ while (*p) {
+ if (!isspace(*p))
+ break;
+ p++;
+ }
+ return ((char *)p);
+}
+
+/* ARGSUSED */
+
+BOOL
+cvt_std(char *linep, long *lp, long mini, long maxi, struct disk *dp)
+{
+ long l = -1L;
+
+/* printf("cvt_std(\"%s\", %d, %d, %d);\n", linep, *lp, mini, maxi);*/
+
+ if (linep[0] == '?') {
+ printf("Enter a number in the range from %ld to %ld\n",
+ mini, maxi);
+ printf("The default radix is 10\n");
+ printf("Precede number with '0x' for hexadecimal or with '0' for octal\n");
+ printf("Shorthands are:\n");
+ printf("\t'^' for minimum value (%ld)\n", mini);
+ printf("\t'$' for maximum value (%ld)\n", maxi);
+ printf("\t'+' for incrementing value to %ld\n", *lp + 1);
+ printf("\t'-' for decrementing value to %ld\n", *lp - 1);
+ return (FALSE);
+ }
+ if (linep[0] == '^' && *skipwhite(&linep[1]) == '\0') {
+ l = mini;
+ } else if (linep[0] == '$' && *skipwhite(&linep[1]) == '\0') {
+ l = maxi;
+ } else if (linep[0] == '+' && *skipwhite(&linep[1]) == '\0') {
+ if (*lp < maxi)
+ l = *lp + 1;
+ } else if (linep[0] == '-' && *skipwhite(&linep[1]) == '\0') {
+ if (*lp > mini)
+ l = *lp - 1;
+ } else if (*astol(linep, &l)) {
+ printf("Not a number: '%s'.\n", linep);
+ return (FALSE);
+ }
+ if (l < mini || l > maxi) {
+ printf("'%s' is out of range.\n", linep);
+ return (FALSE);
+ }
+ *lp = l;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static void
+prt_std(char *s, long l, long mini, long maxi, struct disk *dp)
+{
+ printf("%s %ld (%ld - %ld)/<cr>:", s, l, mini, maxi);
+}
+
+
+BOOL getvalue(char *s, long *lp, long mini, long maxi,
+ void (*prt)(char *, long, long, long, struct disk *),
+ BOOL (*cvt)(char *, long *, long, long, struct disk *),
+ struct disk *dp)
+{
+ char line[128];
+ char *linep;
+
+ for(;;) {
+ (*prt)(s, *lp, mini, maxi, dp);
+ flush();
+ line[0] = '\0';
+ if (rols_getline(line, 80) == EOF)
+ exit(EX_BAD);
+
+ linep = skipwhite(line);
+ /*
+ * Nicht initialisierte Variablen
+ * duerfen nicht uebernommen werden
+ */
+ if (linep[0] == '\0' && *lp != -1L)
+ return (FALSE);
+
+ if (strlen(linep) == 0) {
+ /* Leere Eingabe */
+ /* EMPTY */
+ } else if ((*cvt)(linep, lp, mini, maxi, dp))
+ return (TRUE);
+ }
+ /* NOTREACHED */
+}
+
+
+BOOL getlong(char *s, long *lp, long mini, long maxi)
+{
+ return (getvalue(s, lp, mini, maxi, prt_std, cvt_std, (void *)0));
+}
+
+
+BOOL getint(char *s, int *ip, int mini, int maxi)
+{
+ long l = *ip;
+ BOOL ret;
+
+ ret = getlong(s, &l, (long)mini, (long)maxi);
+ *ip = l;
+ return (ret);
+}
+
+/* VARARGS1 */
+BOOL yes(char *form, ...)
+{
+ va_list args;
+ char okbuf[10];
+
+again:
+ va_start(args, form);
+ vprintf(form, args);
+ va_end(args);
+ flush();
+ if (rols_getline(okbuf, sizeof(okbuf)) == EOF)
+ exit(EX_BAD);
+ if (okbuf[0] == '?') {
+ printf("Enter 'y', 'Y', 'yes' or 'YES' if you agree with the previous asked question.\n");
+ printf("All other input will be handled as if the question has beed answered with 'no'.\n");
+ goto again;
+ }
+ if(streql(okbuf, "y") || streql(okbuf, "yes") ||
+ streql(okbuf, "Y") || streql(okbuf, "YES"))
+ return(TRUE);
+ else
+ return(FALSE);
+}
diff --git a/readom/readom.1 b/readom/readom.1
new file mode 100644
index 0000000..3bdc47b
--- /dev/null
+++ b/readom/readom.1
@@ -0,0 +1,497 @@
+.\" @(#)readom.1 1.23 06/01/12 Copyright 1996-2006 J. Schilling
+.\"
+.\" Modified version of readcd.1 by J. Schilling, 11/2006
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License version 2
+.\" as published by the Free Software Foundation.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along with
+.\" this program; see the file COPYING. If not, write to the Free Software
+.\" Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.if t .ds a \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'a
+.if t .ds o \v'-0.55m'\h'0.00n'\z.\h'0.45n'\z.\v'0.55m'\h'-0.45n'o
+.if t .ds u \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'u
+.if t .ds A \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'A
+.if t .ds O \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'O
+.if t .ds U \v'-0.77m'\h'0.30n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.75n'U
+.if t .ds s \\(*b
+.if t .ds S SS
+.if n .ds a ae
+.if n .ds o oe
+.if n .ds u ue
+.if n .ds s sz
+.TH READOM 1 "Version 2.0" "J\*org Schilling" "Schily\'s USER COMMANDS"
+.SH NAME
+readom \- read or write data Compact Discs
+.SH SYNOPSIS
+.B readom
+.BI dev= device
+[
+.I options
+]
+
+.SH DESCRIPTION
+.B Readom
+is used to read or write Compact Discs.
+.PP
+The
+.I device
+refers to a device location similar to the one used in the wodim command. Refer to its manpage for details.
+.PP
+Also note that this version of readom uses a modified libusal library which has
+a different behaviour compared to the one distributed by its original author.
+
+.SH OPTIONS
+.PP
+If no options except the
+.I dev=
+option have been specified,
+.B readom
+goes into interactive mode.
+Select a primary function and then follow the instructions.
+.PP
+.TP
+.B \-version
+Print version information and exit.
+.TP
+.BI dev= target
+Sets the SCSI target for the drive, see notes above.
+A typical device specification is
+.BI dev= 6,0
+\&.
+If a filename must be provided together with the numerical target
+specification, the filename is implementation specific.
+The correct filename in this case can be found in the system specific
+manuals of the target operating system.
+On a
+.I FreeBSD
+system without
+.I CAM
+support, you need to use the control device (e.g.
+.IR /dev/rcd0.ctl ).
+A correct device specification in this case may be
+.BI dev= /dev/rcd0.ctl:@
+\&.
+.sp
+On Linux, drives connected to a parallel port adapter are mapped
+to a virtual SCSI bus. Different adapters are mapped to different
+targets on this virtual SCSI bus.
+.sp
+If no
+.I dev
+option is present,
+.B readom
+will try to get the device from the
+.B CDR_DEVICE
+environment.
+.sp
+If the argument to the
+.B dev=
+option does not contain the characters ',', '/', '@' or ':',
+it is interpreted as an label name that may be found in the file
+/etc/wodim.conf (see FILES section).
+.TP
+.BI timeout= #
+Set the default SCSI command timeout value to
+.IR # " seconds.
+The default SCSI command timeout is the minimum timeout used for sending
+SCSI commands.
+If a SCSI command fails due to a timeout, you may try to raise the
+default SCSI command timeout above the timeout value of the failed command.
+If the command runs correctly with a raised command timeout,
+please report the better timeout value and the corresponding command to
+the author of the program.
+If no
+.I timeout
+option is present, a default timeout of 40 seconds is used.
+.TP
+.BI debug= "#, " -d
+Set the misc debug value to # (with debug=#) or increment
+the misc debug level by one (with -d). If you specify
+.I -dd,
+this equals to
+.BI debug= 2.
+This may help to find problems while opening a driver for libusal.
+as well as with sector sizes and sector types.
+Using
+.B \-debug
+slows down the process and may be the reason for a buffer underrun.
+.TP
+.BR kdebug= "#, " kd= #
+Tell the
+.BR usal -driver
+to modify the kernel debug value while SCSI commands are running.
+.TP
+.BR \-silent ", " \-s
+Do not print out a status report for failed SCSI commands.
+.TP
+.B \-v
+Increment the level of general verbosity by one.
+This is used e.g. to display the progress of the process.
+.TP
+.B \-V
+Increment the verbose level with respect of SCSI command transport by one.
+This helps to debug problems
+during the process, that occur in the CD-Recorder.
+If you get incomprehensible error messages you should use this flag
+to get more detailed output.
+.B \-VV
+will show data buffer content in addition.
+Using
+.B \-V
+or
+.B \-VV
+slows down the process.
+.TP
+.BI f= file
+Specify the filename where the output should be written or the input should
+be taken from. Using '-' as filename will cause
+.B readom
+to use
+.BR stdout " resp. " stdin .
+.TP
+.B \-w
+Switch to write mode. If this option is not present,
+.B readom
+reads from the specified device.
+.TP
+.B \-c2scan
+Scans the whole CD or the range specified by the
+.BI sectors= range
+for C2 errors. C2 errors are errors that are uncorrectable after the second
+stage of the 24/28 + 28/32 Reed Solomon correction system at audio level
+(2352 bytes sector size). If an audio CD has C2 errors, interpolation is needed
+to hide the errors. If a data CD has C2 errors, these errors are in most
+cases corrected by the ECC/EDC code that makes 2352 bytes out of 2048 data
+bytes. The ECC/EDC code should be able to correct about 100 C2 error bytes
+per sector.
+.sp
+If you find C2 errors you may want to reduce the speed using the
+.B speed=
+option as C2 errors may be a result of dynamic unbalance on the medium.
+.TP
+.B \-scanbus
+Scan all SCSI devices on all SCSI busses and print the inquiry
+strings. This option may be used to find SCSI address of the
+devices on a system.
+The numbers printed out as labels are computed by:
+.B "bus * 100 + target
+.TP
+.BI sectors= range
+Specify a sector range that should be read.
+The range is specified by the starting sector number, a minus sign and the
+ending sector number.
+The end sector is not included in the list, so
+.BR sectors= 0-0
+will not read anything and may be used to check for a CD in the drive.
+.TP
+.BR speed= #
+Set the speed factor of the read or write process to #.
+# is an integer, representing a multiple of the audio speed.
+This is about 150 KB/s for CD-ROM and about 172 KB/s for CD-Audio.
+If no
+.I speed
+option is present,
+.B readom
+will use maximum speed.
+Only MMC compliant drives will benefit from this option.
+The speed of non MMC drives is not changed.
+.sp
+Using a lower speed may increase the readability of a CD or DVD.
+.TP
+.BR ts= #
+Set the maximum transfer size for a single SCSI command to #.
+The syntax for the
+.B ts=
+option is the same as for wodim fs=# or sdd bs=#.
+.sp
+If no
+.B ts=
+option has been specified,
+.B readom
+defaults to a transfer size of 256 kB. If libusal gets lower values from the
+operating system, the value is reduced to the maximum value that is possible
+with the current operating system.
+Sometimes, it may help to further reduce the transfer size or to enhance it,
+but note that it may take a long time to find a better value by experimenting
+with the
+.B ts=
+option.
+.TP
+.B \-notrunc
+Do not truncate the output file when opening it.
+.TP
+.B \-fulltoc
+Retrieve a full TOC from the current disk and print it in hex.
+.TP
+.B \-clone
+Do a clone read. Read the CD with all sub-channel data and a full TOC.
+The full TOC data will be put into a file with similar name as with the
+.B f=
+option but the suffix
+.B .toc
+added.
+.TP
+.B \-noerror
+Do not abort if the high level error checking in
+.B readom
+found an uncorrectable error in the data stream.
+.TP
+.B \-nocorr
+Switch the drive into a mode where it ignores read errors in data sectors that
+are a result of uncorrectable ECC/EDC errors before reading.
+If
+.B readom
+completes, the error recovery mode of the drive is switched back to the remembered
+old mode.
+.TP
+.BI retries= #
+Set the retry count for high level retries in
+.B readom
+to
+.IR # .
+The default is to do 128 retries which may be too much if you like to read a CD
+with many unreadable sectors.
+.TP
+.B \-overhead
+Meter the SCSI command overhead time.
+This is done by executing several commands 1000 times and printing the
+total time used. If you divide the displayed times by 1000, you get
+the average overhead time for a single command.
+.TP
+.BR meshpoints= #
+Print read-speed at # locations.
+The purpose of this option is to create a list of read speed values suitable
+for e.g.
+.BR gnuplot .
+The speed values are calculated assuming that 1000 bytes are one kilobyte
+as documented in the SCSI standard.
+The output data created for this purpose is written to
+.IR stdout .
+.TP
+.B \-factor
+Output the speed values for
+.BR meshpoints= #
+as factor based on
+.I "single speed
+of the current medium.
+This only works if
+.B readom
+is able to determine the current medium type.
+.SH EXAMPLES
+.PP
+For all examples below, it will be assumed that the drive is
+connected to the primary SCSI bus of the machine. The SCSI target id is
+set to 2.
+.PP
+To read the complete media from a CD-ROM writing the data to the file
+.IR cdimage.raw :
+.PP
+ readom dev=2,0 f=cdimage.raw
+.PP
+To read sectors from range 150 ... 10000 from a CD-ROM writing the data to the file
+.IR cdimage.raw :
+.PP
+ readom dev=2,0 sectors=150-10000 f=cdimage.raw
+.PP
+To write the data from the file
+.I cdimage.raw
+(e.g. a filesystem image from
+.BR genisoimage )
+to a DVD-RAM, call:
+.PP
+ readom dev=2,0 -w f=cdimage.raw
+
+.SH ENVIRONMENT
+.TP
+.B RSH
+If the
+.B RSH
+environment is present, the remote connection will not be created via
+.BR rcmd (3)
+but by calling the program pointed to by
+.BR RSH .
+Use e.g.
+.BR RSH= /usr/bin/ssh
+to create a secure shell connection.
+.sp
+Note that this forces
+.B wodim
+to create a pipe to the
+.B rsh(1)
+program and disallows
+.B wodim
+to directly access the network socket to the remote server.
+This makes it impossible to set up performance parameters and slows down
+the connection compared to a
+.B root
+initiated
+.B rcmd(3)
+connection.
+.TP
+.B RSCSI
+If the
+.B RSCSI
+environment is present, the remote SCSI server will not be the program
+.B /opt/schily/sbin/rscsi
+but the program pointed to by
+.BR RSCSI .
+Note that the remote SCSI server program name will be ignored if you log in
+using an account that has been created with a remote SCSI server program as
+login shell.
+.SH SEE ALSO
+.BR wodim (1),
+.BR genisoimage (1),
+.BR rcmd (3),
+.BR ssh (1).
+
+.SH NOTES
+.PP
+Unless you want to risk getting problems,
+.B readom
+should be run as root. If you don't want to allow users to become root on your system,
+.B readom
+may safely be installed suid root.
+For more information see the additional notes of your system/program
+distribution or README.suidroot which is part of the Cdrkit source.
+.PP
+Documentation of the
+.B wodim
+program contains more technical details which could also apply to
+.B readom.
+
+.SH DIAGNOSTICS
+.PP
+.PP
+A typical error message for a SCSI command looks like:
+.sp
+.RS
+.nf
+readom: I/O error. test unit ready: scsi sendcmd: no error
+CDB: 00 20 00 00 00 00
+status: 0x2 (CHECK CONDITION)
+Sense Bytes: 70 00 05 00 00 00 00 0A 00 00 00 00 25 00 00 00 00 00
+Sense Key: 0x5 Illegal Request, Segment 0
+Sense Code: 0x25 Qual 0x00 (logical unit not supported) Fru 0x0
+Sense flags: Blk 0 (not valid)
+cmd finished after 0.002s timeout 40s
+.fi
+.sp
+.RE
+The first line gives information about the transport of the command.
+The text after the first colon gives the error text for the system call
+from the view of the kernel. It usually is:
+.B "I/O error
+unless other problems happen. The next words contain a short description for
+the SCSI command that fails. The rest of the line tells you if there were
+any problems for the transport of the command over the SCSI bus.
+.B "fatal error
+means that it was not possible to transport the command (i.e. no device present
+at the requested SCSI address).
+.PP
+The second line prints the SCSI command descriptor block for the failed command.
+.PP
+The third line gives information on the SCSI status code returned by the
+command, if the transport of the command succeeds.
+This is error information from the SCSI device.
+.PP
+The fourth line is a hex dump of the auto request sense information for the
+command.
+.PP
+The fifth line is the error text for the sense key if available, followed
+by the segment number that is only valid if the command was a
+.I copy
+command. If the error message is not directly related to the current command,
+the text
+.I deferred error
+is appended.
+.PP
+The sixth line is the error text for the sense code and the sense qualifier if available.
+If the type of the device is known, the sense data is decoded from tables
+in
+.IR scsierrs.c " .
+The text is followed by the error value for a field replaceable unit.
+.PP
+The seventh line prints the block number that is related to the failed command
+and text for several error flags. The block number may not be valid.
+.PP
+The eight line reports the timeout set up for this command and the time
+that the command really needed to complete.
+
+.SH BUGS
+.PP
+The
+.B readom
+program described here is the Cdrkit spinoff from the original
+.B readcd
+application (see AUTHOR section for details). It may contain bugs not present
+in the original implementation.
+.PP
+It is definitely less portable than the original implementation.
+.PP
+For platform specific bugs, see the corresponding README.platform file in the
+Cdrkit documentation (eg. README.linux).
+
+.SH "MAILING LISTS
+If you want to actively take part on the development of readom,
+you may join the developer mailing list via this URL:
+.sp
+.B
+http://alioth.debian.org/mail/?group_id=31006
+.PP
+The mail address of the list is:
+.B
+debburn-devel@lists.alioth.debian.org
+
+
+
+
+.SH AUTHOR
+.nf
+J\*org Schilling
+Seestr. 110
+D-13353 Berlin
+Germany
+.fi
+
+.PP
+This is application is a spinoff from the original implementation of readcd delivered in
+the cdrtools package [1] created by Joerg Schilling, who deserves the most credits
+for its success. However, he is not involved into the development
+of this spinoff and therefore he shall not be made responsible for any problem
+caused by it. Do not try to get support from the original author!
+.PP
+Additional information can be found on:
+.br
+https://alioth.debian.org/projects/debburn/
+.PP
+If you have support questions, send them to
+.PP
+.B
+debburn-devel@lists.alioth.debian.org
+.br
+.PP
+If you have definitely found a bug, send a mail to this list or to
+.PP
+.B
+submit@bugs.debian.org
+.br
+.PP
+writing at least a short description into the Subject and "Package: cdrkit"
+into the first line of the mail body.
+.SH SOURCES
+.PP
+.br
+[1] Cdrtools 2.01.01a08 from May 2006, http://cdrecord.berlios.de
+
diff --git a/readom/readom.c b/readom/readom.c
new file mode 100644
index 0000000..d52fe5d
--- /dev/null
+++ b/readom/readom.c
@@ -0,0 +1,2194 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)readcd.c 1.80 06/02/05 Copyright 1987, 1995-2006 J. Schilling */
+/*
+ * Skeleton for the use of the usal genearal SCSI - driver
+ *
+ * Copyright (c) 1987, 1995-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <standard.h>
+#include <unixstd.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <fctldefs.h>
+#include <timedefs.h>
+#include <signal.h>
+#include <schily.h>
+#ifdef HAVE_PRIV_H
+#include <priv.h>
+#endif
+
+#ifdef NEED_O_BINARY
+#include <io.h> /* for setmode() prototype */
+#endif
+
+#include <usal/usalcmd.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsi_scan.h"
+#include "scsimmc.h"
+#define qpto96 __nothing__
+#include "wodim.h"
+#include "defaults.h"
+#undef qpto96
+#include "movesect.h"
+
+char cdr_version[] = "2.01.01a05";
+
+#if defined(PROTOTYPES)
+#define UINT_C(a) (a##u)
+#define ULONG_C(a) (a##ul)
+#define USHORT_C(a) (a##uh)
+#define CONCAT(a, b) a##b
+#else
+#define UINT_C(a) ((unsigned)(a))
+#define ULONG_C(a) ((unsigned long)(a))
+#define USHORT_C(a) ((unsigned short)(a))
+/* CSTYLED */
+#define CONCAT(a, b) a/**/b
+#endif
+
+extern BOOL getlong(char *, long *, long, long);
+extern BOOL getint(char *, int *, int, int);
+
+typedef struct {
+ long start;
+ long end;
+ long sptr; /* sectors per transfer */
+ BOOL askrange;
+ char *name;
+} parm_t;
+
+typedef struct {
+ int errors;
+ int c2_errors;
+ int c2_maxerrs;
+ int c2_errsecs;
+ int c2_badsecs;
+ int secsize;
+ BOOL ismmc;
+} rparm_t;
+
+struct exargs {
+ SCSI *usalp;
+ int old_secsize;
+ int flags;
+ int exflags;
+ char oerr[3];
+} exargs;
+
+BOOL cvt_cyls(void);
+BOOL cvt_bcyls(void);
+void print_defect_list(void);
+static void usage(int ret);
+static void intr(int sig);
+static void exscsi(int excode, void *arg);
+static void excdr(int excode, void *arg);
+static int prstats(void);
+static int prstats_silent(void);
+static void dorw(SCSI *usalp, char *filename, char *sectors);
+static void doit(SCSI *usalp);
+static void read_disk(SCSI *usalp, parm_t *parmp);
+#ifdef CLONE_WRITE
+static void readcd_disk(SCSI *usalp, parm_t *parmp);
+static void read_lin(SCSI *usalp, parm_t *parmp);
+static int read_secheader(SCSI *usalp, long addr);
+static int read_ftoc(SCSI *usalp, parm_t *parmp, BOOL do_sectype);
+static void read_sectypes(SCSI *usalp, FILE *f);
+static void get_sectype(SCSI *usalp, long addr, char *st);
+#endif
+
+static void readc2_disk(SCSI *usalp, parm_t *parmp);
+static int fread_data(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+#ifdef CLONE_WRITE
+static int fread_2448(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+static int fread_2448_16(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+static int fread_2352(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+static int fread_lin(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+#endif
+static int bits(int c);
+static int bitidx(int c);
+static int fread_c2(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr,
+ int cnt);
+
+static int fdata_null(rparm_t *rp, caddr_t bp, long addr, int cnt);
+static int fdata_c2(rparm_t *rp, caddr_t bp, long addr, int cnt);
+
+#ifdef used
+static int read_scsi_g1(SCSI *usalp, caddr_t bp, long addr, int cnt);
+#endif
+
+int write_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt);
+int write_g0(SCSI *usalp, caddr_t bp, long addr, int cnt);
+int write_g1(SCSI *usalp, caddr_t bp, long addr, int cnt);
+
+#ifdef used
+static void Xrequest_sense(SCSI *usalp);
+#endif
+static int read_retry(SCSI *usalp, caddr_t bp, long addr, long cnt,
+ int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt),
+ rparm_t *rp);
+static void read_generic(SCSI *usalp, parm_t *parmp,
+ int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt),
+ rparm_t *rp,
+ int (*dfunc)(rparm_t *rp, caddr_t bp, long addr, int cnt));
+static void write_disk(SCSI *usalp, parm_t *parmp);
+static int choice(int n);
+static void ra(SCSI *usalp);
+
+int read_da(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize,
+ int subcode);
+int read_cd(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize,
+ int data, int subch);
+
+static void oldmode(SCSI *usalp, int *errp, int *retrp);
+static void domode(SCSI *usalp, int err, int retr);
+
+static void qpto96(Uchar *sub, Uchar *subq, int dop);
+static void ovtime(SCSI *usalp);
+static void add_bad(long addr);
+static void print_bad(void);
+
+struct timeval starttime;
+struct timeval stoptime;
+int didintr;
+int exsig;
+
+char *Sbuf;
+long Sbufsize;
+
+/*#define MAX_RETRY 32*/
+#define MAX_RETRY 128
+
+int help;
+int xdebug;
+int lverbose;
+int quiet;
+BOOL is_suid;
+BOOL is_cdrom;
+BOOL is_dvd;
+BOOL do_write;
+BOOL c2scan;
+BOOL fulltoc;
+BOOL clonemode;
+BOOL noerror;
+BOOL nocorr;
+BOOL notrunc;
+int retries = MAX_RETRY;
+int maxtry = 0;
+int meshpoints;
+BOOL do_factor;
+
+struct scsi_format_data fmt;
+
+/*XXX*/EXPORT BOOL cvt_cyls(void) { return (FALSE); }
+/*XXX*/EXPORT BOOL cvt_bcyls(void) { return (FALSE); }
+/*XXX*/EXPORT void print_defect_list(void) {}
+
+static void
+usage(int ret)
+{
+ fprintf(stderr, "Usage:\treadom [options]\n");
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, "\t-version print version information and exit\n");
+ fprintf(stderr, "\tdev=target SCSI target to use\n");
+ fprintf(stderr, "\tf=filename Name of file to read/write\n");
+ fprintf(stderr, "\tsectors=range Range of sectors to read/write\n");
+ fprintf(stderr, "\tspeed=# set speed of drive (MMC only)\n");
+ fprintf(stderr, "\tts=# set maximum transfer size for a single SCSI command\n");
+ fprintf(stderr, "\t-w Switch to write mode\n");
+ fprintf(stderr, "\t-c2scan Do a C2 error scan\n");
+#ifdef CLONE_WRITE
+ fprintf(stderr, "\t-fulltoc Retrieve the full TOC\n");
+ fprintf(stderr, "\t-clone Retrieve the full TOC and all data\n");
+#endif
+ fprintf(stderr, "\ttimeout=# set the default SCSI command timeout to #.\n");
+ fprintf(stderr, "\tdebug=#,-d Set to # or increment misc debug level\n");
+ fprintf(stderr, "\tkdebug=#,kd=# do Kernel debugging\n");
+ fprintf(stderr, "\t-quiet,-q be more quiet in error retry mode\n");
+ fprintf(stderr, "\t-verbose,-v increment general verbose level by one\n");
+ fprintf(stderr, "\t-Verbose,-V increment SCSI command transport verbose level by one\n");
+ fprintf(stderr, "\t-silent,-s do not print status of failed SCSI commands\n");
+ fprintf(stderr, "\t-scanbus scan the SCSI bus and exit\n");
+ fprintf(stderr, "\t-noerror do not abort on error\n");
+#ifdef CLONE_WRITE
+ fprintf(stderr, "\t-nocorr do not apply error correction in drive\n");
+#endif
+ fprintf(stderr, "\t-notrunc do not truncate outputfile in read mode\n");
+ fprintf(stderr, "\tretries=# set retry count (default is %d)\n", retries);
+ fprintf(stderr, "\t-overhead meter SCSI command overhead times\n");
+ fprintf(stderr, "\tmeshpoints=# print read-speed at # locations\n");
+ fprintf(stderr, "\t-factor try to use speed factor with meshpoints=# if possible\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "sectors=0-0 will read nothing, sectors=0-1 will read one sector starting from 0\n");
+ exit(ret);
+}
+
+/* CSTYLED */
+char opts[] = "debug#,d+,kdebug#,kd#,timeout#,quiet,q,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,help,h,version,scanbus,dev*,sectors*,w,c2scan,fulltoc,clone,noerror,nocorr,notrunc,retries#,factor,f*,speed#,ts&,overhead,meshpoints#";
+
+int
+main(int argc, char *argv[])
+{
+ char *dev = NULL;
+ int fcount;
+ int cac;
+ char * const *cav;
+ int scsibus = -1;
+ int target = -1;
+ int lun = -1;
+ int silent = 0;
+ int verbose = 0;
+ int kdebug = 0;
+ int debug = 0;
+ int deftimeout = 40;
+ int pversion = 0;
+ int scanbus = 0;
+ int speed = -1;
+ int dooverhead = 0;
+ SCSI *usalp;
+ char *filename = NULL;
+ char *sectors = NULL;
+
+ save_args(argc, argv);
+
+ cac = --argc;
+ cav = ++argv;
+
+ if (getallargs(&cac, &cav, opts,
+ &debug, &debug,
+ &kdebug, &kdebug,
+ &deftimeout,
+ &quiet, &quiet,
+ &lverbose, &lverbose,
+ &verbose, &verbose,
+ &xdebug, &xdebug,
+ &silent, &silent,
+ &help, &help, &pversion,
+ &scanbus, &dev, &sectors, &do_write,
+ &c2scan,
+ &fulltoc, &clonemode,
+ &noerror, &nocorr,
+ &notrunc, &retries, &do_factor, &filename,
+ &speed, getnum, &Sbufsize,
+ &dooverhead, &meshpoints) < 0) {
+ errmsgno(EX_BAD, "Bad flag: %s.\n", cav[0]);
+ usage(EX_BAD);
+ }
+ if (help)
+ usage(0);
+ if (pversion) {
+ printf("readcd %s is not what you see here. This line is only a fake for too clever\n"
+ "GUIs and other frontend applications. In fact, this program is:\n", cdr_version);
+
+ printf("readom " CDRKIT_VERSION " (" HOST_SYSTEM ")\n"
+ "Copyright (C) 1987, 1995-2006 Joerg Schilling\n"
+ "Copyright (C) 2006 Cdrkit maintainers\n"
+ "(modified version of <censored> -- "
+ "don't bother Joerg Schilling with problems)\n");
+ exit(0);
+ }
+
+ fcount = 0;
+ cac = argc;
+ cav = argv;
+
+ while (getfiles(&cac, &cav, opts) > 0) {
+ fcount++;
+ if (fcount == 1) {
+ if (*astoi(cav[0], &target) != '\0') {
+ errmsgno(EX_BAD,
+ "Target '%s' is not a Number.\n",
+ cav[0]);
+ usage(EX_BAD);
+ /* NOTREACHED */
+ }
+ }
+ if (fcount == 2) {
+ if (*astoi(cav[0], &lun) != '\0') {
+ errmsgno(EX_BAD,
+ "Lun is '%s' not a Number.\n",
+ cav[0]);
+ usage(EX_BAD);
+ /* NOTREACHED */
+ }
+ }
+ if (fcount == 3) {
+ if (*astoi(cav[0], &scsibus) != '\0') {
+ errmsgno(EX_BAD,
+ "Scsibus is '%s' not a Number.\n",
+ cav[0]);
+ usage(EX_BAD);
+ /* NOTREACHED */
+ }
+ }
+ cac--;
+ cav++;
+ }
+/*fprintf(stderr, "dev: '%s'\n", dev);*/
+ if (!scanbus)
+ cdr_defaults(&dev, NULL, NULL, NULL);
+ if (debug) {
+ printf("dev: '%s'\n", dev);
+ }
+ if (!scanbus && dev == NULL &&
+ scsibus == -1 && (target == -1 || lun == -1)) {
+ errmsgno(EX_BAD, "No SCSI device specified.\n");
+ usage(EX_BAD);
+ }
+ if (dev || scanbus) {
+ char errstr[80];
+
+ /*
+ * Call usal_remote() to force loading the remote SCSI transport
+ * library code that is located in librusal instead of the dummy
+ * remote routines that are located inside libusal.
+ */
+ usal_remote();
+ if (dev != NULL &&
+ ((strncmp(dev, "HELP", 4) == 0) ||
+ (strncmp(dev, "help", 4) == 0))) {
+ usal_help(stderr);
+ exit(0);
+ }
+ if ((usalp = usal_open(dev, errstr, sizeof (errstr), debug, lverbose)) == (SCSI *)0) {
+ int err = geterrno();
+
+ errmsgno(err, "%s%sCannot open SCSI driver.\n", errstr, errstr[0]?". ":"");
+ errmsgno(EX_BAD, "For possible targets try 'wodim -scanbus'.%s\n",
+ geteuid() ? " Make sure you are root.":"");
+ errmsgno(EX_BAD, "For possible transport specifiers try 'wodim dev=help'.\n");
+ exit(err);
+ }
+ } else {
+ if (scsibus == -1 && target >= 0 && lun >= 0)
+ scsibus = 0;
+
+ usalp = usal_smalloc();
+ usalp->debug = debug;
+ usalp->kdebug = kdebug;
+
+ usal_settarget(usalp, scsibus, target, lun);
+ if (usal__open(usalp, NULL) <= 0)
+ comerr("Cannot open SCSI driver.\n");
+ }
+ usalp->silent = silent;
+ usalp->verbose = verbose;
+ usalp->debug = debug;
+ usalp->kdebug = kdebug;
+ usal_settimeout(usalp, deftimeout);
+
+ if (Sbufsize == 0)
+ Sbufsize = 256*1024L;
+ Sbufsize = usal_bufsize(usalp, Sbufsize);
+ if ((Sbuf = usal_getbuf(usalp, Sbufsize)) == NULL)
+ comerr("Cannot get SCSI I/O buffer.\n");
+
+#ifdef HAVE_PRIV_SET
+ is_suid = priv_ineffect(PRIV_FILE_DAC_READ) &&
+ !priv_ineffect(PRIV_PROC_SETID);
+ /*
+ * Give up privs we do not need anymore.
+ * We no longer need:
+ * file_dac_read,net_privaddr
+ * We still need:
+ * sys_devices
+ */
+ priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+ PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, NULL);
+ priv_set(PRIV_OFF, PRIV_PERMITTED,
+ PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, NULL);
+ priv_set(PRIV_OFF, PRIV_INHERITABLE,
+ PRIV_FILE_DAC_READ, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
+#endif
+ /*
+ * This is only for OS that do not support fine grained privs.
+ */
+ if (!is_suid)
+ is_suid = geteuid() != getuid();
+ /*
+ * We don't need root privilleges anymore.
+ */
+#ifdef HAVE_SETREUID
+ if (setreuid(-1, getuid()) < 0)
+#else
+#ifdef HAVE_SETEUID
+ if (seteuid(getuid()) < 0)
+#else
+ if (setuid(getuid()) < 0)
+#endif
+#endif
+ comerr("Panic cannot set back effective uid.\n");
+
+ /* code to use SCG */
+
+ if (scanbus) {
+ select_target(usalp, stdout);
+ exit(0);
+ }
+ do_inquiry(usalp, FALSE);
+ allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */
+ if (is_mmc(usalp, NULL, NULL)) {
+ int rspeed;
+ int wspeed;
+ /*
+ * At this point we know that we have a SCSI-3/mmc compliant drive.
+ * Unfortunately ATAPI drives violate the SCSI spec in returning
+ * a response data format of '1' which from the SCSI spec would
+ * tell us not to use the "PF" bit in mode select. As ATAPI drives
+ * require the "PF" bit to be set, we 'correct' the inquiry data.
+ */
+ if (usalp->inq->data_format < 2)
+ usalp->inq->data_format = 2;
+
+ if ((rspeed = get_curprofile(usalp)) >= 0) {
+ if (rspeed >= 0x08 && rspeed < 0x10)
+ is_cdrom = TRUE;
+ if (rspeed >= 0x10 && rspeed < 0x20)
+ is_dvd = TRUE;
+ } else {
+ BOOL dvd;
+
+ mmc_check(usalp, NULL, NULL, NULL, NULL, &dvd, NULL);
+ if (dvd == FALSE) {
+ is_cdrom = TRUE;
+ } else {
+ char xb[32];
+
+ if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) {
+ /*
+ * If read DVD structure is supported and works, then
+ * we must have a DVD media in the drive. Signal to
+ * use the DVD driver.
+ */
+ is_dvd = TRUE;
+ } else {
+ is_cdrom = TRUE;
+ }
+ }
+ }
+
+ if (speed > 0)
+ speed *= 177;
+ if (speed > 0xFFFF || speed < 0)
+ speed = 0xFFFF;
+ scsi_set_speed(usalp, speed, speed, ROTCTL_CLV);
+ if (scsi_get_speed(usalp, &rspeed, &wspeed) >= 0) {
+ fprintf(stderr, "Read speed: %5d kB/s (CD %3dx, DVD %2dx).\n",
+ rspeed, rspeed/176, rspeed/1385);
+ fprintf(stderr, "Write speed: %5d kB/s (CD %3dx, DVD %2dx).\n",
+ wspeed, wspeed/176, wspeed/1385);
+ }
+ }
+ exargs.usalp = usalp;
+ exargs.old_secsize = -1;
+/* exargs.flags = flags;*/
+ exargs.oerr[2] = 0;
+
+ /*
+ * Install exit handler before we change the drive status.
+ */
+ on_comerr(exscsi, &exargs);
+ signal(SIGINT, intr);
+ signal(SIGTERM, intr);
+
+ if (dooverhead) {
+ ovtime(usalp);
+ comexit(0);
+ }
+
+ if (is_suid) {
+ if (usalp->inq->type != INQ_ROMD)
+ comerrno(EX_BAD, "Not root. Will only work on CD-ROM in suid/priv mode\n");
+ }
+
+ if (filename || sectors || c2scan || meshpoints || fulltoc || clonemode) {
+ dorw(usalp, filename, sectors);
+ } else {
+ doit(usalp);
+ }
+ comexit(0);
+ return (0);
+}
+
+/*
+ * XXX Leider kann man vim Signalhandler keine SCSI Kommandos verschicken
+ * XXX da meistens das letzte SCSI Kommando noch laeuft.
+ * XXX Eine Loesung waere ein Abort Callback in SCSI *.
+ */
+static void
+intr(int sig)
+{
+ didintr++;
+ exsig = sig;
+/* comexit(sig);*/
+}
+
+/* ARGSUSED */
+static void
+exscsi(int excode, void *arg)
+{
+ struct exargs *exp = (struct exargs *)arg;
+ int i;
+
+ /*
+ * Try to restore the old sector size.
+ */
+ if (exp != NULL && exp->exflags == 0) {
+ for (i = 0; i < 10*100; i++) {
+ if (!exp->usalp->running)
+ break;
+ if (i == 10) {
+ errmsgno(EX_BAD,
+ "Waiting for current SCSI command to finish.\n");
+ }
+ usleep(100000);
+ }
+
+ if (!exp->usalp->running) {
+ if (exp->oerr[2] != 0) {
+ domode(exp->usalp, exp->oerr[0], exp->oerr[1]);
+ }
+ if (exp->old_secsize > 0 && exp->old_secsize != 2048)
+ select_secsize(exp->usalp, exp->old_secsize);
+ }
+ exp->exflags++; /* Make sure that it only get called once */
+ }
+}
+
+static void
+excdr(int excode, void *arg)
+{
+ exscsi(excode, arg);
+
+#ifdef needed
+ /* Do several other restores/statistics here (see cdrecord.c) */
+#endif
+}
+
+/*
+ * Return milliseconds since start time.
+ */
+static int
+prstats(void)
+{
+ int sec;
+ int usec;
+ int tmsec;
+
+ if (gettimeofday(&stoptime, (struct timezone *)0) < 0)
+ comerr("Cannot get time\n");
+
+ sec = stoptime.tv_sec - starttime.tv_sec;
+ usec = stoptime.tv_usec - starttime.tv_usec;
+ tmsec = sec*1000 + usec/1000;
+#ifdef lint
+ tmsec = tmsec; /* Bisz spaeter */
+#endif
+ if (usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ fprintf(stderr, "Time total: %d.%03dsec\n", sec, usec/1000);
+ return (1000*sec + (usec / 1000));
+}
+
+/*
+ * Return milliseconds since start time, but be silent this time.
+ */
+static int
+prstats_silent(void)
+{
+ int sec;
+ int usec;
+ int tmsec;
+
+ if (gettimeofday(&stoptime, (struct timezone *)0) < 0)
+ comerr("Cannot get time\n");
+
+ sec = stoptime.tv_sec - starttime.tv_sec;
+ usec = stoptime.tv_usec - starttime.tv_usec;
+ tmsec = sec*1000 + usec/1000;
+#ifdef lint
+ tmsec = tmsec; /* Bisz spaeter */
+#endif
+ if (usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ return (1000*sec + (usec / 1000));
+}
+
+static void
+dorw(SCSI *usalp, char *filename, char *sectors)
+{
+ parm_t params;
+ char *p = NULL;
+
+ params.start = 0;
+ params.end = -1;
+ params.sptr = -1;
+ params.askrange = FALSE;
+ params.name = NULL;
+
+ if (filename)
+ params.name = filename;
+ if (meshpoints > 0) {
+ if (params.name == NULL)
+ params.name = "/dev/null";
+ }
+ if (sectors)
+ p = astol(sectors, &params.start);
+ if (p && *p == '-')
+ p = astol(++p, &params.end);
+ if (p && *p != '\0')
+ comerrno(EX_BAD, "Not a valid sector range '%s'\n", sectors);
+
+ if (!wait_unit_ready(usalp, 60))
+ comerrno(EX_BAD, "Device not ready.\n");
+
+#ifdef CLONE_WRITE
+ if (fulltoc) {
+ if (params.name == NULL)
+ params.name = "/dev/null";
+ read_ftoc(usalp, &params, FALSE);
+ } else if (clonemode) {
+ if (!is_mmc(usalp, NULL, NULL))
+ comerrno(EX_BAD, "Unsupported device for clone mode.\n");
+ noerror = TRUE;
+ if (retries == MAX_RETRY)
+ retries = 10;
+ if (params.name == NULL)
+ params.name = "/dev/null";
+
+ if (read_ftoc(usalp, &params, TRUE) < 0)
+ comerrno(EX_BAD, "Read fulltoc problems.\n");
+ readcd_disk(usalp, &params);
+ } else
+#endif
+ if (c2scan) {
+ noerror = TRUE;
+ if (retries == MAX_RETRY)
+ retries = 10;
+ if (params.name == NULL)
+ params.name = "/dev/null";
+ readc2_disk(usalp, &params);
+ } else if (do_write)
+ write_disk(usalp, &params);
+ else
+ read_disk(usalp, &params);
+}
+
+static void
+doit(SCSI *usalp)
+{
+ int i = 0;
+ parm_t params;
+
+ params.start = 0;
+ params.end = -1;
+ params.sptr = -1;
+ params.askrange = TRUE;
+ params.name = "/dev/null";
+
+ for (;;) {
+ if (!wait_unit_ready(usalp, 60))
+ comerrno(EX_BAD, "Device not ready.\n");
+
+ printf("0:read 1:veri 2:erase 3:read buffer 4:cache 5:ovtime 6:cap\n");
+ printf("7:wne 8:floppy 9:verify 10:checkcmds 11:read disk 12:write disk\n");
+ printf("13:scsireset 14:seektest 15: readda 16: reada 17: c2err\n");
+#ifdef CLONE_WRITE
+ printf("18:readom 19: lin 20: full toc\n");
+#endif
+
+ getint("Enter selection:", &i, 0, 20);
+ if (didintr)
+ return;
+
+ switch (i) {
+
+ case 5: ovtime(usalp); break;
+ case 11: read_disk(usalp, 0); break;
+ case 12: write_disk(usalp, 0); break;
+ case 15: ra(usalp); break;
+/* case 16: reada_disk(usalp, 0, 0); break;*/
+ case 17: readc2_disk(usalp, &params); break;
+#ifdef CLONE_WRITE
+ case 18: readcd_disk(usalp, 0); break;
+ case 19: read_lin(usalp, 0); break;
+ case 20: read_ftoc(usalp, 0, FALSE); break;
+#endif
+ }
+ }
+}
+
+static void
+read_disk(SCSI *usalp, parm_t *parmp)
+{
+ rparm_t rp;
+
+ read_capacity(usalp);
+ print_capacity(usalp, stderr);
+
+ rp.errors = 0;
+ rp.c2_errors = 0;
+ rp.c2_maxerrs = 0;
+ rp.c2_errsecs = 0;
+ rp.c2_badsecs = 0;
+ rp.secsize = usalp->cap->c_bsize;
+
+ read_generic(usalp, parmp, fread_data, &rp, fdata_null);
+}
+
+#ifdef CLONE_WRITE
+static void
+readcd_disk(SCSI *usalp, parm_t *parmp)
+{
+ rparm_t rp;
+ int osecsize = 2048;
+ int oerr = 0;
+ int oretr = 10;
+ int (*funcp)(SCSI *_usalp, rparm_t *_rp, caddr_t bp, long addr, int cnt);
+
+ usalp->silent++;
+ if (read_capacity(usalp) >= 0)
+ osecsize = usalp->cap->c_bsize;
+ usalp->silent--;
+ if (osecsize != 2048)
+ select_secsize(usalp, 2048);
+
+ read_capacity(usalp);
+ print_capacity(usalp, stderr);
+
+ rp.errors = 0;
+ rp.c2_errors = 0;
+ rp.c2_maxerrs = 0;
+ rp.c2_errsecs = 0;
+ rp.c2_badsecs = 0;
+ rp.secsize = 2448;
+ rp.ismmc = is_mmc(usalp, NULL, NULL);
+ funcp = fread_2448;
+
+ wait_unit_ready(usalp, 10);
+ if (fread_2448(usalp, &rp, Sbuf, 0, 0) < 0) {
+ errmsgno(EX_BAD, "read 2448 failed\n");
+ if (rp.ismmc &&
+ fread_2448_16(usalp, &rp, Sbuf, 0, 0) >= 0) {
+ errmsgno(EX_BAD, "read 2448_16 : OK\n");
+
+ funcp = fread_2448_16;
+ }
+ }
+
+ oldmode(usalp, &oerr, &oretr);
+ exargs.oerr[0] = oerr;
+ exargs.oerr[1] = oretr;
+ exargs.oerr[2] = 0xFF;
+ if (parmp == NULL) /* XXX Nur am Anfang!!! */
+ domode(usalp, -1, -1);
+ else
+ domode(usalp, nocorr?0x21:0x20, 10);
+
+ read_generic(usalp, parmp, funcp, &rp, fdata_null);
+ if (osecsize != 2048)
+ select_secsize(usalp, osecsize);
+ domode(usalp, oerr, oretr);
+}
+
+/* ARGSUSED */
+static void
+read_lin(SCSI *usalp, parm_t *parmp)
+{
+ parm_t parm;
+ rparm_t rp;
+
+ read_capacity(usalp);
+ print_capacity(usalp, stderr);
+
+ parm.start = ULONG_C(0xF0000000);
+ parm.end = ULONG_C(0xFF000000);
+ parm.name = "DDD";
+
+ rp.errors = 0;
+ rp.c2_errors = 0;
+ rp.c2_maxerrs = 0;
+ rp.c2_errsecs = 0;
+ rp.c2_badsecs = 0;
+ rp.secsize = 2448;
+ rp.ismmc = is_mmc(usalp, NULL, NULL);
+ domode(usalp, -1, -1);
+ read_generic(usalp, &parm, fread_lin, &rp, fdata_null);
+}
+
+static int
+read_secheader(SCSI *usalp, long addr)
+{
+ rparm_t rp;
+ int osecsize = 2048;
+ int ret = 0;
+
+ usalp->silent++;
+ if (read_capacity(usalp) >= 0)
+ osecsize = usalp->cap->c_bsize;
+ usalp->silent--;
+ if (osecsize != 2048)
+ select_secsize(usalp, 2048);
+
+ read_capacity(usalp);
+
+ rp.errors = 0;
+ rp.c2_errors = 0;
+ rp.c2_maxerrs = 0;
+ rp.c2_errsecs = 0;
+ rp.c2_badsecs = 0;
+ rp.secsize = 2352;
+ rp.ismmc = is_mmc(usalp, NULL, NULL);
+
+ wait_unit_ready(usalp, 10);
+
+ fillbytes(Sbuf, 2352, '\0');
+ if (fread_2352(usalp, &rp, Sbuf, addr, 1) < 0) {
+ ret = -1;
+ }
+ if (osecsize != 2048)
+ select_secsize(usalp, osecsize);
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+read_ftoc(SCSI *usalp, parm_t *parmp, BOOL do_sectype)
+{
+ FILE *f;
+ int i;
+ char filename[1024];
+ struct tocheader *tp;
+ char *p;
+ char xb[256];
+ int len;
+ char xxb[10000];
+
+
+ strcpy(filename, "toc.dat");
+ if (strcmp(parmp->name, "/dev/null") != 0) {
+
+ len = strlen(parmp->name);
+ if (len > (sizeof (filename)-5)) {
+ len = sizeof (filename)-5;
+ }
+ snprintf(filename, sizeof (filename), "%.*s.toc", len, parmp->name);
+ }
+
+ tp = (struct tocheader *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_FULLTOC) < 0) {
+ if (usalp->silent == 0 || usalp->verbose > 0)
+ errmsgno(EX_BAD, "Cannot read TOC header\n");
+ return (-1);
+ }
+ len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2;
+ fprintf(stderr, "TOC len: %d. First Session: %d Last Session: %d.\n", len, tp->first, tp->last);
+
+ if (read_toc(usalp, xxb, 0, len, 0, FMT_FULLTOC) < 0) {
+ if (len & 1) {
+ /*
+ * Work around a bug in some operating systems that do not
+ * handle odd byte DMA correctly for ATAPI drives.
+ */
+ wait_unit_ready(usalp, 30);
+ read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_FULLTOC);
+ wait_unit_ready(usalp, 30);
+ if (read_toc(usalp, xxb, 0, len+1, 0, FMT_FULLTOC) >= 0) {
+ goto itworked;
+ }
+ }
+ if (usalp->silent == 0)
+ errmsgno(EX_BAD, "Cannot read full TOC\n");
+ return (-1);
+ }
+
+itworked:
+ f = fileopen(filename, "wctb");
+
+ if (f == NULL)
+ comerr("Cannot open '%s'.\n", filename);
+ filewrite(f, xxb, len);
+ if (do_sectype)
+ read_sectypes(usalp, f);
+ fflush(f);
+ fclose(f);
+
+ p = &xxb[4];
+ for (; p < &xxb[len]; p += 11) {
+ for (i = 0; i < 11; i++)
+ fprintf(stderr, "%02X ", p[i] & 0xFF);
+ fprintf(stderr, "\n");
+ }
+ /*
+ * List all lead out start times to give information about multi
+ * session disks.
+ */
+ p = &xxb[4];
+ for (; p < &xxb[len]; p += 11) {
+ if ((p[3] & 0xFF) == 0xA2) {
+ fprintf(stderr, "Lead out %d: %ld\n", p[0], msf_to_lba(p[8], p[9], p[10], TRUE));
+ }
+ }
+ return (0);
+}
+
+static void
+read_sectypes(SCSI *usalp, FILE *f)
+{
+ char sect;
+
+ sect = SECT_AUDIO;
+ get_sectype(usalp, 4, &sect);
+ if (f != NULL)
+ filewrite(f, &sect, 1);
+ if (xdebug)
+ usal_prbytes("sec 0", (Uchar *)Sbuf, 16);
+
+ sect = SECT_AUDIO;
+ get_sectype(usalp, usalp->cap->c_baddr-4, &sect);
+ if (f != NULL)
+ filewrite(f, &sect, 1);
+ if (xdebug) {
+ usal_prbytes("sec E", (Uchar *)Sbuf, 16);
+ fprintf(stderr, "baddr: %ld\n", (long)usalp->cap->c_baddr);
+ }
+}
+
+static void
+get_sectype(SCSI *usalp, long addr, char *st)
+{
+ char *synchdr = "\0\377\377\377\377\377\377\377\377\377\377\0";
+ int sectype = SECT_AUDIO;
+ int i;
+ long raddr = addr;
+#define _MAX_TRY_ 20
+
+ usalp->silent++;
+ for (i = 0; i < _MAX_TRY_ && read_secheader(usalp, raddr) < 0; i++) {
+ if (addr == 0)
+ raddr++;
+ else
+ raddr--;
+ }
+ usalp->silent--;
+ if (i >= _MAX_TRY_) {
+ fprintf(stderr, "Sectype (%ld) is CANNOT\n", addr);
+ return;
+ } else if (i > 0) {
+ fprintf(stderr, "Sectype (%ld) needed %d retries\n", addr, i);
+ }
+#undef _MAX_TRY_
+
+ if (cmpbytes(Sbuf, synchdr, 12) < 12) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is AUDIO\n", addr);
+ if (st)
+ *st = SECT_AUDIO;
+ return;
+ }
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is DATA\n", addr);
+ if (Sbuf[15] == 0) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 0\n", addr);
+ sectype = SECT_MODE_0;
+
+ } else if (Sbuf[15] == 1) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 1\n", addr);
+ sectype = SECT_ROM;
+
+ } else if (Sbuf[15] == 2) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 2\n", addr);
+
+ if ((Sbuf[16+2] & 0x20) == 0 &&
+ (Sbuf[16+4+2] & 0x20) == 0) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 2 form 1\n", addr);
+ sectype = SECT_MODE_2_F1;
+
+ } else if ((Sbuf[16+2] & 0x20) != 0 &&
+ (Sbuf[16+4+2] & 0x20) != 0) {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 2 form 2\n", addr);
+ sectype = SECT_MODE_2_F2;
+ } else {
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is MODE 2 formless\n", addr);
+ sectype = SECT_MODE_2;
+ }
+ } else {
+ fprintf(stderr, "Sectype (%ld) is UNKNOWN\n", addr);
+ }
+ if (st)
+ *st = sectype;
+ if (xdebug)
+ fprintf(stderr, "Sectype (%ld) is 0x%02X\n", addr, sectype);
+}
+
+#endif /* CLONE_WRITE */
+
+char zeroblk[512];
+
+static void
+readc2_disk(SCSI *usalp, parm_t *parmp)
+{
+ rparm_t rp;
+ int osecsize = 2048;
+ int oerr = 0;
+ int oretr = 10;
+
+ usalp->silent++;
+ if (read_capacity(usalp) >= 0)
+ osecsize = usalp->cap->c_bsize;
+ usalp->silent--;
+ if (osecsize != 2048)
+ select_secsize(usalp, 2048);
+
+ read_capacity(usalp);
+ print_capacity(usalp, stderr);
+
+ rp.errors = 0;
+ rp.c2_errors = 0;
+ rp.c2_maxerrs = 0;
+ rp.c2_errsecs = 0;
+ rp.c2_badsecs = 0;
+ rp.secsize = 2352 + 294;
+ rp.ismmc = is_mmc(usalp, NULL, NULL);
+
+ oldmode(usalp, &oerr, &oretr);
+ exargs.oerr[0] = oerr;
+ exargs.oerr[1] = oretr;
+ exargs.oerr[2] = 0xFF;
+ domode(usalp, 0x21, 10);
+
+
+ read_generic(usalp, parmp, fread_c2, &rp, fdata_c2);
+ if (osecsize != 2048)
+ select_secsize(usalp, osecsize);
+ domode(usalp, oerr, oretr);
+
+ printf("Total of %d hard read errors.\n", rp.errors);
+ printf("C2 errors total: %d bytes in %d sectors on disk\n", rp.c2_errors, rp.c2_errsecs);
+ printf("C2 errors rate: %f%% \n", (100.0*rp.c2_errors)/usalp->cap->c_baddr/2352);
+ printf("C2 errors on worst sector: %d, sectors with 100+ C2 errors: %d\n", rp.c2_maxerrs, rp.c2_badsecs);
+}
+
+/* ARGSUSED */
+static int
+fread_data(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ return (read_g1(usalp, bp, addr, cnt));
+}
+
+#ifdef CLONE_WRITE
+static int
+fread_2448(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ if (rp->ismmc) {
+ return (read_cd(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC */
+ (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3),
+ /* plus all subchannels RAW */
+ 1));
+ } else {
+ return (read_da(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC + all subch */
+ 0x02));
+ }
+}
+
+static int
+fread_2448_16(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+
+ if (rp->ismmc) {
+ track_t trackdesc;
+ int ret;
+ int i;
+ char *p;
+
+ trackdesc.isecsize = 2368;
+ trackdesc.secsize = 2448;
+ ret = read_cd(usalp, bp, addr, cnt, 2368,
+ /* Sync + all headers + user data + EDC/ECC */
+ (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3),
+ /* subchannels P/Q */
+ 2);
+ if (ret < 0)
+ return (ret);
+
+ scatter_secs(&trackdesc, bp, cnt);
+ for (i = 0, p = bp+2352; i < cnt; i++) {
+#ifdef more_than_q_sub
+ if ((p[15] & 0x80) != 0)
+ printf("P");
+#endif
+ /*
+ * As the drives don't return P-sub, we check
+ * whether the index equals 0.
+ */
+ qpto96((Uchar *)p, (Uchar *)p, p[2] == 0);
+ p += 2448;
+ }
+ return (ret);
+ } else {
+ comerrno(EX_BAD, "Cannot fread_2448_16 on non MMC drives\n");
+
+ return (read_da(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC + all subch */
+ 0x02));
+ }
+}
+
+static int
+fread_2352(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ if (rp->ismmc) {
+ return (read_cd(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC */
+ (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3),
+ /* NO subchannels */
+ 0));
+ } else {
+ comerrno(EX_BAD, "Cannot fread_2352 on non MMC drives\n");
+
+ return (read_da(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC + all subch */
+ 0x02));
+ }
+}
+
+static int
+fread_lin(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ if (addr != ULONG_C(0xF0000000))
+ addr = ULONG_C(0xFFFFFFFF);
+
+ return (read_cd(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC */
+ (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3),
+ /* plus all subchannels RAW */
+ 1));
+}
+#endif /* CLONE_WRITE */
+
+static int
+bits(int c)
+{
+ int n = 0;
+
+ if (c & 0x01)
+ n++;
+ if (c & 0x02)
+ n++;
+ if (c & 0x04)
+ n++;
+ if (c & 0x08)
+ n++;
+ if (c & 0x10)
+ n++;
+ if (c & 0x20)
+ n++;
+ if (c & 0x40)
+ n++;
+ if (c & 0x80)
+ n++;
+ return (n);
+}
+
+static int
+bitidx(int c)
+{
+ if (c & 0x80)
+ return (0);
+ if (c & 0x40)
+ return (1);
+ if (c & 0x20)
+ return (2);
+ if (c & 0x10)
+ return (3);
+ if (c & 0x08)
+ return (4);
+ if (c & 0x04)
+ return (5);
+ if (c & 0x02)
+ return (6);
+ if (c & 0x01)
+ return (7);
+ return (-1);
+}
+
+static int
+fread_c2(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ if (rp->ismmc) {
+ return (read_cd(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC + C2 */
+/* (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3 | 2 << 1),*/
+ (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3 | 1 << 1),
+ /* without subchannels */
+ 0));
+ } else {
+ return (read_da(usalp, bp, addr, cnt, rp->secsize,
+ /* Sync + all headers + user data + EDC/ECC + C2 */
+ 0x04));
+ }
+}
+
+/* ARGSUSED */
+static int
+fdata_null(rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ return (0);
+}
+
+static int
+fdata_c2(rparm_t *rp, caddr_t bp, long addr, int cnt)
+{
+ int i;
+ int j;
+ int k;
+ char *p;
+
+ p = &bp[2352];
+
+ for (i = 0; i < cnt; i++, p += (2352+294)) {
+/* usal_prbytes("XXX ", p, 294);*/
+ if ((j = cmpbytes(p, zeroblk, 294)) < 294) {
+ printf("C2 in sector: %3ld first at byte: %4d (0x%02X)", addr+i,
+ j*8 + bitidx(p[j]), p[j]&0xFF);
+ for (j = 0, k = 0; j < 294; j++)
+ k += bits(p[j]);
+ printf(" total: %4d errors\n", k);
+/* usal_prbytes("XXX ", p, 294);*/
+ rp->c2_errors += k;
+ if (k > rp->c2_maxerrs)
+ rp->c2_maxerrs = k;
+ rp->c2_errsecs++;
+ if (k >= 100)
+ rp->c2_badsecs += 1;
+ }
+ }
+ return (0);
+}
+
+#ifdef used
+static int
+read_scsi_g1(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+/* scmd->size = cnt*512;*/
+ scmd->size = cnt*usalp->cap->c_bsize;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x28;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read extended";
+
+ return (usal_cmd(usalp));
+}
+#endif
+
+#define G0_MAXADDR 0x1FFFFFL
+
+int
+write_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ if (addr <= G0_MAXADDR)
+ return (write_g0(usalp, bp, addr, cnt));
+ else
+ return (write_g1(usalp, bp, addr, cnt));
+}
+
+int
+write_g0(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (usalp->cap->c_bsize <= 0)
+ raisecond("capacity_not_set", 0L);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*usalp->cap->c_bsize;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_WRITE;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, addr);
+ scmd->cdb.g0_cdb.count = (Uchar)cnt;
+
+ usalp->cmdname = "write_g0";
+
+ return (usal_cmd(usalp));
+}
+
+int
+write_g1(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (usalp->cap->c_bsize <= 0)
+ raisecond("capacity_not_set", 0L);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*usalp->cap->c_bsize;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = SC_EWRITE;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "write_g1";
+
+ return (usal_cmd(usalp));
+}
+
+#ifdef used
+static void
+Xrequest_sense(SCSI *usalp)
+{
+ char sense_buf[32];
+ struct usal_cmd ocmd;
+ int sense_count;
+ char *cmdsave;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ cmdsave = usalp->cmdname;
+
+ movebytes(scmd, &ocmd, sizeof (*scmd));
+
+ fillbytes((caddr_t)sense_buf, sizeof (sense_buf), '\0');
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)sense_buf;
+ scmd->size = sizeof (sense_buf);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x3;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = sizeof (sense_buf);
+
+ usalp->cmdname = "request sense";
+
+ usal_cmd(usalp);
+
+ sense_count = sizeof (sense_buf) - usal_getresid(usalp);
+ movebytes(&ocmd, scmd, sizeof (*scmd));
+ scmd->sense_count = sense_count;
+ movebytes(sense_buf, (Uchar *)&scmd->sense, scmd->sense_count);
+
+ usalp->cmdname = cmdsave;
+ usal_printerr(usalp);
+ usal_printresult(usalp); /* XXX restore key/code in future */
+}
+#endif
+
+static int
+read_retry(SCSI *usalp, caddr_t bp, long addr, long cnt,
+ int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt),
+ rparm_t *rp)
+{
+/* int secsize = usalp->cap->c_bsize;*/
+ int secsize = rp->secsize;
+ int try = 0;
+ int err;
+ char dummybuf[8192];
+
+ if (secsize > sizeof (dummybuf)) {
+ errmsgno(EX_BAD, "Cannot retry, sector size %d too big.\n", secsize);
+ return (-1);
+ }
+
+ errmsgno(EX_BAD, "Retrying from sector %ld.\n", addr);
+ while (cnt > 0) {
+ fprintf(stderr, ".");
+
+ do {
+ if (didintr)
+ comexit(exsig); /* XXX besseres Konzept?!*/
+ wait_unit_ready(usalp, 120);
+ if (try >= 10) { /* First 10 retries without seek */
+ if ((try % 8) == 0) {
+ fprintf(stderr, "+"); /* Read last sector */
+ usalp->silent++;
+ (*rfunc)(usalp, rp, dummybuf, usalp->cap->c_baddr, 1);
+ usalp->silent--;
+ } else if ((try % 4) == 0) {
+ fprintf(stderr, "-"); /* Read first sector */
+ usalp->silent++;
+ (*rfunc)(usalp, rp, dummybuf, 0, 1);
+ usalp->silent--;
+ } else {
+ fprintf(stderr, "~"); /* Read random sector */
+ usalp->silent++;
+ (*rfunc)(usalp, rp, dummybuf, choice(usalp->cap->c_baddr), 1);
+ usalp->silent--;
+ }
+ if (didintr)
+ comexit(exsig); /* XXX besseres Konzept?!*/
+ wait_unit_ready(usalp, 120);
+ }
+ if (didintr)
+ comexit(exsig); /* XXX besseres Konzept?!*/
+
+ fillbytes(bp, secsize, 0);
+
+ usalp->silent++;
+ err = (*rfunc)(usalp, rp, bp, addr, 1);
+ usalp->silent--;
+
+ if (err < 0) {
+ err = usalp->scmd->ux_errno;
+/* fprintf(stderr, "\n");*/
+/* errmsgno(err, "Cannot read source disk\n");*/
+ } else {
+ if (usal_getresid(usalp)) {
+ fprintf(stderr, "\nresid: %d\n", usal_getresid(usalp));
+ return (-1);
+ }
+ break;
+ }
+ } while (++try < retries);
+
+ if (try >= retries) {
+ fprintf(stderr, "\n");
+ errmsgno(err, "Error on sector %ld not corrected. Total of %d errors.\n",
+ addr, ++rp->errors);
+
+ if (usalp->silent <= 1 && lverbose > 0)
+ usal_printerr(usalp);
+
+ add_bad(addr);
+
+ if (!noerror)
+ return (-1);
+ errmsgno(EX_BAD, "-noerror set, continuing ...\n");
+ } else {
+ if (try >= maxtry)
+ maxtry = try;
+
+ if (try > 1) {
+ fprintf(stderr, "\n");
+ errmsgno(EX_BAD,
+ "Error on sector %ld corrected after %d tries. Total of %d errors.\n",
+ addr, try, rp->errors);
+ }
+ }
+ try = 0;
+ cnt -= 1;
+ addr += 1;
+ bp += secsize;
+ }
+ return (0);
+}
+
+static void
+read_generic(SCSI *usalp, parm_t *parmp,
+ int (*rfunc)(SCSI *usalp, rparm_t *rp, caddr_t bp, long addr, int cnt),
+ rparm_t *rp,
+ int (*dfunc)(rparm_t *rp, caddr_t bp, long addr, int cnt))
+{
+ char filename[512];
+ char *defname = NULL;
+ FILE *f;
+ long addr = 0L;
+ long old_addr = 0L;
+ long num;
+ long end = 0L;
+ long start = 0L;
+ long cnt = 0L;
+ long next_point = 0L;
+ long secs_per_point = 0L;
+ double speed;
+ int msec;
+ int old_msec = 0;
+ int err = 0;
+ BOOL askrange = FALSE;
+ BOOL isrange = FALSE;
+ int secsize = rp->secsize;
+ int i = 0;
+
+ if (is_suid) {
+ if (usalp->inq->type != INQ_ROMD)
+ comerrno(EX_BAD, "Not root. Will only read from CD in suid/priv mode\n");
+ }
+
+ if (parmp == NULL || parmp->askrange)
+ askrange = TRUE;
+ if (parmp != NULL && !askrange && (parmp->start <= parmp->end))
+ isrange = TRUE;
+
+ filename[0] = '\0';
+
+ usalp->silent++;
+ if (read_capacity(usalp) >= 0)
+ end = usalp->cap->c_baddr + 1;
+ usalp->silent--;
+
+ if ((end <= 0 && isrange) || (askrange && usal_yes("Ignore disk size? ")))
+ end = 10000000; /* Hack to read empty (e.g. blank=fast) disks */
+
+ if (parmp) {
+ if (parmp->name)
+ defname = parmp->name;
+ if (defname != NULL) {
+ fprintf(stderr, "Copy from SCSI (%d,%d,%d) disk to file '%s'\n",
+ usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp),
+ defname);
+ }
+
+ addr = start = parmp->start;
+ if (parmp->end != -1 && parmp->end < end)
+ end = parmp->end;
+ cnt = Sbufsize / secsize;
+ }
+
+ if (defname == NULL) {
+ defname = "disk.out";
+ fprintf(stderr, "Copy from SCSI (%d,%d,%d) disk to file\n",
+ usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
+ fprintf(stderr, "Enter filename [%s]: ", defname); flush();
+ (void) rols_getline(filename, sizeof (filename));
+ }
+
+ if (askrange) {
+ addr = start;
+ getlong("Enter starting sector for copy:", &addr, start, end-1);
+/* getlong("Enter starting sector for copy:", &addr, -300, end-1);*/
+ start = addr;
+ }
+
+ if (askrange) {
+ num = end - addr;
+ getlong("Enter number of sectors to copy:", &num, 1L, num);
+ end = addr + num;
+ }
+
+ if (askrange) {
+/* XXX askcnt */
+ cnt = Sbufsize / secsize;
+ getlong("Enter number of sectors per copy:", &cnt, 1L, cnt);
+ }
+
+ if (filename[0] == '\0')
+ strncpy(filename, defname, sizeof (filename));
+ filename[sizeof (filename)-1] = '\0';
+ if (streql(filename, "-")) {
+ f = stdout;
+#ifdef NEED_O_BINARY
+ setmode(STDOUT_FILENO, O_BINARY);
+#endif
+ } else if ((f = fileopen(filename, notrunc?"wcub":"wctub")) == NULL)
+ comerr("Cannot open '%s'.\n", filename);
+
+ fprintf(stderr, "end: %8ld\n", end);
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ comerr("Cannot get start time\n");
+
+ if (meshpoints > 0) {
+ if ((end-start) < meshpoints)
+ secs_per_point = 1;
+ else
+ secs_per_point = (end-start) / meshpoints;
+ next_point = start + secs_per_point;
+ old_addr = start;
+ }
+
+ for (; addr < end; addr += cnt) {
+ if (didintr)
+ comexit(exsig); /* XXX besseres Konzept?!*/
+
+ if ((addr + cnt) > end)
+ cnt = end - addr;
+
+ if (meshpoints > 0) {
+ if (addr > next_point) {
+
+ msec = prstats_silent();
+ if ((msec - old_msec) == 0) /* Avoid division by zero */
+ msec = old_msec + 1;
+ speed = ((addr - old_addr)/(1000.0/secsize)) / (0.001*(msec - old_msec));
+ if (do_factor) {
+ if (is_cdrom)
+ speed /= 176.400;
+ else if (is_dvd)
+ speed /= 1385.0;
+ }
+ fprintf(stderr, "addr: %8ld cnt: %ld", addr, cnt);
+ printf("%8ld %8.2f\n", addr, speed);
+ fprintf(stderr, "\r");
+ next_point += secs_per_point;
+ old_addr = addr;
+ old_msec = msec;
+ i++;
+ if (meshpoints < 100)
+ flush();
+ else if (i % (meshpoints/100) == 0)
+ flush();
+ }
+ }
+ fprintf(stderr, "addr: %8ld cnt: %ld\r", addr, cnt);
+
+ usalp->silent++;
+ if ((*rfunc)(usalp, rp, Sbuf, addr, cnt) < 0) {
+ usalp->silent--;
+ err = usalp->scmd->ux_errno;
+ if (quiet) {
+ fprintf(stderr, "\n");
+ } else if (usalp->silent == 0) {
+ usal_printerr(usalp);
+ }
+ errmsgno(err, "Cannot read source disk\n");
+
+ if (read_retry(usalp, Sbuf, addr, cnt, rfunc, rp) < 0)
+ goto out;
+ } else {
+ usalp->silent--;
+ if (usal_getresid(usalp)) {
+ fprintf(stderr, "\nresid: %d\n", usal_getresid(usalp));
+ goto out;
+ }
+ }
+ (*dfunc)(rp, Sbuf, addr, cnt);
+ if (filewrite(f, Sbuf, cnt * secsize) < 0) {
+ err = geterrno();
+ fprintf(stderr, "\n");
+ errmsgno(err, "Cannot write '%s'\n", filename);
+ break;
+ }
+ }
+ fprintf(stderr, "addr: %8ld", addr);
+out:
+ fprintf(stderr, "\n");
+ msec = prstats();
+ if (msec == 0) /* Avoid division by zero */
+ msec = 1;
+#ifdef OOO
+ fprintf(stderr, "Read %.2f kB at %.1f kB/sec.\n",
+ (double)(addr - start)/(1024.0/usalp->cap->c_bsize),
+ (double)((addr - start)/(1024.0/usalp->cap->c_bsize)) / (0.001*msec));
+#else
+ fprintf(stderr, "Read %.2f kB at %.1f kB/sec.\n",
+ (double)(addr - start)/(1024.0/secsize),
+ (double)((addr - start)/(1024.0/secsize)) / (0.001*msec));
+#endif
+ print_bad();
+}
+
+static void
+write_disk(SCSI *usalp, parm_t *parmp)
+{
+ char filename[512];
+ char *defname = "disk.out";
+ FILE *f;
+ long addr = 0L;
+ long cnt;
+ long amt;
+ long end;
+ int msec;
+ int start;
+
+ if (is_suid)
+ comerrno(EX_BAD, "Not root. Will not write in suid/priv mode\n");
+
+ filename[0] = '\0';
+ if (read_capacity(usalp) >= 0) {
+ end = usalp->cap->c_baddr + 1;
+ print_capacity(usalp, stderr);
+ }
+
+ if (end <= 1)
+ end = 10000000; /* Hack to write empty disks */
+
+ if (parmp) {
+ if (parmp->name)
+ defname = parmp->name;
+ fprintf(stderr, "Copy from file '%s' to SCSI (%d,%d,%d) disk\n",
+ defname,
+ usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
+
+ addr = start = parmp->start;
+ if (parmp->end != -1 && parmp->end < end)
+ end = parmp->end;
+ cnt = Sbufsize / usalp->cap->c_bsize;
+ } else {
+ fprintf(stderr, "Copy from file to SCSI (%d,%d,%d) disk\n",
+ usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
+ fprintf(stderr, "Enter filename [%s]: ", defname); flush();
+ (void) rols_getline(filename, sizeof (filename));
+ fprintf(stderr, "Notice: reading from file always starts at file offset 0.\n");
+
+ getlong("Enter starting sector for copy:", &addr, 0L, end-1);
+ start = addr;
+ cnt = end - addr;
+ getlong("Enter number of sectors to copy:", &end, 1L, end);
+ end = addr + cnt;
+
+ cnt = Sbufsize / usalp->cap->c_bsize;
+ getlong("Enter number of sectors per copy:", &cnt, 1L, cnt);
+/* fprintf(stderr, "end: %8ld\n", end);*/
+ }
+
+ if (filename[0] == '\0')
+ strncpy(filename, defname, sizeof (filename));
+ filename[sizeof (filename)-1] = '\0';
+ if (streql(filename, "-")) {
+ f = stdin;
+#ifdef NEED_O_BINARY
+ setmode(STDIN_FILENO, O_BINARY);
+#endif
+ } else if ((f = fileopen(filename, "rub")) == NULL)
+ comerr("Cannot open '%s'.\n", filename);
+
+ fprintf(stderr, "end: %8ld\n", end);
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ comerr("Cannot get start time\n");
+
+ for (; addr < end; addr += cnt) {
+ if (didintr)
+ comexit(exsig); /* XXX besseres Konzept?!*/
+
+ if ((addr + cnt) > end)
+ cnt = end - addr;
+
+ fprintf(stderr, "addr: %8ld cnt: %ld\r", addr, cnt);
+
+ if ((amt = fileread(f, Sbuf, cnt * usalp->cap->c_bsize)) < 0)
+ comerr("Cannot read '%s'\n", filename);
+ if (amt == 0)
+ break;
+ if ((amt / usalp->cap->c_bsize) < cnt)
+ cnt = amt / usalp->cap->c_bsize;
+ if (write_scsi(usalp, Sbuf, addr, cnt) < 0)
+ comerrno(usalp->scmd->ux_errno,
+ "Cannot write destination disk\n");
+ }
+ fprintf(stderr, "addr: %8ld\n", addr);
+ msec = prstats();
+ if (msec == 0) /* Avoid division by zero */
+ msec = 1;
+ fprintf(stderr, "Wrote %.2f kB at %.1f kB/sec.\n",
+ (double)(addr - start)/(1024.0/usalp->cap->c_bsize),
+ (double)((addr - start)/(1024.0/usalp->cap->c_bsize)) / (0.001*msec));
+}
+
+static int
+choice(int n)
+{
+#if defined(HAVE_DRAND48)
+ extern double drand48(void);
+
+ return (drand48() * n);
+#else
+# if defined(HAVE_RAND)
+ extern int rand(void);
+
+ return (rand() % n);
+# else
+ return (0);
+# endif
+#endif
+}
+
+static void
+ra(SCSI *usalp)
+{
+/* char filename[512];*/
+ FILE *f;
+/* long addr = 0L;*/
+/* long cnt;*/
+/* long end;*/
+/* int msec;*/
+/* int start;*/
+/* int err = 0;*/
+
+ select_secsize(usalp, 2352);
+ read_capacity(usalp);
+ print_capacity(usalp, stderr);
+ fillbytes(Sbuf, 50*2352, 0);
+ if (read_g1(usalp, Sbuf, 0, 50) < 0)
+ errmsg("read CD\n");
+ f = fileopen("DDA", "wctb");
+/* filewrite(f, Sbuf, 50 * 2352 - usal_getresid(usalp));*/
+ filewrite(f, Sbuf, 50 * 2352);
+ fclose(f);
+}
+
+#define g5x_cdblen(cdb, len) ((cdb)->count[0] = ((len) >> 16L)& 0xFF,\
+ (cdb)->count[1] = ((len) >> 8L) & 0xFF,\
+ (cdb)->count[2] = (len) & 0xFF)
+
+int
+read_da(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, int subcode)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (usalp->cap->c_bsize <= 0)
+ raisecond("capacity_not_set", 0L);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*framesize;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xd8;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ g5_cdbaddr(&scmd->cdb.g5_cdb, addr);
+ g5_cdblen(&scmd->cdb.g5_cdb, cnt);
+ scmd->cdb.g5_cdb.res10 = subcode;
+
+ usalp->cmdname = "read_da";
+
+ return (usal_cmd(usalp));
+}
+
+int
+read_cd(SCSI *usalp, caddr_t bp, long addr, int cnt, int framesize, int data,
+ int subch)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*framesize;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xBE;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res = 0; /* expected sector type field ALL */
+ g5_cdbaddr(&scmd->cdb.g5_cdb, addr);
+ g5x_cdblen(&scmd->cdb.g5_cdb, cnt);
+
+ scmd->cdb.g5_cdb.count[3] = data & 0xFF;
+ scmd->cdb.g5_cdb.res10 = subch & 0x07;
+
+ usalp->cmdname = "read_cd";
+
+ return (usal_cmd(usalp));
+}
+
+static void
+oldmode(SCSI *usalp, int *errp, int *retrp)
+{
+ Uchar mode[0x100];
+ Uchar cmode[0x100];
+ Uchar *p;
+ int i;
+ int len;
+
+ fillbytes(mode, sizeof (mode), '\0');
+ fillbytes(cmode, sizeof (cmode), '\0');
+
+ if (!get_mode_params(usalp, 0x01, "CD error recovery parameter",
+ mode, (Uchar *)0, (Uchar *)cmode, (Uchar *)0, &len)) {
+ return;
+ }
+ if (xdebug)
+ usal_prbytes("Mode Sense Data", mode, len);
+
+ mode[0] = 0;
+ mode[2] = 0; /* ??? ist manchmal 0x80 */
+ p = mode;
+ p += mode[3] + 4;
+ *p &= 0x3F;
+
+ if (xdebug)
+ usal_prbytes("Mode page 1:", p, 0x10);
+
+ i = p[2];
+ if (errp != NULL)
+ *errp = i;
+
+ i = p[3];
+ if (retrp != NULL)
+ *retrp = i;
+}
+
+static void
+domode(SCSI *usalp, int err, int retr)
+{
+ Uchar mode[0x100];
+ Uchar cmode[0x100];
+ Uchar *p;
+ int i;
+ int len;
+
+ fillbytes(mode, sizeof (mode), '\0');
+ fillbytes(cmode, sizeof (cmode), '\0');
+
+ if (!get_mode_params(usalp, 0x01, "CD error recovery parameter",
+ mode, (Uchar *)0, (Uchar *)cmode, (Uchar *)0, &len)) {
+ return;
+ }
+ if (xdebug || (err == -1 && retr == -1)) {
+ usal_prbytes("Mode Sense Data", mode, len);
+ }
+
+ mode[0] = 0;
+ mode[2] = 0; /* ??? ist manchmal 0x80 */
+ p = mode;
+ p += mode[3] + 4;
+ *p &= 0x3F;
+
+ if (xdebug || (err == -1 && retr == -1))
+ usal_prbytes("Mode page 1:", p, 0x10);
+
+ i = p[2];
+ if (err == -1) {
+ getint("Error handling? ", &i, 0, 255);
+ p[2] = i;
+ } else {
+ if (xdebug)
+ fprintf(stderr, "Error handling set from %02X to %02X\n",
+ p[2], err);
+ p[2] = err;
+ }
+
+ i = p[3];
+ if (retr == -1) {
+ getint("Retry count? ", &i, 0, 255);
+ p[3] = i;
+ } else {
+ if (xdebug)
+ fprintf(stderr, "Retry count set from %d to %d\n",
+ p[3] & 0xFF, retr);
+ p[3] = retr;
+ }
+
+ if (xdebug || (err == -1 && retr == -1))
+ usal_prbytes("Mode Select Data", mode, len);
+ mode_select(usalp, mode, len, 0, usalp->inq->data_format >= 2);
+}
+
+
+/*--------------------------------------------------------------------------*/
+static void qpto96(Uchar *sub, Uchar *subq, int dop);
+/*EXPORT void qpto96 __PR((Uchar *sub, Uchar *subq, int dop));*/
+/*
+ * Q-Sub auf 96 Bytes blähen und P-Sub addieren
+ *
+ * OUT: sub, IN: subqptr
+ */
+static void
+/*EXPORT void*/
+qpto96(Uchar *sub, Uchar *subqptr, int dop)
+{
+ Uchar tmp[16];
+ Uchar *p;
+ int c;
+ int i;
+
+ if (subqptr == sub) {
+ movebytes(subqptr, tmp, 12);
+ subqptr = tmp;
+ }
+ fillbytes(sub, 96, '\0');
+
+ /* CSTYLED */
+ if (dop) for (i = 0, p = sub; i < 96; i++) {
+ *p++ |= 0x80;
+ }
+ for (i = 0, p = sub; i < 12; i++) {
+ c = subqptr[i] & 0xFF;
+/*printf("%02X\n", c);*/
+ if (c & 0x80)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x40)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x20)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x10)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x08)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x04)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x02)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x01)
+ *p++ |= 0x40;
+ else
+ p++;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void
+ovtime(SCSI *usalp)
+{
+ register int i;
+
+ usalp->silent++;
+ (void) test_unit_ready(usalp);
+ usalp->silent--;
+ if (test_unit_ready(usalp) < 0)
+ return;
+
+ printf("Doing 1000 'TEST UNIT READY' operations.\n");
+
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ comerr("Cannot get start time\n");
+
+ for (i = 1000; --i >= 0; ) {
+ (void) test_unit_ready(usalp);
+
+ if (didintr)
+ return;
+ }
+
+ prstats();
+
+ /*
+ * ATAPI drives do not like seek_g0()
+ */
+ usalp->silent++;
+ i = seek_g0(usalp, 0L);
+ usalp->silent--;
+
+ if (i >= 0) {
+ printf("Doing 1000 'SEEK_G0 (0)' operations.\n");
+
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ comerr("Cannot get start time\n");
+
+ for (i = 1000; --i >= 0; ) {
+ (void) seek_g0(usalp, 0L);
+
+ if (didintr)
+ return;
+ }
+
+ prstats();
+ }
+
+ usalp->silent++;
+ i = seek_g1(usalp, 0L);
+ usalp->silent--;
+ if (i < 0)
+ return;
+
+ printf("Doing 1000 'SEEK_G1 (0)' operations.\n");
+
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ comerr("Cannot get start time\n");
+
+ for (i = 1000; --i >= 0; ) {
+ (void) seek_g1(usalp, 0L);
+
+ if (didintr)
+ return;
+ }
+
+ prstats();
+}
+
+#define BAD_INC 16
+long *badsecs;
+int nbad;
+int maxbad;
+
+static void
+add_bad(long addr)
+{
+ if (maxbad == 0) {
+ maxbad = BAD_INC;
+ badsecs = malloc(maxbad * sizeof (long));
+ if (badsecs == NULL)
+ comerr("No memory for bad sector list\n.");
+ }
+ if (nbad >= maxbad) {
+ maxbad += BAD_INC;
+ badsecs = realloc(badsecs, maxbad * sizeof (long));
+ if (badsecs == NULL)
+ comerr("No memory to grow bad sector list\n.");
+ }
+ badsecs[nbad++] = addr;
+}
+
+static void
+print_bad(void)
+{
+ int i;
+
+ if (nbad == 0)
+ return;
+
+ fprintf(stderr, "Max corected retry count was %d (limited to %d).\n", maxtry, retries);
+ fprintf(stderr, "The following %d sector(s) could not be read correctly:\n", nbad);
+ for (i = 0; i < nbad; i++)
+ fprintf(stderr, "%ld\n", badsecs[i]);
+}