for (j = 7; j >= 0; j--) {
uchar_t flag = 0x1 << j;
if ((*svc_major) & flag) {
-
-
return ((i * 8) + j);
}
}
for (j = 7; j >= 0; j--) {
uchar_t flag = 0x1 << j;
if ((*req_major) & flag) {
-
-
return ((i * 8) + j);
}
}
ASC_LOG_BLK_OOR = 0x21 /* LOGICAL BLOCK OUT OF RANGE */
} atapi_add_sense_code_t ;
+struct atapi_irq_flags {
+ uint8_t c_d : 1;
+ uint8_t io_dir : 1;
+ uint8_t rel : 1;
+ uint8_t tag : 5;
+} __attribute__((packed));
+
+
struct atapi_sense_data {
union {
} __attribute__((packed));
+struct atapi_mode_sense_cmd {
+ uint8_t atapi_cmd; // 0x5a
+ uint8_t rsvd1 : 3;
+ uint8_t disable_blk_desc : 1;
+ uint8_t long_lba_acc : 1;
+ uint8_t lun : 3;
+ uint8_t page_code : 6;
+ uint8_t page_ctrl : 2;
+ uint8_t sub_page_code;
+ uint8_t rsvd2[3];
+ uint16_t alloc_len;
+ uint8_t link : 1;
+ uint8_t flag : 1;
+ uint8_t naca : 1;
+ uint8_t rsvd3 : 3;
+ uint8_t vendor_specific : 2;
+} __attribute__((packed));
+
+struct atapi_mode_sense_hdr {
+ uint16_t mode_data_len;
+ uint8_t media_type_code;
+ uint8_t rsvd[2];
+ uint16_t blk_desc_len;
+} __attribute__((packed));
+
+
+struct atapi_rd_capacity_cmd {
+ uint8_t atapi_cmd; // 0x25
+ uint8_t obsolete : 1;
+ uint8_t rsvd1 : 4;
+ uint8_t lun : 3;
+ uint32_t lba;
+ uint16_t rsvd2;
+ uint8_t pmi;
+ uint8_t rsvd3 : 7;
+ uint8_t link : 1;
+ uint8_t flag : 1;
+ uint8_t naca : 1;
+ uint8_t rsvd4 : 3;
+ uint8_t vendor_spec : 2;
+} __attribute__((packed));
+
+
+struct atapi_rd_capacity_resp {
+ uint32_t lba;
+ uint32_t block_len;
+} __attribute__((packed));
+
+
+struct atapi_rd_toc_cmd {
+ uint8_t atapi_cmd; // 0x43
+ uint8_t rsvd1 : 1;
+ uint8_t msf : 1;
+ uint8_t rsvd2 : 3;
+ uint8_t lun : 3;
+ uint8_t format : 4;
+ uint8_t rsvd3 : 4;
+ uint8_t rsvd4[3];
+ uint8_t track_num;
+ uint16_t alloc_len;
+ uint8_t link : 1;
+ uint8_t flag : 1;
+ uint8_t naca : 1;
+ uint8_t rsvd5 : 3;
+ uint8_t vendor_specific : 2;
+} __attribute__((packed));
+
+struct atapi_rd_toc_resp {
+ uint16_t data_len;
+ uint8_t first_track_num;
+ uint8_t last_track_num;
+
+ struct {
+ uint8_t rsvd;
+ uint8_t ctrl : 4;
+ uint8_t adr : 4;
+ uint8_t track_num;
+ uint8_t rsvd2;
+ uint32_t start_addr;
+ } track_descs[0] __attribute__((packed)) ;
+
+} __attribute__((packed));
+
+
+struct atapi_error_recovery {
+ uint8_t page_code : 6;
+ uint8_t rsvd : 1;
+ uint8_t page_ctrl : 1;
+ uint8_t page_len;
+ uint8_t dcr : 1;
+ uint8_t dte : 1;
+ uint8_t per : 1;
+ uint8_t rsvd1 : 1;
+ uint8_t rc : 1;
+ uint8_t tb : 1;
+ uint8_t arre : 1;
+ uint8_t awre : 1;
+ uint8_t rd_retry_cnt;
+ uint8_t correct_spin;
+ uint8_t head_offset;
+ uint8_t data_strobe_offset;
+ uint8_t emcdr : 2;
+ uint8_t rsvd2 : 6;
+ uint8_t wr_retry_cnt;
+ uint8_t rsvd3;
+ uint16_t recovery_time_limit;
+} __attribute__((packed));
+
+
+struct atapi_cdrom_caps {
+ uint8_t page_code : 6;
+ uint8_t rsvd : 1;
+ uint8_t page_ctrl : 1;
+ uint8_t page_len;
+ uint8_t cdr_rd : 1;
+ uint8_t cdrw_rd : 1;
+ uint8_t mthd_2 : 1;
+ uint8_t dvdrom_rd : 1;
+ uint8_t dvdr_rd : 1;
+ uint8_t dvdram_rd : 1;
+ uint8_t rsvd1 : 2;
+ uint8_t cdr_wr : 1;
+ uint8_t cdrw_wr : 1;
+ uint8_t tst_wr : 1;
+ uint8_t rsvd2 : 1;
+ uint8_t dvdr_wr : 1;
+ uint8_t dvdram_wr : 1;
+ uint8_t rsvd3 : 2;
+ uint8_t audio_play : 1;
+ uint8_t composite : 1;
+ uint8_t digi_port1 : 1;
+ uint8_t digi_port2 : 1;
+ uint8_t mode2_form1 : 1;
+ uint8_t mode2_form2 : 1;
+ uint8_t multisession : 1;
+ uint8_t BUF : 1;
+ uint8_t cd_da : 1;
+ uint8_t cdda_str_acc : 1;
+ uint8_t rw_supported : 1;
+ uint8_t rw_dc : 1;
+ uint8_t c2_ptrs_supp : 1;
+ uint8_t isrc : 1;
+ uint8_t upc : 1;
+ uint8_t rd_bar_cd_cap : 1;
+ uint8_t lock : 1;
+ uint8_t lock_state : 1;
+ uint8_t prevent_jmpr : 1;
+ uint8_t eject : 1;
+ uint8_t rsvd4 : 1;
+ uint8_t lmt : 3;
+ uint8_t sep_vol : 1;
+ uint8_t sep_chnl_mute : 1;
+ uint8_t sdp : 1;
+ uint8_t sss : 1;
+ uint8_t side_chg_cap : 1;
+ uint8_t rw_in_lead_rd : 1;
+ uint8_t rsvd5 : 2;
+ uint16_t obsolete1;
+ uint16_t num_vols_supp;
+ uint16_t lun_buf_size; // in KBytes
+ uint16_t obsolete2;
+ uint8_t obsolete3;
+ uint8_t rsvd6 : 1;
+ uint8_t bckf : 1;
+ uint8_t rck : 1;
+ uint8_t lsbf : 1;
+ uint8_t len : 2;
+ uint8_t rsvd7 : 2;
+ uint16_t obsolete4[2];
+ uint16_t cp_mgmnt_rev_supp;
+ uint8_t rsvd8;
+ uint8_t rot_ctrl_sel : 2;
+ uint8_t rsvd9 : 6;
+ uint16_t cur_wr_spd; // KBytes/sec
+ uint16_t num_lun_wr_spd_dsc_tbls;
+ // lun write speed descriptor tables
+} __attribute__((packed));
+
#endif
#endif
*/
#define ATAPI_PACKET_SIZE 12
+#define ATAPI_BLOCK_SIZE 2048
#include "atapi-types.h"
+
+/* ATAPI sucks...
+ * The OS will write to the cylinder register the number of bytes it wants to read
+ * however the device can change that value
+ *
+ */
+static int atapi_update_req_len(struct vm_device * dev, struct ide_channel * channel, uint_t xfer_len) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ // PrintDebug("Updating request length (pre=%d)\n", drive->req_len);
+
+ if (drive->req_len == 0) {
+ PrintError("ATAPI Error: request of length 0\n");
+ return -1;
+ }
+
+
+ channel->status.busy = 0;
+ channel->status.data_req = 1;
+ channel->status.error = 0;
+
+ drive->irq_flags.io_dir = 1;
+ drive->irq_flags.c_d = 0;
+
+ // make count even
+ if (drive->req_len % 2) {
+ drive->req_len -= 1;
+ }
+
+ // if the device can't return as much as the OS requested
+ if (drive->req_len > xfer_len) {
+ drive->req_len = xfer_len;
+ }
+
+ // PrintDebug("Updating request length (post=%d)\n", drive->req_len);
+
+ return 0;
+}
+
+
+
+// This is for simple commands that don't need to sanity check the req_len
+static void atapi_setup_cmd_resp(struct vm_device * dev, struct ide_channel * channel, uint_t xfer_len) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ drive->transfer_length = xfer_len;
+ drive->transfer_index = 0;
+ drive->req_len = drive->transfer_length;
+
+ drive->irq_flags.io_dir = 1;
+ drive->irq_flags.c_d = 0;
+
+ channel->status.busy = 0;
+ channel->status.data_req = 1;
+ channel->status.error = 0;
+
+ ide_raise_irq(dev, channel);
+}
+
static void atapi_cmd_error(struct vm_device * dev, struct ide_channel * channel,
atapi_sense_key_t sense_key, atapi_add_sense_code_t asc) {
struct ide_drive * drive = get_selected_drive(channel);
drive->cd_state.sense.sense_key = sense_key;
drive->cd_state.sense.asc = asc;
+
+ drive->irq_flags.io_dir = 1;
+ drive->irq_flags.c_d = 1;
+ drive->irq_flags.rel = 0;
+
ide_raise_irq(dev, channel);
}
static void atapi_cmd_nop(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
channel->status.busy = 0;
channel->status.ready = 1;
channel->status.data_req = 0;
channel->status.error = 0;
+ drive->irq_flags.io_dir = 1;
+ drive->irq_flags.c_d = 1;
+ drive->irq_flags.rel = 0;
+
ide_raise_irq(dev, channel);
}
return 0;
}
+
+static int atapi_update_data_buf(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ switch (drive->cd_state.atapi_cmd) {
+ case 0x28: // read(10)
+ case 0xa8: // read(12)
+
+ // Update lba address to point to next block
+ drive->cd_state.current_lba++;
+
+ // read the next block
+ return atapi_read_chunk(dev, channel);
+
+ default:
+ PrintError("Unhandled ATAPI command in update buffer %x\n", drive->cd_state.atapi_cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) {
struct ide_drive * drive = get_selected_drive(channel);
struct atapi_read10_cmd * cmd = (struct atapi_read10_cmd *)(drive->data_buf);
atapi_cmd_nop(dev, channel);
return 0;
}
-
+
if ((lba + xfer_len) > drive->cd_ops->get_capacity(drive->private_data)) {
atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_LOG_BLK_OOR);
- }
+ ide_raise_irq(dev, channel);
+ return 0;}
+
+ // PrintDebug("Reading %d blocks from LBA 0x%x\n", xfer_len, lba);
drive->cd_state.current_lba = lba;
+ // Update the request length value in the cylinder registers
if (atapi_read_chunk(dev, channel) == -1) {
PrintError("IDE: Could not read initial chunk from CD\n");
- return -1;
+ return -1;
}
- drive->transfer_length = xfer_len;
+ drive->transfer_length = xfer_len * ATAPI_BLOCK_SIZE;
drive->transfer_index = 0;
- channel->status.busy = 0;
- channel->status.data_req = 1;
- channel->status.error = 0;
+ // Length of ATAPI buffer sits in cylinder registers
+ // This is weird... The host sets this value to say what it would like to transfer,
+ // if it is larger than the correct size, the device shrinks it to the correct size
+ if (atapi_update_req_len(dev, channel, ATAPI_BLOCK_SIZE) == -1) {
+ PrintError("Could not update initial request length\n");
+ return -1;
+ }
ide_raise_irq(dev, channel);
static void atapi_req_sense(struct vm_device * dev, struct ide_channel * channel) {
struct ide_drive * drive = get_selected_drive(channel);
- drive->transfer_length = 18;
- drive->transfer_index = 0;
memcpy(drive->data_buf, drive->cd_state.sense.buf, sizeof(drive->cd_state.sense.buf));
+ atapi_setup_cmd_resp(dev, channel, 18);
+}
+
+
+
+static int atapi_get_capacity(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ struct atapi_rd_capacity_resp * resp = (struct atapi_rd_capacity_resp *)(drive->data_buf);
+ uint32_t capacity = drive->cd_ops->get_capacity(drive->private_data);
+
+ resp->lba = le_to_be_32(capacity);
+ resp->block_len = le_to_be_32(ATAPI_BLOCK_SIZE);
+
+ atapi_setup_cmd_resp(dev, channel, sizeof(struct atapi_rd_capacity_resp));
+
+ return 0;
+}
+
+
+static int atapi_read_toc(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ struct atapi_rd_toc_cmd * cmd = (struct atapi_rd_toc_cmd *)(drive->data_buf);
+ uint16_t alloc_len = be_to_le_16(cmd->alloc_len);
+
+
+ struct atapi_rd_toc_resp * resp = (struct atapi_rd_toc_resp *)(drive->data_buf);
+
+ resp->data_len = 10;
+
+ return -1;
+
+}
+
+
+static int atapi_mode_sense_cur_values(struct vm_device * dev, struct ide_channel * channel,
+ struct atapi_mode_sense_cmd * sense_cmd) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ struct atapi_mode_sense_hdr * hdr = (struct atapi_mode_sense_hdr *)(drive->data_buf);
+ uint_t resp_len = sizeof(struct atapi_mode_sense_hdr);
+ uint16_t alloc_len = be_to_le_16(sense_cmd->alloc_len);
+ PrintDebug("Page Code: %x\n", sense_cmd->page_code);
+ PrintDebug("Alloc len: %d\n", alloc_len);
+
+ switch (sense_cmd->page_code) {
+
+ case 0x01: { // error recovery
+ struct atapi_error_recovery * err = NULL;
+ err = (struct atapi_error_recovery *)(drive->data_buf +
+ sizeof(struct atapi_mode_sense_hdr));
+
+ memcpy(err, &(drive->cd_state.err_recovery), sizeof(struct atapi_error_recovery));
+
+ resp_len += sizeof(struct atapi_error_recovery);
+
+ hdr->mode_data_len = le_to_be_16(resp_len - 2);
+
+ break;
+ }
+ case 0x2a: { // CDROM caps and mech. status
+ struct atapi_cdrom_caps * caps = NULL;
+ caps = (struct atapi_cdrom_caps *)(drive->data_buf + sizeof(struct atapi_mode_sense_hdr));
+
+
+ memset(caps, 0, sizeof(struct atapi_cdrom_caps));
+
+ resp_len += sizeof(struct atapi_cdrom_caps);
+
+ hdr->mode_data_len = le_to_be_16(resp_len - 2);
+
+ caps->page_code = 0x2a;
+ caps->page_len = 0x12;
+ caps->mode2_form1 = 1;
+ caps->mode2_form2 = 1;
+ caps->multisession = 1;
+ caps->isrc = 1;
+ caps->upc = 1;
+
+ /* JRL TODO: These are dynamic caps */
+ caps->lock = 1;
+ caps->lock_state = 0;
+ caps->eject = 1;
+
+ caps->lmt = 1;
+ caps->obsolete1 = le_to_be_16(0x2c2);
+ caps->num_vols_supp = le_to_be_16(2);
+
+ caps->lun_buf_size = le_to_be_16(512);
+ caps->obsolete2 = le_to_be_16(0x2c2);
+
+ break;
+ }
+ case 0x0d:
+ case 0x0e:
+ case 0x3f:
+ default:
+ PrintError("ATAPI: Mode sense Page Code not supported (%x)\n", sense_cmd->page_code);
+ atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_INV_CMD_FIELD);
+ ide_raise_irq(dev, channel);
+ return 0;
+ }
+
+
+ // We do this after error checking, because its only valid if everything worked
+ memset(hdr, 0, sizeof(struct atapi_mode_sense_hdr));
+ hdr->media_type_code = 0x70;
+
+ PrintDebug("resp_len=%d\n", resp_len);
+
+ drive->transfer_length = (resp_len > alloc_len) ? alloc_len : resp_len;
+ drive->transfer_index = 0;
+ atapi_update_req_len(dev, channel, drive->transfer_length);
+
ide_raise_irq(dev, channel);
+
+ return 0;
}
+
+static int atapi_mode_sense(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ struct atapi_mode_sense_cmd * sense_cmd = (struct atapi_mode_sense_cmd *)(drive->data_buf);
+
+ switch (sense_cmd->page_ctrl) {
+ case 0x00: // Current values
+ return atapi_mode_sense_cur_values(dev, channel, sense_cmd);
+ case 0x01: // Changeable values
+ case 0x02: // default values
+ case 0x03: // saved values
+ default:
+ PrintError("ATAPI: Mode sense mode not supported (%x)\n", sense_cmd->page_ctrl);
+ return -1;
+ }
+ return 0;
+}
+
+
static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * channel) {
struct ide_drive * drive = get_selected_drive(channel);
- uint8_t command = drive->data_buf[0];
+ uint8_t cmd = drive->data_buf[0];
- PrintDebug("IDE: ATAPI Command %x\n", command);
+ PrintDebug("IDE: ATAPI Command %x\n", cmd);
- switch (command) {
+ drive->cd_state.atapi_cmd = cmd;
+
+ switch (cmd) {
case 0x00: // test unit ready
atapi_cmd_nop(dev, channel);
case 0x28: // read(10)
if (atapi_read10(dev, channel) == -1) {
- PrintError("IDE: Error in ATAPI read (%x)\n", command);
+ PrintError("IDE: Error in ATAPI read (%x)\n", cmd);
return -1;
}
+ break;
+ case 0x5a: // mode sense
+ if (atapi_mode_sense(dev, channel) == -1) {
+ PrintError("IDE: Error in ATAPI mode sense (%x)\n", cmd);
+ return -1;
+ }
break;
+
+
+ case 0x25: // read cdrom capacity
+ if (atapi_get_capacity(dev, channel) == -1) {
+ PrintError("IDE: Error getting CDROM capacity (%x)\n", cmd);
+ return -1;
+ }
+ break;
+
+
+ case 0x43: // read TOC
+ if (atapi_read_toc(dev, channel) == -1) {
+ PrintError("IDE: Error getting CDROM TOC (%x)\n", cmd);
+ return -1;
+ }
+ break;
+
case 0xa8: // read(12)
case 0x1b: // start/stop drive
case 0xbd: // mechanism status
- case 0x5a: // mode sense
case 0x12: // inquiry
- case 0x25: // read cdrom capacity
+
case 0xbe: // read cd
- case 0x43: // read TOC
+
case 0x2b: // seek
case 0x46: // ???
case 0x4a: // ???
default:
- PrintError("Unhandled ATAPI command %x\n", command);
+ PrintError("Unhandled ATAPI command %x\n", cmd);
atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_INV_CMD_FIELD);
ide_raise_irq(dev, channel);
return -1;
drive->transfer_length = 512;
drive->transfer_index = 0;
+
+
memset(drive_id->buf, 0, sizeof(drive_id->buf));
drive_id->fixed_drive = 1;
struct ide_cd_state {
struct atapi_sense_data sense;
uint_t current_lba;
+ uint8_t atapi_cmd;
+ struct atapi_error_recovery err_recovery;
};
struct ide_hd_state {
// calculated for easy access
uint_t transfer_length;
+
// We have a local data buffer that we use for IO port accesses
uint8_t data_buf[DATA_BUFFER_SIZE];
+
void * private_data;
+
+ union {
+ uint8_t sector_count; // 0x1f2,0x172
+ struct atapi_irq_flags irq_flags;
+ } __attribute__((packed));
- uint8_t sector_count; // 0x1f2,0x172
uint8_t sector_num; // 0x1f3,0x173
+
union {
uint16_t cylinder;
+
struct {
uint8_t cylinder_low; // 0x1f4,0x174
uint8_t cylinder_high; // 0x1f5,0x175
} __attribute__((packed));
+
+ // The transfer length requested by the CPU
+ uint16_t req_len;
} __attribute__((packed));
-static inline uint32_t be_to_le_16(const uint16_t val) {
+static inline uint16_t be_to_le_16(const uint16_t val) {
uint8_t * buf = (uint8_t *)&val;
return (buf[0] << 8) | (buf[1]) ;
}
+static inline uint16_t le_to_be_16(const uint16_t val) {
+ return be_to_le_16(val);
+}
+
static inline uint32_t be_to_le_32(const uint32_t val) {
uint8_t * buf = (uint8_t *)&val;
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
+static inline uint32_t le_to_be_32(const uint32_t val) {
+ return be_to_le_32(val);
+}
+
static inline int get_channel_index(ushort_t port) {
if (((port & 0xfff8) == 0x1f0) ||
memset(drive->data_buf, 0, sizeof(drive->data_buf));
- drive->transfer_length = 0;
drive->transfer_index = 0;
// Send the reset signal to the connected device callbacks
}
-
-
// Include the ATAPI interface handlers
#include "atapi.h"
-
-
static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
struct ide_channel * channel = get_selected_channel(ide, port);
struct ide_channel * channel = get_selected_channel(ide, port);
struct ide_drive * drive = get_selected_drive(channel);
- PrintDebug("IDE: Writing Data Port %x (val=%x, len=%d)\n", port, *(uint32_t *)src, length);
+ // PrintDebug("IDE: Writing Data Port %x (val=%x, len=%d)\n",
+ // port, *(uint32_t *)src, length);
memcpy(drive->data_buf + drive->transfer_index, src, length);
drive->transfer_index += length;
}
-static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
- struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
- struct ide_channel * channel = get_selected_channel(ide, port);
+static int read_hd_data(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) {
+ PrintError("Harddrive data port read not implemented\n");
+ return -1;
+}
+
+
+
+static int read_cd_data(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) {
struct ide_drive * drive = get_selected_drive(channel);
int data_offset = drive->transfer_index % DATA_BUFFER_SIZE;
+ int req_offset = drive->transfer_index % drive->req_len;
+
+ if (drive->cd_state.atapi_cmd != 0x28) {
+ PrintDebug("IDE: Reading CD Data (len=%d) (req_len=%d)\n", length, drive->req_len);
+ }
- PrintDebug("IDE: Reading Data Port %x\n", port);
-
- if (data_offset == DATA_BUFFER_SIZE) {
- // check for more data to transfer, if there isn't any then that's a problem...
- /*
- * if (ide_update_buffer(drive) == -1) {
- * return -1;
- * }
- */
+ if (drive->transfer_index >= drive->transfer_length) {
+ PrintError("Buffer Overrun... (xfer_len=%d) (cur_idx=%d) (post_idx=%d)\n",
+ drive->transfer_length, drive->transfer_index,
+ drive->transfer_index + length);
return -1;
}
- // check for overruns...
- // We will return the data padded with 0's
- if (drive->transfer_index + length > drive->transfer_length) {
- length = drive->transfer_length - drive->transfer_index;
- memset(dst, 0, length);
+
+ if ((data_offset == 0) && (drive->transfer_index > 0)) {
+
+ if (drive->drive_type == IDE_CDROM) {
+ if (atapi_update_data_buf(dev, channel) == -1) {
+ PrintError("Could not update CDROM data buffer\n");
+ return -1;
+ }
+ } else {
+ PrintError("IDE Harddrives not implemented\n");
+ return -1;
+ }
}
memcpy(dst, drive->data_buf + data_offset, length);
-
+
drive->transfer_index += length;
+ if ((req_offset == 0) && (drive->transfer_index > 0)) {
+ if (drive->transfer_index < drive->transfer_length) {
+ // An increment is complete, but there is still more data to be transferred...
+
+ channel->status.data_req = 1;
+
+ drive->irq_flags.c_d = 0;
+
+ // Update the request length in the cylinder regs
+ if (atapi_update_req_len(dev, channel, drive->transfer_length - drive->transfer_index) == -1) {
+ PrintError("Could not update request length after completed increment\n");
+ return -1;
+ }
+ } else {
+ // This was the final read of the request
+ channel->status.data_req = 0;
+ channel->status.ready = 1;
+
+ drive->irq_flags.c_d = 1;
+ drive->irq_flags.rel = 0;
+ }
+
+ drive->irq_flags.io_dir = 1;
+ channel->status.busy = 0;
+
+ ide_raise_irq(dev, channel);
+ }
+
+ return length;
+}
+
+
+static int read_drive_id(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ channel->status.busy = 0;
+ channel->status.ready = 1;
+ channel->status.write_fault = 0;
+ channel->status.seek_complete = 1;
+ channel->status.corrected = 0;
+ channel->status.error = 0;
+
+
+ memcpy(dst, drive->data_buf + drive->transfer_index, length);
+ drive->transfer_index += length;
if (drive->transfer_index >= drive->transfer_length) {
channel->status.data_req = 0;
}
+
+ return length;
+}
+
+
+static int ide_read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+ struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
+ struct ide_channel * channel = get_selected_channel(ide, port);
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ // PrintDebug("IDE: Reading Data Port %x (len=%d)\n", port, length);
+
+ if ((channel->cmd_reg == 0xec) ||
+ (channel->cmd_reg == 0xa1)) {
+ return read_drive_id((uint8_t *)dst, length, dev, channel);
+ }
+
+ if (drive->drive_type == IDE_CDROM) {
+ if (read_cd_data((uint8_t *)dst, length, dev, channel) == -1) {
+ PrintError("IDE: Could not read CD Data\n");
+ return -1;
+ }
+ } else if (drive->drive_type == IDE_DISK) {
+ if (read_hd_data((uint8_t *)dst, length, dev, channel) == -1) {
+ PrintError("IDE: Could not read HD Data\n");
+ return -1;
+ }
+ } else {
+ memset((uint8_t *)dst, 0, length);
+ }
return length;
}
&read_port_std, &write_port_std);
v3_dev_hook_io(dev, PRI_DATA_PORT,
- &read_data_port, &write_data_port);
+ &ide_read_data_port, &write_data_port);
v3_dev_hook_io(dev, PRI_FEATURES_PORT,
&read_port_std, &write_port_std);
v3_dev_hook_io(dev, PRI_SECT_CNT_PORT,
&read_port_std, &write_port_std);
v3_dev_hook_io(dev, SEC_DATA_PORT,
- &read_data_port, &write_data_port);
+ &ide_read_data_port, &write_data_port);
v3_dev_hook_io(dev, SEC_FEATURES_PORT,
&read_port_std, &write_port_std);
v3_dev_hook_io(dev, SEC_SECT_CNT_PORT,
case 0xec: // IDENTIFY DEVICE
case 0xa1:
{
-
-
controller->status.busy = 0;
controller->status.drive_ready = 1;
controller->status.write_fault = 0;
controller->status.seek_complete = 1;
controller->status.corrected_data = 0;
controller->status.err = 0;
-
+
/*
value32 = controller->buffer[index];
index++;
if (controller->buffer_index >= 512) {
controller->status.drq = 0;
}
-
+
return length;
}
case 0xa0: //send packet cmd