/* yamaha - Yamaha CDR100-specific SCSI routines. */ #include #include #include #include #include #include #include #include #include #include #include #include "WriteCDR.h" #include "util.h" #include "yamaha.h" /* Yamaha CDR100 vendor specific commands. */ #define FIXATION 0xE9 #define FLUSH_CACHE 0x35 #define READ_BLOCK_LIMIT 0x05 #define READ_DISC_INFO 0x43 #define WRITE 0x2A #define WRITE_TRACK 0xE6 #define DEVICE_BUFFER_SIZE (512*1024) #define CDROM_BUFFER_SIZE ((DEVICE_BUFFER_SIZE / (8*CDROM_BLOCKSIZE)) * CDROM_BLOCKSIZE) #define CDDA_BUFFER_SIZE ((DEVICE_BUFFER_SIZE / (8*CDDA_BLOCKSIZE)) * CDDA_BLOCKSIZE) /* disc info layout. */ typedef struct { unsigned toc_pma_data_length: 16; unsigned reserved1: 1; unsigned first_track_number: 7; unsigned reserved2: 1; unsigned last_track_number: 7; unchar reserved3; unsigned reserved4: 4; unsigned control: 4; unchar track_number; unchar reserved5; ulong_t logical_block_address; } disc_info; /* mode sense / mode select page layout. */ typedef struct { unchar sense_data_length; unchar medium_type; unsigned reserved2: 1; unsigned host_application_code: 7; unchar block_descriptor_length; } sense_header; typedef struct { unchar reserved1; unchar medium_type; unsigned reserved2: 1; unsigned host_application_code: 7; unchar block_descriptor_length; } select_header; typedef struct { unchar density_code; unsigned number_of_blocks: 24; unchar reserved; unsigned block_length: 24; } block_descriptor; typedef struct { /* Drive configuration Page. */ unsigned reserved0: 2; unsigned page_code: 6; unchar page_length; unchar reserved; unsigned speed: 4; unsigned write_mode: 4; } page31; typedef struct { /* Write Format Page. */ unsigned reserved0: 2; unsigned page_code: 6; unchar page_length; unsigned reserved1: 4; unsigned sbcd_format: 4; unsigned reserved2: 4; unsigned format_mode: 4; unchar fixed1; unchar fixed2; unchar fixed3; unchar fixed4; } page32; static struct {sense_header h; block_descriptor bd; page31 p;} page31_sense; static struct {sense_header h; block_descriptor bd; page32 p;} page32_sense; /* utility routines. * ----------------- */ static boolean_t check_yamaha_status(dsreq_t *dsp, char *msg) { if (check_status(dsp, msg)) { if (STATUS(dsp) == STA_CHECK) { unchar asc = ADDSENSECODE(SENSEBUF(dsp)); char *m = 0; switch (asc) { case 0x00: m = "No additional sense"; break; case 0x03: m = "Caddy out"; break; case 0x04: m = "Drive not ready"; break; case 0x08: m = "Communication failure"; break; case 0x09: m = "Track following error"; break; case 0x11: m = "Unrecovered read error"; break; case 0x15: m = "Positioning error"; break; case 0x17: m = "Recovered read data with retries"; break; case 0x18: m = "Recovered read with ECC correction"; break; case 0x1A: m = "Parameter list length error"; break; case 0x20: m = "Invalid command"; break; case 0x21: m = "Invalid block address"; break; case 0x24: m = "Illegal field in command descriptor"; break; case 0x25: m = "Invalid LUN"; break; case 0x26: m = "Invalid field in parameter list"; break; case 0x28: m = "Medium changed"; break; case 0x29: m = "Power-on reset or bus reset occured"; break; case 0x2C: m = "Command sequence error"; break; case 0x31: m = "Medium format corrupted"; break; case 0x32: m = "Write data error with CD"; break; case 0x33: m = "Monitor ATIP error"; break; case 0x34: m = "Absorption control error"; break; case 0x3D: m = "Invalid bits in identify message"; break; case 0x40: m = "Diagnostic failure"; break; case 0x42: m = "Power-on or self test failure"; break; case 0x44: m = "Internal controller error"; break; case 0x47: m = "SCSI parity error"; break; case 0x50: m = "Write operation in progress"; break; case 0x53: m = "Medium (caddy) load or eject failed"; break; case 0x57: m = "Unable to read TOC, PMA, subcode or ATIP"; break; case 0x5A: m = "Operator medium removal request"; break; case 0x63: m = "End of user area encountered on this track"; break; case 0x64: m = "Illegal mode for this track"; break; case 0x65: m = "Verify failed"; break; case 0x81: m = "Illegal track"; break; case 0x82: m = "Command currently not valid"; break; case 0x83: m = "Medium removal is prevented"; break; case 0xA0: m = "Stopped on non-data block"; break; case 0xA1: m = "Invalid start address"; break; case 0xA2: m = "Attempt to cross track-boundary"; break; case 0xA3: m = "Illegal medium"; break; case 0xA4: m = "Disc write-protected"; break; case 0xA5: m = "Application code conflict"; break; case 0xA6: m = "Illegal block size for command"; break; case 0xA7: m = "Block size conflict"; break; case 0xA8: m = "Illegal transfer length"; break; case 0xA9: m = "Request for fixation failed"; break; case 0xAA: m = "End of medium reached"; break; case 0xAB: m = "Data track length error"; break; case 0xAD: m = "Buffer underrun (writer ran out of data, system too slow)"; break; case 0xAE: m = "Illegal track mode"; break; case 0xAF: m = "Optimum-power calibration error"; break; case 0xB0: m = "Calibration area almost full"; break; case 0xB1: m = "Current program are empty"; break; case 0xB2: m = "No EFM at search address"; break; case 0xB3: m = "Link area encountered"; break; case 0xB4: m = "Calibration area full"; break; case 0xB5: m = "Dummy blocks added"; break; case 0xB6: m = "Block size format conflict"; break; case 0xB7: m = "Current command aborted"; break; case 0xB8: m = "Write Lead-in in progress"; break; case 0xBF: m = "Buffer overflow"; break; case 0xC1: m = "Barcode reading error"; break; case 0xD0: m = "Recovery needed"; break; case 0xD1: m = "Can't recover from track"; break; case 0xD2: m = "Can't recover from program emmory area"; break; case 0xD3: m = "Can't recover from lead-in area"; break; case 0xD4: m = "Can't recover from lead-out area"; break; case 0xD5: m = "Can't recover from optical power"; break; case 0xD6: m = "EEPROM failure"; break; case 0xE0: m = "Laser current over"; break; case 0xE1: m = "Servo adjustment error"; break; } if (m != 0) printf("Yamaha ASC=0x%.2X means:\n '%s.'\n", asc, m); } return B_TRUE; } else return B_FALSE; } static void wait_write_complete(struct dsreq *dsp) { if (STATUS(dsp) == STA_GOOD) { testunitready00(dsp); while ((STATUS(dsp) == STA_CHECK) && ADDSENSECODE(SENSEBUF(dsp)) == 0x50) /* write operation in progress */ { sleep(1); testunitready00(dsp); } } } static boolean_t fixation(struct dsreq *dsp) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), FIXATION, 1, 0, 0, 0, 0, 0, 0, 1, 0); filldsreq(dsp, 0, 0, DSRQ_WRITE|DSRQ_SENSE); doscsireq(getfd(dsp), dsp); wait_write_complete(dsp); return check_yamaha_status(dsp, "Fixation"); } static boolean_t flush_cache(struct dsreq *dsp) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), FLUSH_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0); filldsreq(dsp, 0, 0, DSRQ_WRITE|DSRQ_SENSE); TIME(dsp) = 600 * 1000; /* 10 minutes timeout */ doscsireq(getfd(dsp), dsp); wait_write_complete(dsp); return check_yamaha_status(dsp, "Flush Cache"); } static void read_block_limit(dsreq_t *dsp, ulong_t *capacityp) { *capacityp = 0; fillg0cmd(dsp, (unchar *)CMDBUF(dsp), READ_BLOCK_LIMIT, 0, 0, 0, 0, 0); filldsreq(dsp, (unchar *)capacityp, 4, DSRQ_READ | DSRQ_SENSE); doscsireq(getfd(dsp), dsp); if (STATUS(dsp) != STA_GOOD) *capacityp = 0; } static boolean_t read_disc_info(dsreq_t *dsp, disc_info *infop, ulong_t info_len) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ_DISC_INFO, 0, 0, 0, 0, 0, 0, B2(info_len), 0); filldsreq(dsp, (unchar *)infop, info_len, DSRQ_READ | DSRQ_SENSE); doscsireq(getfd(dsp), dsp); return check_yamaha_status(dsp, "Read Disc Info"); } static boolean_t write_track(dsreq_t *dsp, boolean_t audio) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), WRITE_TRACK, 0, 0, 0, 0, 0, audio? 4:1, 0, 0, 0); filldsreq(dsp, 0, 0, DSRQ_WRITE | DSRQ_SENSE); TIME(dsp) = 120 * 1000; /* 2 minutes timeout */ doscsireq(getfd(dsp), dsp); return check_yamaha_status(dsp, "Write Track"); } static boolean_t write_blocks(dsreq_t *dsp, unchar *buffer, uint_t n_blocks, ulong_t block_size) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), WRITE, 0, 0, 0, 0, 0, 0, B2(n_blocks), 0); filldsreq(dsp, buffer, n_blocks * block_size, DSRQ_WRITE | DSRQ_SENSE); TIME(dsp) = 120 * 1000 + n_blocks * block_size / 100; /* 2 minutes + 0.01ms/byte */ doscsireq(getfd(dsp), dsp); return check_yamaha_status(dsp, "Write"); } /* Externally callable routines. * ----------------------------- */ void yamaha_cleanup_writer(dsreq_t *dsp) { struct {select_header h; block_descriptor bd; page31 p;} page31_select; struct {select_header h; block_descriptor bd; page32 p;} page32_select; if (page31_sense.h.sense_data_length != 0) { bcopy(&page31_sense, &page31_select, sizeof page31_sense); page31_select.h.reserved1 = 0; mode_select(dsp, (caddr_t)&page31_select, sizeof page31_select); } if (page32_sense.h.sense_data_length != 0) { bcopy(&page32_sense, &page32_select, sizeof page32_sense); page32_select.h.reserved1 = 0; mode_select(dsp, (caddr_t)&page32_select, sizeof page32_select); } prevent_removal(dsp, B_FALSE); eject_disc(dsp); } boolean_t yamaha_setup_writer(SETUP_WRITER_ARGS) { disc_info info; struct {select_header h; block_descriptor bd; page31 p;} page31_select; struct {select_header h; block_descriptor bd; page32 p;} page32_select; ulong_t capacity; read_disc_info(dsp, &info, sizeof info); if ((info.first_track_number != 0) || (info.last_track_number != 0) || (info.control != 0) || (info.track_number != 0xAA)) { fprintf(stderr, "Disc not blank.\n"); return B_TRUE; } modesense1a(dsp, (caddr_t)&page31_sense, sizeof page31_sense, 0, 0x31, 0); if (check_yamaha_status(dsp, "Mode Sense Page 31")) return B_TRUE; if (speed == 0) { switch (page31_sense.p.speed) { case 0: speed = 1; break; case 1: speed = 2; break; case 2: speed = 4; break; default: speed = 6; } if (speed > max_speed) speed = max_speed; } bcopy(&page31_sense, &page31_select, sizeof page31_sense); page31_select.h.reserved1 = 0; page31_select.bd.block_length = audio? CDDA_BLOCKSIZE : CDROM_BLOCKSIZE; page31_select.p.speed = speed >> 1; page31_select.p.write_mode = (unsigned)check; mode_select(dsp, (caddr_t)&page31_select, sizeof page31_select); if (check_yamaha_status(dsp, "Mode Select Page 31")) return B_TRUE; modesense1a(dsp, (caddr_t)&page32_sense, sizeof page32_sense, 0, 0x32, 0); if (check_yamaha_status(dsp, "Mode Sense Page 32")) return B_TRUE; bcopy(&page32_sense, &page32_select, sizeof page32_sense); page32_select.h.reserved1 = 0; page32_select.p.format_mode = 0; /* track at once */ mode_select(dsp, (caddr_t)&page32_select, sizeof page32_select); if (check_yamaha_status(dsp, "Mode Select Page 32")) return B_TRUE; capacity = (info.logical_block_address - (2 * FRAMES_PER_SECOND * (audio ? n_files : 1))) * page31_select.bd.block_length; if (verbose > 0) report_sizes(capacity, total_bytes, page31_select.bd.block_length, speed); if (total_bytes > capacity) { fprintf(stderr, "Image data will not fit on disc.\n"); return B_TRUE; } return B_FALSE; } boolean_t yamaha_write_audio(WRITE_AUDIO_ARGS) { ulong_t buffer_room; ulong_t total_written = 0; unchar *data_buffer; int fnr; boolean_t error = B_FALSE; /* Allocate a buffer that is about a quarter of the device's buffer size. */ data_buffer = (unchar *)malloc(CDDA_BUFFER_SIZE); if (data_buffer == 0) { fprintf(stderr, "Out of memory.\n"); return B_TRUE; } /* write audio tracks. */ for (fnr = 0; (fnr < n_files) && !error; fnr++) { ulong_t bytes_written = 0; ulong_t track_bytes = audio_info[fnr].blocks*CDDA_BLOCKSIZE; if (verbose > 0) printf("\r Track %d.%c", fnr + 1, verbose > 1? '\n' : '\r'); error = write_track(dsp, B_TRUE); while ((bytes_written != track_bytes) && !error) { ulong_t read_size = track_bytes - bytes_written; ulong_t frames_read; if (read_size > CDDA_BUFFER_SIZE) read_size = CDDA_BUFFER_SIZE; frames_read = AFreadframes(audio_info[fnr].file, AF_DEFAULT_TRACK, data_buffer, (int)(read_size / 4)); if (4*frames_read != read_size) bzero(data_buffer + 4*frames_read, (read_size - 4*frames_read)); swap_bytes((ulong_t *)data_buffer, frames_read); read_block_limit(dsp, &buffer_room); if ((total_written > DEVICE_BUFFER_SIZE) && (buffer_room > DEVICE_BUFFER_SIZE/4)) fputc('\007', stderr); /* write the blocks. */ error = write_blocks(dsp, data_buffer, read_size / CDDA_BLOCKSIZE, CDDA_BLOCKSIZE); bytes_written += read_size; total_written += read_size; display_progress(verbose, total_written, total_bytes, DEVICE_BUFFER_SIZE, buffer_room); } if (flush_cache(dsp)) error = B_TRUE; } if (error) return B_TRUE; if (verbose > 0) printf("\nBegin fixation.\n"); return fixation(dsp); } boolean_t yamaha_write_image(WRITE_IMAGE_ARGS) { ulong_t buffer_room; unchar *data_buffer; ulong_t total_written = 0; ulong_t read_size; boolean_t error; /* Allocate a buffer that is about a quarter of the device's buffer size. */ data_buffer = (unchar *)malloc(CDROM_BUFFER_SIZE); if (data_buffer == 0) { fprintf(stderr, "Out of memory.\n"); return B_TRUE; } error = write_track(dsp, B_FALSE); while ((total_written != total_bytes) && !error) { read_size = total_bytes - total_written; if (read_size > CDROM_BUFFER_SIZE) read_size = CDROM_BUFFER_SIZE; if (read_block(file_list, n_files, read_size, data_buffer)) error = B_TRUE; else { read_block_limit(dsp, &buffer_room); if ((total_written > DEVICE_BUFFER_SIZE) && (buffer_room > DEVICE_BUFFER_SIZE/4)) fputc('\007', stderr); error = write_blocks(dsp, data_buffer, (read_size + CDROM_BLOCKSIZE - 1) / CDROM_BLOCKSIZE, CDROM_BLOCKSIZE); } total_written += read_size; display_progress(verbose, total_written, total_bytes, DEVICE_BUFFER_SIZE, buffer_room); } if (flush_cache(dsp)) error = B_TRUE; if (error) return B_TRUE; if (verbose > 0) printf("\nBegin fixation.\n"); return fixation(dsp); }