/* mmc - SCSI-3 mmc (multimedia commands)-standard-specific routines. */ #include #include #include #include #include #include #include #include #include #include #include #include "WriteCDR.h" #include "util.h" #include "mmc.h" /* mmc commands used. */ #define CLOSE_TRACK_SESSION 0x5B #define READ_BUFFER_CAPACITY 0x5C #define READ_DISC_INFORMATION 0x51 #define READ_TRACK_INFORMATION 0x52 #define SET_CD_SPEED 0xBB #define SYNCHRONIZE_CACHE 0x35 #define WRITE 0x2A /* disc information layout. */ typedef struct { unsigned data_length: 16; unsigned reserved1: 3; unsigned erasable: 1; unsigned state_of_last_session: 2; unsigned disc_status: 2; unchar number_of_first_track_on_disc; unchar number_of_sessions; unchar first_track_number_in_last_session; unchar last_track_number_in_last_session; unsigned did_v: 1; unsigned dbc_v: 1; unsigned uru: 1; unsigned reserved2: 5; unchar disc_type; unchar reserved3[3]; unchar disc_identification[4]; unchar last_session_lead_in_start_time[4]; unchar last_possible_start_time_for_start_of_lead_out[4]; unchar disc_bar_code[8]; unchar reserved4; unchar number_of_opc_table_entries; } disc_information; /* mode sense / mode select page layout. */ typedef struct { unchar sense_data_length; unchar medium_type; unsigned reserved2: 3; unsigned dpofua: 1; unsigned reserved3: 3; unsigned ebc: 1; unchar block_descriptor_length; } sense_select_header; typedef struct { unsigned number_of_blocks: 32; unchar density_code; unsigned block_length: 24; } block_descriptor; typedef struct { /* Write Parameters Page. */ /* 0 */ unsigned ps: 1; unsigned reserved1: 1; unsigned page_code: 6; /* 1 */ unchar page_length; /* 2 */ unsigned reserved2: 3; unsigned test_write: 1; unsigned write_type: 4; /* 3 */ unsigned multi_session: 2; unsigned fp: 1; unsigned copy: 1; unsigned track_mode: 4; /* 4 */ unsigned reserved3: 4; unsigned data_block_type: 4; /* 5 */ unchar reserved4; /* 6 */ unchar reserved5; /* 7 */ unsigned reserved6: 2; unsigned host_application_code: 6; /* 8 */ unchar session_format; /* 9 */ unchar reserved7; /* 10 */ unchar packet_size[4]; /* 14 */ unsigned audio_pause_length: 16; /* 16 */ unchar media_catalog_number[16]; /* 32 */ unchar international_standard_recording_code[16]; /* 48 */ unchar sub_header[4]; /* 52 */ unchar vendor_specific[4]; } page05; typedef struct { /* CD Capabilities and Mechanical Status Page. */ /* 0 */ unsigned ps: 1; unsigned reserved1: 1; unsigned page_code: 6; /* 1 */ unchar page_length; /* 2 */ unsigned reserved2: 5; unsigned method2: 1; unsigned cd_rw_read: 1; unsigned cd_r_read: 1; /* 3 */ unsigned reserved3: 5; unsigned test_write: 1; unsigned cd_rw_write: 1; unsigned cd_r_write: 1; /* 4 */ unsigned reserved4: 1; unsigned multi_session: 1; unsigned mode2_form2: 1; unsigned mode2_form1: 1; unsigned digital_port2: 1; unsigned digital_port1: 1; unsigned composite: 1; unsigned audio_play: 1; /* 5 */ unsigned read_bar_code: 1; unsigned upc: 1; unsigned isrc: 1; unsigned c2_pointers_are_supported: 1; unsigned rw_de_interleave_and_corrected: 1; unsigned rw_supported: 1; unsigned cd_da_stream_is_accurate: 1; unsigned cd_da_commands_supported: 1; /* 6 */ unsigned loading_mechanism_type: 3; unsigned reserved5: 1; unsigned eject: 1; unsigned prevent_jumper: 1; unsigned lock_state: 1; unsigned lock: 1; /* 7 */ unsigned reserved6: 4; unsigned sw_slot_selection: 1; unsigned changer_supports_disc_present_reporting: 1; unsigned separate_channel_mute_supported: 1; unsigned separate_volume_levels_per_channel: 1; /* 8 */ unsigned maximum_read_speed_supported: 16; /* 10 */ unsigned number_of_volume_levels_supported: 16; /* 12 */ unsigned buffer_size_supported_by_drive: 16; /* 14 */ unsigned current_read_speed_selected: 16; /* 16 */ unchar reserved7; /* 17 */ unsigned reserved8: 2; unsigned length: 2; unsigned lsbf: 1; unsigned rck: 1; unsigned bck: 1; unsigned reserved9: 1; /* 18 */ unsigned maximum_write_speed_supported: 16; /* 20 */ unsigned current_write_speed_selected: 16; } page2a; static ulong_t logical_block_address = 0; /* utility routines. * ----------------- */ static boolean_t check_mmc_status(dsreq_t *dsp, char *msg) { if (check_status(dsp, msg)) { if (STATUS(dsp) == STA_CHECK) { unchar key = SENSEKEY(SENSEBUF(dsp)); unchar asc = ADDSENSECODE(SENSEBUF(dsp)); unchar ascq = SENSEBUF(dsp)[13]; char *m = 0; switch ((key << 16) | (asc << 8) | ascq) { case 0x0: m = "NO ADDITIONAL SENSE INFORMATION"; break; case 0x10B00: m = "WARNING"; break; case 0x10B01: m = "WARNING - SPECIFIED TEMPERATURE EXCEEDED"; break; case 0x10B02: m = "WARNING - ENCLOSURE DEGRADED"; break; case 0x11700: m = "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"; break; case 0x11701: m = "RECOVERED DATA WITH RETRIES"; break; case 0x11702: m = "RECOVERED DATA WITH POSITIVE HEAD OFFSET"; break; case 0x11703: m = "RECOVERED DATA WITH NEGATIVE HEAD OFFSET"; break; case 0x11704: m = "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"; break; case 0x11705: m = "RECOVERED DATA USING PREVIOUS SECTOR ID"; break; case 0x11707: m = "RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT"; break; case 0x11708: m = "RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE"; break; case 0x11709: m = "RECOVERED DATA WITHOUT ECC - DATA REWRITTEN"; break; case 0x11800: m = "RECOVERED DATA WITH ERROR CORRECTION APPLIED"; break; case 0x11801: m = "RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED"; break; case 0x11802: m = "RECOVERED DATA - DATA AUTO-REALLOCATED"; break; case 0x11803: m = "RECOVERED DATA WITH CIRC"; break; case 0x11804: m = "RECOVERED DATA WITH L-EC"; break; case 0x11805: m = "RECOVERED DATA - RECOMMEND REASSIGNMENT"; break; case 0x11806: m = "RECOVERED DATA - RECOMMEND REWRITE"; break; case 0x13700: m = "ROUNDED PARAMETER"; break; case 0x17301: m = "POWER CALIBRATION AREA ALMOST FULL"; break; case 0x20400: m = "LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"; break; case 0x20401: m = "LOGICAL UNIT IS IN PROCESS OF BECOMING READY"; break; case 0x20402: m = "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED"; break; case 0x20403: m = "LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED"; break; case 0x20404: m = "LOGICAL UNIT NOT READY, FORMAT IN PROGRESS"; break; case 0x20407: m = "LOGICAL UNIT NOT READY, OPERATION IN PROGRESS"; break; case 0x20408: m = "LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS"; break; case 0x23000: m = "INCOMPATIBLE MEDIUM INSTALLED"; break; case 0x23001: m = "CANNOT READ MEDIUM - UNKNOWN FORMAT"; break; case 0x23002: m = "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"; break; case 0x23003: m = "CLEANING CARTRIDGE INSTALLED"; break; case 0x23004: m = "CANNOT WRITE MEDIUM - UNKNOWN FORMAT"; break; case 0x23005: m = "CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"; break; case 0x23006: m = "CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM"; break; case 0x23007: m = "CLEANING FAILURE"; break; case 0x23A00: m = "MEDIUM NOT PRESENT"; break; case 0x23A01: m = "MEDIUM NOT PRESENT - TRAY CLOSED"; break; case 0x23A02: m = "MEDIUM NOT PRESENT - TRAY OPEN"; break; case 0x23E00: m = "LOGICAL UNIT HAS NOT SELF-CONFIGURED YET"; break; case 0x30200: m = "NO SEEK COMPLETE"; break; case 0x30600: m = "NO REFERENCE POSITION FOUND"; break; case 0x30C00: m = "WRITE ERROR"; break; case 0x30C07: m = "WRITE ERROR - RECOVERY NEEDED"; break; case 0x30C08: m = "WRITE ERROR - RECOVERY FAILED"; break; case 0x30C09: m = "WRITE ERROR - LOSS OF STREAMING"; break; case 0x30C0A: m = "WRITE ERROR - PADDING BLOCKS ADDED"; break; case 0x31100: m = "UNRECOVERED READ ERROR"; break; case 0x31101: m = "READ RETRIES EXHAUSTED"; break; case 0x31102: m = "ERROR TOO LONG TO CORRECT"; break; case 0x31105: m = "L-EC UNCORRECTABLE ERROR"; break; case 0x31106: m = "CIRC UNRECOVERED ERROR"; break; case 0x3110F: m = "ERROR READING UPC/EAN NUMBER"; break; case 0x31110: m = "ERROR READING ISRC NUMBER"; break; case 0x31500: m = "RANDOM POSITIONING ERROR"; break; case 0x31501: m = "MECHANICAL POSITIONING ERROR"; break; case 0x31502: m = "POSITIONING ERROR DETECTED BY READ OF MEDIUM"; break; case 0x33100: m = "MEDIUM FORMAT CORRUPTED"; break; case 0x33101: m = "FORMAT COMMAND FAILED"; break; case 0x35100: m = "ERASE FAILURE"; break; case 0x35700: m = "UNABLE TO RECOVER TABLE-OF-CONTENTS"; break; case 0x37200: m = "SESSION FIXATION ERROR"; break; case 0x37201: m = "SESSION FIXATION ERROR WRITING LEAD-IN"; break; case 0x37202: m = "SESSION FIXATION ERROR WRITING LEAD-OUT"; break; case 0x37300: m = "CD CONTROL ERROR"; break; case 0x37302: m = "POWER CALIBRATION AREA IS FULL"; break; case 0x37303: m = "POWER CALIBRATION AREA ERROR"; break; case 0x37304: m = "PROGRAM MEMORY AREA UPDATE FAILURE"; break; case 0x37305: m = "PROGRAM MEMORY AREA IS FULL"; break; case 0x40017: m = "CLEANING REQUESTED"; break; case 0x40500: m = "LOGICAL UNIT DOES NOT RESPOND TO SELECTION"; break; case 0x40800: m = "LOGICAL UNIT COMMUNICATION FAILURE"; break; case 0x40801: m = "LOGICAL UNIT COMMUNICATION TIME-OUT"; break; case 0x40802: m = "LOGICAL UNIT COMMUNICATION PARITY ERROR"; break; case 0x40900: m = "TRACK FOLLOWING ERROR"; break; case 0x40901: m = "TRACKING SERVO FAILURE"; break; case 0x40902: m = "FOCUS SERVO FAILURE"; break; case 0x40903: m = "SPINDLE SERVO FAILURE"; break; case 0x40904: m = "HEAD SELECT FAULT"; break; case 0x41B00: m = "SYNCHRONOUS DATA TRANSFER ERROR"; break; case 0x43E01: m = "LOGICAL UNIT FAILURE"; break; case 0x43E02: m = "TIMEOUT ON LOGICAL UNIT"; break; case 0x44002: m = "TIMEOUT ON LOGICAL UNIT"; break; case 0x44400: m = "INTERNAL TARGET FAILURE"; break; case 0x44600: m = "UNSUCCESSFUL SOFT RESET"; break; case 0x44700: m = "SCSI PARITY ERROR"; break; case 0x44A00: m = "COMMAND PHASE ERROR"; break; case 0x44B00: m = "DATA PHASE ERROR"; break; case 0x44C00: m = "LOGICAL UNIT FAILED SELF-CONFIGURATION"; break; case 0x45300: m = "MEDIA LOAD OR EJECT FAILED"; break; case 0x46500: m = "VOLTAGE FAULT"; break; case 0x50011: m = "AUDIO PLAY OPERATION IN PROGRESS"; break; case 0x50700: m = "MULTIPLE PERIPHERAL DEVICES SELECTED"; break; case 0x51A00: m = "PARAMETER LIST LENGTH ERROR"; break; case 0x52000: m = "INVALID COMMAND OPERATION CODE"; break; case 0x52100: m = "LOGICAL BLOCK ADDRESS OUT OF RANGE"; break; case 0x52101: m = "INVALID ELEMENT ADDRESS"; break; case 0x52400: m = "INVALID FIELD IN CDB"; break; case 0x52500: m = "LOGICAL UNIT NOT SUPPORTED"; break; case 0x52600: m = "INVALID FIELD IN PARAMETER LIST"; break; case 0x52601: m = "PARAMETER NOT SUPPORTED"; break; case 0x52602: m = "PARAMETER VALUE INVALID"; break; case 0x52603: m = "THRESHOLD PARAMETERS NOT SUPPORTED"; break; case 0x52604: m = "INVALID RELEASE OF ACTIVE PERSISTENT RESERVATION"; break; case 0x52700: m = "WRITE PROTECTED"; break; case 0x52701: m = "HARDWARE WRITE PROTECTED"; break; case 0x52702: m = "LOGICAL UNIT SOFTWARE WRITE PROTECTED"; break; case 0x52703: m = "ASSOCIATED WRITE PROTECT"; break; case 0x52704: m = "PERSISTENT WRITE PROTECT"; break; case 0x52705: m = "PERMANENT WRITE PROTECT"; break; case 0x52B00: m = "COPY CANNOT EXECUTE SINCE HOST CANNOT DISCONNECT"; break; case 0x52C00: m = "COMMAND SEQUENCE ERROR"; break; case 0x52C03: m = "CURRENT PROGRAM AREA IS NOT EMPTY"; break; case 0x52C04: m = "CURRENT PROGRAM AREA IS EMPTY"; break; case 0x53008: m = "CANNOT WRITE - APPLICATION CODE MISMATCH"; break; case 0x53009: m = "CURRENT SESSION NOT FIXATED FOR APPEND"; break; case 0x53900: m = "SAVING PARAMETERS NOT SUPPORTED"; break; case 0x53D00: m = "INVALID BITS IN IDENTIFY MESSAGE"; break; case 0x54300: m = "MESSAGE ERROR"; break; case 0x55302: m = "MEDIUM REMOVAL PREVENTED"; break; case 0x56300: m = "END OF USER AREA ENCOUNTERED ON THIS TRACK"; break; case 0x56301: m = "PACKET DOES NOT FIT IN AVAILABLE SPACE"; break; case 0x56400: m = "ILLEGAL MODE FOR THIS TRACK"; break; case 0x56401: m = "INVALID PACKET SIZE"; break; case 0x57203: m = "SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION"; break; case 0x57204: m = "EMPTY OR PARTIALLY WRITTEN RESERVED TRACK"; break; case 0x60A00: m = "ERROR LOG OVERFLOW"; break; case 0x62800: m = "NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED"; break; case 0x62801: m = "IMPORT OR EXPORT ELEMENT ACCESSED"; break; case 0x62900: m = "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"; break; case 0x62901: m = "POWER ON OCCURRED"; break; case 0x62902: m = "SCSI BUS RESET OCCURRED"; break; case 0x62903: m = "BUS DEVICE RESET FUNCTION OCCURRED"; break; case 0x62904: m = "DEVICE INTERNAL RESET"; break; case 0x62A00: m = "PARAMETERS CHANGED"; break; case 0x62A01: m = "MODE PARAMETERS CHANGED"; break; case 0x62A02: m = "LOG PARAMETERS CHANGED"; break; case 0x62A03: m = "RESERVATIONS PREEMPTED"; break; case 0x62F00: m = "COMMANDS CLEARED BY ANOTHER INITIATOR"; break; case 0x63B0D: m = "MEDIUM DESTINATION ELEMENT FULL"; break; case 0x63B0E: m = "MEDIUM SOURCE ELEMENT EMPTY"; break; case 0x63B0F: m = "END OF MEDIUM REACHED"; break; case 0x63B11: m = "MEDIUM MAGAZINE NOT ACCESSIBLE"; break; case 0x63B12: m = "MEDIUM MAGAZINE REMOVED"; break; case 0x63B13: m = "MEDIUM MAGAZINE INSERTED"; break; case 0x63B14: m = "MEDIUM MAGAZINE LOCKED"; break; case 0x63B15: m = "MEDIUM MAGAZINE UNLOCKED"; break; case 0x63F00: m = "TARGET OPERATING CONDITIONS HAVE CHANGED"; break; case 0x63F01: m = "MICROCODE HAS BEEN CHANGED"; break; case 0x63F02: m = "CHANGED OPERATING DEFINITION"; break; case 0x63F03: m = "INQUIRY DATA HAS CHANGED"; break; case 0x65A00: m = "OPERATOR REQUEST OR STATE CHANGE INPUT"; break; case 0x65A01: m = "OPERATOR MEDIUM REMOVAL REQUEST"; break; case 0x65A02: m = "OPERATOR SELECTED WRITE PROTECT"; break; case 0x65A03: m = "OPERATOR SELECTED WRITE PERMIT"; break; case 0x65B00: m = "LOG EXCEPTION"; break; case 0x65B01: m = "THRESHOLD CONDITION MET"; break; case 0x65B02: m = "LOG COUNTER AT MAXIMUM"; break; case 0x65B03: m = "LOG LIST CODES EXHAUSTED"; break; case 0x65D00: m = "FAILURE PREDICTION THRESHOLD EXCEEDED"; break; case 0x65DFF: m = "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)"; break; case 0x65E00: m = "LOW POWER CONDITION ON"; break; case 0x65E01: m = "IDLE CONDITION ACTIVATED BY TIMER"; break; case 0x65E02: m = "STANDBY CONDITION ACTIVATED BY TIMER"; break; case 0x65E03: m = "IDLE CONDITION ACTIVATED BY COMMAND"; break; case 0x65E04: m = "STANDBY CONDITION ACTIVATED BY COMMAND"; break; case 0xA1D00: m = "MISCOMPARE DURING VERIFY OPERATION"; break; case 0xB0006: m = "I/O PROCESS TERMINATED"; break; case 0xB1111: m = "READ ERROR - LOSS OF STREAMING"; break; case 0xB4500: m = "SELECT OR RESELECT FAILURE"; break; case 0xB4800: m = "INITIATOR DETECTED ERROR MESSAGE RECEIVED"; break; case 0xB4900: m = "INVALID MESSAGE ERROR"; break; case 0xB4E00: m = "OVERLAPPED COMMANDS ATTEMPTED"; break; } if (m != 0) printf("MMC ASC=0x%.2X/ASCQ=0x%.2X/key=0x%.2X means:\n %s.\n", asc, ascq, key, m); } return B_TRUE; } else return B_FALSE; } static boolean_t close_session(struct dsreq *dsp) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), CLOSE_TRACK_SESSION, 0, 2, 0, 0, 0, 0, 0, 0, 0); filldsreq(dsp, 0, 0, DSRQ_SENSE); TIME(dsp) = 600 * 1000; /* 10 minutes timeout */ doscsireq(getfd(dsp), dsp); return check_mmc_status(dsp, "Close Session"); } static boolean_t read_buffer_capacity(dsreq_t *dsp, ulong_t *capacityp, ulong_t *blankp) { struct { unsigned data_length: 16; unchar reserved1[2]; unsigned total_length: 32; unsigned blank_length: 32; } buffer_capacity; fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ_BUFFER_CAPACITY, 0, 0, 0, 0, 0, 0, B2(sizeof buffer_capacity), 0); filldsreq(dsp, (unchar *)&buffer_capacity, sizeof buffer_capacity, DSRQ_READ | DSRQ_SENSE); doscsireq(getfd(dsp), dsp); *capacityp = buffer_capacity.total_length; *blankp = buffer_capacity.blank_length; return check_mmc_status(dsp, "Read Buffer Capacity"); } static boolean_t read_disc_information(dsreq_t *dsp, disc_information *infop, ulong_t info_len) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ_DISC_INFORMATION, 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_mmc_status(dsp, "Read Disc Information"); } static boolean_t set_write_speed(dsreq_t *dsp, unchar speed) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), SET_CD_SPEED, 0, B2(0xFFFF), B2(speed * 706 / 4), 0, 0, 0, 0); (CMDBUF(dsp))[10] = 0; (CMDBUF(dsp))[11] = 0; CMDLEN(dsp) = 12; filldsreq(dsp, 0, 0, DSRQ_SENSE); doscsireq(getfd(dsp), dsp); return check_mmc_status(dsp, "Set Write Speed"); } static boolean_t synchronize_cache(struct dsreq *dsp) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0); filldsreq(dsp, 0, 0, DSRQ_SENSE); TIME(dsp) = 600 * 1000; /* 10 minutes timeout */ doscsireq(getfd(dsp), dsp); if (check_mmc_status(dsp, "Synchronize Cache")) return B_TRUE; testunitready00(dsp); testunitready00(dsp); return check_mmc_status(dsp, "Synchronize Cache / Test ready"); } 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, B4(logical_block_address), 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); logical_block_address += n_blocks; return check_mmc_status(dsp, "Write"); } static boolean_t update_lba(dsreq_t *dsp, unchar track_number) { struct { unsigned data_length: 16; unchar track_number; unchar session_number; unchar reserved1; unsigned reserved2: 2; unsigned damage: 1; unsigned copy: 1; unsigned track_mode: 4; unsigned rt: 1; unsigned blank: 1; unsigned packet: 1; unsigned fp: 1; unsigned data_mode: 4; unsigned reserved3: 7; unsigned nwa_v: 1; ulong_t track_start_address; ulong_t next_writable_address; ulong_t free_blocks; ulong_t fixed_packet_size; ulong_t track_size; } track_info; fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ_TRACK_INFORMATION, 1, 0, 0, 0, track_number, 0, B2(sizeof(track_info)), 0); filldsreq(dsp, (unchar *)&track_info, sizeof(track_info), DSRQ_READ | DSRQ_SENSE); doscsireq(getfd(dsp), dsp); logical_block_address = track_info.next_writable_address; return check_mmc_status(dsp, "Read Track Information"); } static boolean_t allocate_buffer(dsreq_t *dsp, uint_t block_size, ulong_t *alloc_size, unchar **data_buffer) { ulong_t buffer_size, buffer_room; /* Allocate a buffer that is about 1/8th of the device's buffer size. */ if (read_buffer_capacity(dsp, &buffer_size, &buffer_room)) return B_TRUE; *alloc_size = (buffer_size / (8*block_size)) * block_size; *data_buffer = (unchar *)malloc(*alloc_size); if (*data_buffer == 0) { fprintf(stderr, "Out of memory.\n"); return B_TRUE; } return B_FALSE; } /* Externally callable routines. * ----------------------------- */ void mmc_cleanup_writer(dsreq_t *dsp) { prevent_removal(dsp, B_FALSE); eject_disc(dsp); } boolean_t mmc_setup_writer(SETUP_WRITER_ARGS) { struct {sense_select_header h; block_descriptor bd; page05 p;} p05; struct {sense_select_header h; block_descriptor bd; page2a p;} p2a; disc_information info; ulong_t capacity; ulong_t block_length = audio ? CDDA_BLOCKSIZE : CDROM_BLOCKSIZE; read_disc_information(dsp, &info, sizeof info); if (info.disc_status != 0 /* empty */) { fprintf(stderr, "Disc not blank.\n"); return B_TRUE; } if (speed == 0) { modesense1a(dsp, (caddr_t)&p2a, sizeof p2a, 0, 0x2A, 0); if (check_mmc_status(dsp, "Mode Sense Page 2A")) return B_TRUE; speed = p2a.p.current_write_speed_selected / 176; if ((speed < 1)|| (speed > max_speed)) { fprintf(stderr, "Current write speed (%d kB/s) unrecognized, use -s parameter to set speed.\n", p2a.p.current_write_speed_selected); return B_TRUE; } } else if (set_write_speed(dsp, speed)) return B_TRUE; capacity = (MSF2INT(info.last_possible_start_time_for_start_of_lead_out[1], info.last_possible_start_time_for_start_of_lead_out[2], info.last_possible_start_time_for_start_of_lead_out[3]) - ((2 * FRAMES_PER_SECOND) * (audio? n_files : 1))) * block_length; if (verbose > 0) report_sizes(capacity, total_bytes, block_length, speed); if (total_bytes > capacity) { fprintf(stderr, "Image data will not fit on disc.\n"); return B_TRUE; } modesense1a(dsp, (caddr_t)&p05, sizeof p05, 0, 0x05, 0); if (check_mmc_status(dsp, "Mode Sense Page 05")) return B_TRUE; p05.bd.block_length = block_length; p05.h.sense_data_length = 0; /* reserved on mode select */ p05.h.dpofua = 0; /* reserved on mode select */ p05.h.ebc = 0; /* reserved */ p05.p.test_write = check; p05.p.write_type = 1; /* track at once */ p05.p.multi_session = 3; /* next session allowed */ p05.p.fp = 0; /* variable packets */ p05.p.copy = 0; /* original */ p05.p.host_application_code = 0; p05.p.session_format = 0; /* CD-DA or CD-ROM */ p05.p.packet_size[0] = 0; p05.p.packet_size[1] = 0; p05.p.packet_size[2] = 0; p05.p.packet_size[3] = 0; p05.p.audio_pause_length = 150; if (audio) { p05.p.track_mode = 2; /* 2-channel audio without pre-emphasis, copy permitted */ p05.p.data_block_type = 0; /* 2352 bytes of raw data*/ } else { p05.p.track_mode = 6; /* data track, recorded uninterrupted, copy permitted */ p05.p.data_block_type = 8; /* Mode 1, 2048 byte blocks */ } mode_select(dsp, (caddr_t)&p05, sizeof p05); return check_mmc_status(dsp, "Mode Select Page 05"); } boolean_t mmc_write_audio(WRITE_AUDIO_ARGS) { ulong_t alloc_size; ulong_t buffer_size; ulong_t buffer_room; ulong_t total_written = 0; unchar *data_buffer; int fnr; if (allocate_buffer(dsp, CDDA_BLOCKSIZE, &alloc_size, &data_buffer)) return B_TRUE; /* write audio tracks. */ for (fnr = 0; fnr < n_files; 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'); if (update_lba(dsp, fnr+1)) return B_TRUE; do { ulong_t read_size = track_bytes - bytes_written; ulong_t frames_read; if (read_size > alloc_size) read_size = alloc_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); if (read_buffer_capacity(dsp, &buffer_size, &buffer_room)) return B_TRUE; if ((bytes_written > buffer_size) && (buffer_room > buffer_size/4)) fputc('\007', stderr); if (write_blocks(dsp, data_buffer, read_size / CDDA_BLOCKSIZE, CDDA_BLOCKSIZE)) return B_TRUE; bytes_written += read_size; total_written += read_size; display_progress(verbose, total_written, total_bytes, buffer_size, buffer_room); } while (bytes_written != track_bytes); if ((verbose > 0) && (fnr == n_files-1)) printf("\nBegin fixation.\n"); if (synchronize_cache(dsp)) return B_TRUE; } return close_session(dsp); } boolean_t mmc_write_image(WRITE_IMAGE_ARGS) { ulong_t alloc_size; ulong_t buffer_size; ulong_t buffer_room; unchar *data_buffer; ulong_t bytes_written = 0; if (allocate_buffer(dsp, CDROM_BLOCKSIZE, &alloc_size, &data_buffer)) return B_TRUE; /* write data. */ do { ulong_t read_size = total_bytes - bytes_written; if (read_size > alloc_size) read_size = alloc_size; if (read_block(file_list, n_files, read_size, data_buffer)) return B_TRUE; if (read_buffer_capacity(dsp, &buffer_size, &buffer_room)) return B_TRUE; if ((bytes_written > buffer_size) && (buffer_room > buffer_size/4)) fputc('\007', stderr); if (write_blocks(dsp, data_buffer, (read_size + CDROM_BLOCKSIZE - 1) / CDROM_BLOCKSIZE, CDROM_BLOCKSIZE)) return B_TRUE; bytes_written += read_size; display_progress(verbose, bytes_written, total_bytes, buffer_size, buffer_room); } while (bytes_written != total_bytes); if (verbose > 0) printf("\nBegin fixation.\n"); if (synchronize_cache(dsp)) return B_TRUE; return close_session(dsp); }