/* WriteCDR - Write a CDR disc. * * * * * Modification History. * * Carsten Koch, 10-Sep-1995. * Original Version. Supports Sony CDU 920S in disc-at-once data mode. * Carsten Koch, 17-Sep-1995. * Add support for Yamaha CDR 100. * Carsten Koch, 4-Oct-1995. * Implement audio support for Sony CDU 920S. * Carsten Koch, 8-Oct-1995. * Implement audio support for Yamaha CDR 100. * Carsten Koch, 26-Oct-1995. * Implement -i option. * Carsten Koch, 30-Jul-1996. Version 1.0. * Implement support for Sony CDU 924S. * Carsten Koch, 24-Aug-1996. Version 1.1. * Fix audio capacity and estimated time display for Sony driver. * Carsten Koch, 29-Oct-1996. Version 1.2. * Implement support for Yamaha CDR 102. * Carsten Koch, 13-Nov-1996. Version 1.3. * Increase "write-continue" timeout to 10 minutes for Sony firmware * version 2.0b, which appears to take that long to fixate the disc. * Carsten Koch, 4-Jan-1997. Version 1.4. * Fix maximum image size calculation in sony.c/sony_setup_writer. * If a binary image is not a multiple of the CD-ROM block size, * issue a warning message and fill the last block automatically. * Scan all devices in all SCSI buses until the first ENOENT error. * Carsten Koch, 16-Apr-1997. Version 1.5. * Implement support for Sony CDU 926S. * Carsten Koch, 28-Apr-1997. Version 1.6 beta 1. * Implement audio support for Sony CDU 926S. * Change WriteCDR to translate the CD-writer's error code * to plain english for friendlier error messages. * Carsten Koch / Jim Newberry, 13-Jun-1997. Version 1.6 beta 2. * Implement support for SCSI-3 MMC compliant writers. * WriteCDR now supports the Yamaha CDR 400c and CDR 400t. * Carsten Koch, 21-Jun-1997. Version 1.6. * Implement support for Sony CDU 926S zero-second audio pause. * Carsten Koch, 24-Aug-1997. Version 1.6.1. * Add a "Begin Fixation" message to the mmc driver (similar to sony). * Carsten Koch, 29-Jun-1998. Version 1.6.2. * make uitil.c/find_device search at least 15 devices and 2 busses, * so it will work correctly under IRIX >= 6.3. * Jeroen Kleinnibbelink, 30-Sep-1998. Version 1.6.3. * Implement support for Yamaha CRW4260. * Craig Alsop, 15-Jan-1999. Version 1.7. * Implement support for Sony CDU 948S. * Carsten Koch, 17-Jan-1999. Version 1.8. * Fix capacity calculation and disk style check in sony driver. * Merge the above four (independent) mods into one version. */ #include #include #include #include #include #include #include #include #include #include #include #include "WriteCDR.h" #include "util.h" #include "mmc.h" #include "sony.h" #include "yamaha.h" static struct { char vendor_identification[9]; char product_identification[17]; char writer_description[40]; boolean_t speed_supported[6]; uint_t max_write_speed; boolean_t (*setup_writer) (SETUP_WRITER_ARGS); boolean_t (*write_audio) (WRITE_AUDIO_ARGS); boolean_t (*write_image) (WRITE_IMAGE_ARGS); void (*cleanup_writer)(dsreq_t *dsp); } writer_info[NR_OF_SUPPORTED_WRITERS] = { "SONY ", "CD-R CDU920S ", "Sony CDU 920S", B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, 2, sony_setup_writer, sony_write_audio, sony_write_image, sony_cleanup_writer, "SONY ", "CD-R CDU924S ", "Sony CDU 924S", B_FALSE, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, 2, sony_setup_writer, sony_write_audio, sony_write_image, sony_cleanup_writer, "SONY ", "CD-R CDU926S ", "Sony CDU 926S", B_FALSE, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, 2, sony_setup_writer, sony926_write_audio, sony926_write_image, sony_cleanup_writer, "SONY ", "CD-R CDU948S ", "Sony CDU 948S", B_FALSE, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, 4, sony_setup_writer, sony926_write_audio, sony926_write_image, sony_cleanup_writer, "YAMAHA ", "CDR100 ", "Yamaha CDR 100", B_TRUE, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE, 4, yamaha_setup_writer, yamaha_write_audio, yamaha_write_image, yamaha_cleanup_writer, "YAMAHA ", "CDR102 ", "Yamaha CDR 102", B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, 2, yamaha_setup_writer, yamaha_write_audio, yamaha_write_image, yamaha_cleanup_writer, "YAMAHA ", "CDR400c ", "Yamaha CDR 400 (caddy version)", B_TRUE, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE, 4, mmc_setup_writer, mmc_write_audio, mmc_write_image, mmc_cleanup_writer, "YAMAHA ", "CDR400t ", "Yamaha CDR 400 (tray version)", B_TRUE, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE, 4, mmc_setup_writer, mmc_write_audio, mmc_write_image, mmc_cleanup_writer, "YAMAHA ", "CRW4260 ", "Yamaha CRW4260", B_TRUE, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE, 4, mmc_setup_writer, mmc_write_audio, mmc_write_image, mmc_cleanup_writer, }; boolean_t identify_device(dsreq_t *dsp, char *device_id) { uint_t wt; struct { /* 0 */ unchar peripheral_device_type; /* 1 */ unsigned rmb: 1; unsigned device_type_qualifier: 7; /* 2 */ unsigned iso_version: 2; unsigned ecma_version: 3; unsigned ansi_approved_version: 3; /* 3 */ unsigned aenc: 1; unsigned trmio: 1; unsigned reserved1: 2; unsigned response_data_format: 4; /* 4 */ unchar additional_length; /* 5 */ unchar reserved2; /* 6 */ unchar reserved3; /* 7 */ unsigned relad: 1; unsigned wb32: 1; unsigned wb16: 1; unsigned sync: 1; unsigned link: 1; unsigned reserved4: 1; unsigned cmdq: 1; unsigned srst: 1; /* 8 */ char vendor_identification[8]; /* 16 */ char product_identification[16]; /* 32 */ char product_revision_level[4]; /* 36 */ unchar vendor_specific[20]; /* 56 */ unchar reserved5[40]; } inquiry_data; bzero(&inquiry_data, sizeof(inquiry_data)); inquiry12(dsp, (caddr_t)&inquiry_data, sizeof(inquiry_data), 0); if (STATUS(dsp) != STA_GOOD) return B_TRUE; for (wt = 0; wt < NR_OF_SUPPORTED_WRITERS; wt++) if ((strncmp(inquiry_data.vendor_identification, writer_info[wt].vendor_identification, 8) == 0) && (strncmp(inquiry_data.product_identification, writer_info[wt].product_identification, 16) == 0)) { *device_id = wt; return B_FALSE; } return B_TRUE; } /* top level routines. * ------------------- */ static boolean_t preprocess_audio_files(char *argv[], uint_t verbose, int file_list[99], uint_t n_files, audio_info_t audio_info[99], ulong_t *total_sizep) { int fnr; ulong_t total_blocks = 0; for (fnr = 0; fnr < n_files; fnr++) { AFfilehandle af = AFopenfd(file_list[fnr], "r", AF_NULL_FILESETUP); int samp_type, bits_per_samp; ulong_t blocks; if (af == AF_NULL_FILEHANDLE) { fprintf(stderr, "%s is not an audio file.\n", argv[optind + fnr]); return B_TRUE; } if (AFgetchannels(af, AF_DEFAULT_TRACK) != 2) { fprintf(stderr, "Audio data on %s not stereo.\n", argv[optind + fnr]); return B_TRUE; } if (AFgetrate(af, AF_DEFAULT_TRACK) != 44100.0) { fprintf(stderr, "Audio data on %s not sampled at 44100 Hz.\n", argv[optind + fnr]); return B_TRUE; } AFgetsampfmt(af, AF_DEFAULT_TRACK, &samp_type, &bits_per_samp); if (bits_per_samp != 16) { fprintf(stderr, "Audio data on %s not 16-bit samples.\n", argv[optind + fnr]); return B_TRUE; } blocks = ((long)AFgetframecnt(af, AF_DEFAULT_TRACK) * 4 + CDDA_BLOCKSIZE - 1) / CDDA_BLOCKSIZE; if (blocks < 4 * FRAMES_PER_SECOND) { fprintf(stderr, "Audio data on %s shorter than 4 seconds.\n", argv[optind + fnr]); return B_TRUE; } audio_info[fnr].file = af; audio_info[fnr].blocks = blocks; total_blocks += blocks; } if (verbose > 0) printf("Total audio data=%.2u:%.2u:%.2u.\n", MINUTE(total_blocks), SECOND(total_blocks), FRAME(total_blocks)); if (MINUTE(total_blocks) > 80) { /* a precise check will be done when the actual media size is known, but over 80 minutes cannot fit on any current media. */ fprintf(stderr, "Total audio data too long.\n"); return B_TRUE; } *total_sizep = total_blocks * CDDA_BLOCKSIZE; return B_FALSE; } static boolean_t process_options(int argc, char *argv[], boolean_t *audiop, boolean_t *checkp, char **devp, uint_t *speedp, uint_t *verbosep, int file_list[99], uint_t *n_filesp, ulong_t *total_sizep, audio_info_t audio_info[99]) { int c, fnr; long stdin_size = 0; struct stat st; *audiop = B_FALSE; *checkp = B_FALSE; *devp = 0; *speedp = 0; *verbosep = 0; *total_sizep = 0; while ((c = getopt(argc, argv, "acd:i:s:tv")) != EOF) switch (c) { case 'a': *audiop = B_TRUE; break; case 'c': *checkp = B_TRUE; break; case 'd': *devp = strdup(optarg); break; case 'i': stdin_size = atol(optarg); break; case 's': *speedp = atoi(optarg); if ((*speedp < 1) || (*speedp > 6)) { fprintf(stderr, "-s option argument error, speed must be between 1 and 6.\n"); return B_TRUE; } break; case 'v': (*verbosep)++; break; default: return B_TRUE; } *n_filesp = argc - optind; if (stdin_size != 0) (*n_filesp)++; if (*n_filesp > 99) { fprintf(stderr, "Too many image files, maximum is 99.\n"); return B_TRUE; } else if (*n_filesp == 0) { fprintf(stderr, "No input.\n"); return B_TRUE; } fnr = 0; if (stdin_size != 0) { if (*audiop) { fprintf(stderr, "Cannot read AIFF-C files from stdin.\n"); return B_TRUE; } (*total_sizep) += stdin_size; file_list[fnr++] = 0; } for (; fnr < *n_filesp; fnr++) { int f = open(argv[optind + fnr], O_RDONLY|O_EXCL); if ((f < 0) || (fstat(f, &st) < 0)) { perror(argv[optind + fnr]); return B_TRUE; } (*total_sizep) += st.st_size; file_list[fnr] = f; } if (*audiop) return preprocess_audio_files(argv, *verbosep, file_list, *n_filesp, audio_info, total_sizep); if (*total_sizep < (4 * FRAMES_PER_SECOND * CDROM_BLOCKSIZE)) { fprintf(stderr, "total image size must be > %d, is %d bytes.\n", (4 * FRAMES_PER_SECOND * CDROM_BLOCKSIZE), *total_sizep); return B_TRUE; } if (*total_sizep % CDROM_BLOCKSIZE != 0) fprintf(stderr, "Warning - total image size not an integer multiple of CD-ROM block size.\n"); return B_FALSE; } static void usage(char *name) { fprintf(stderr, "\ Usage:\n\ \n\ %s [-a] [-c] [-d dev] [-i size] [-s n] [-v] file...\n\ \n\ Option Meaning\n\ \n\ -a Audio mode.\n\ -a not specified: Create a data disc (CD-ROM).\n\ -a specified: Create an audio disc (CD-DA).\n\ See file... below.\n\ \n\ -c Check mode.\n\ -c not specified: Actually write to the CD-R disc.\n\ -c specified: Perform pseudo-writes.\n\ Checks the data path, but leaves CD-R disc unaltered.\n\ \n\ -d dev CD writer device.\n\ -d not specified: Find first available writer automatically.\n\ -d U: Use CD writer on SCSI controller 0, unit U.\n\ -d /dev/scsi/scCdUl0: Use CD writer on SCSI controller C, unit U.\n\ \n\ -i size Process stdin as first image file.\n\ stdin must contain/supply exactly 'size' bytes of data.\n\ -i is not supported in audio mode.\n\ \n\ -s n CD Writer speed.\n\ -s not specified: Keep current CD writer speed.\n\ -s 1: Single speed.\n\ -s 2: 2x speed.\n\ etc.\n\ \n\ -v Level of verbosity.\n\ -v not specified: Error information only.\n\ -v specified once: Status and Error information.\n\ -v specified twice: Debug, Status and Error information.\n\ \n\ file... In audio mode, each of these files must contain 44100 Hz\n\ stereo audio data in AIFF-C format.\n\ Each file is written to an individual audio track and\n\ must therefore comprise at least 4 seconds of audio data.\n\ \n\ In data mode, the files are concatenated and written\n\ to a single data track. WriteCDR does not interpret the\n\ file contents in any way. The user is responsible for\n\ meaningful contents, i.e. a filesystem image.\n\ If '-i' is specified, stdin is read first and must\n\ contain the specified number of bytes.\n", name); } /* Main program. * ------------- */ int main(int argc, char *argv[]) { boolean_t audio; /* create audio disc */ audio_info_t audio_info[99]; /* list of audio file handles and sizes */ boolean_t check; /* just a check run, medium is not modified */ char *dev; /* CD writer SCSI device name */ dsreq_t *dsp; /* CD writer SCSI device handle */ int file_list[99]; /* list of image file handles */ uint_t n_files; /* number of image files */ ulong_t total_size; /* total size of image files [bytes] */ uint_t speed; /* requested writer speed */ uint_t verbose; /* 0 = errors, 1 = status, 2 = debug */ unchar writer_model; /* writer hardware model */ int return_code = 0;/* program exit code */ if (process_options(argc, argv, &audio, &check, &dev, &speed, &verbose, file_list, &n_files, &total_size, audio_info)) { usage(argv[0]); return 1; } if (verbose > 0) fprintf(stderr, "WriteCDR Version " VERSION ".\n"); if (verbose > 1) dsdebug = 1; if (find_device(dev, verbose, &dsp, (char *)&writer_model)) { usage(argv[0]); return 2; } if ((speed != 0) && !writer_info[writer_model].speed_supported[speed-1]) { fprintf(stderr, "%dx write speed not supported by %s.\n", speed, writer_info[writer_model].writer_description); usage(argv[0]); return 2; } if (verbose > 0) fprintf(stderr, "CD writer: %s.\n", writer_info[writer_model].writer_description); if (wait_ready(dsp, verbose)) return 3; if (prevent_removal(dsp, B_TRUE)) return 4; if (writer_info[writer_model].setup_writer(dsp, verbose, check, writer_info[writer_model].max_write_speed, speed, n_files, total_size, audio, writer_model)) return_code = 5; else { if (audio) { if (writer_info[writer_model].write_audio(dsp, verbose, audio_info, n_files, total_size)) return_code = 6; } else if (writer_info[writer_model].write_image(dsp, verbose, file_list, n_files, total_size)) return_code = 7; } writer_info[writer_model].cleanup_writer(dsp); if ((return_code == 0) && (verbose > 0)) printf("\nDisc completed successfully.\n"); return return_code; }