Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


updates to ATAPI packet command support
Jack Lange [Tue, 24 Mar 2009 23:34:16 +0000 (18:34 -0500)]
palacios/src/devices/apic.c
palacios/src/devices/atapi-types.h
palacios/src/devices/atapi.h
palacios/src/devices/ide.c
palacios/src/devices/ramdisk.c

index a26f231..13ead3c 100644 (file)
@@ -288,8 +288,6 @@ static int get_highest_isr(struct apic_state * apic) {
            for (j = 7; j >= 0; j--) {
                uchar_t flag = 0x1 << j;
                if ((*svc_major) & flag) {
-
-
                    return ((i * 8) + j);
                }
            }
@@ -312,8 +310,6 @@ static int get_highest_irr(struct apic_state * apic) {
            for (j = 7; j >= 0; j--) {
                uchar_t flag = 0x1 << j;
                if ((*req_major) & flag) {
-
-
                    return ((i * 8) + j);
                }
            }
index 1f4df14..849a151 100644 (file)
@@ -39,6 +39,14 @@ typedef enum  {
     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 {
@@ -75,6 +83,184 @@ struct atapi_read10_cmd {
 } __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
index e10d4e8..30d829f 100644 (file)
  */
 
 #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);
@@ -40,16 +100,27 @@ static void atapi_cmd_error(struct vm_device * dev, struct ide_channel * 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);
 }
 
@@ -68,6 +139,28 @@ static int atapi_read_chunk(struct vm_device * dev, struct ide_channel * 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);
@@ -82,25 +175,33 @@ static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) {
        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);
 
@@ -112,20 +213,153 @@ static int atapi_read10(struct vm_device * dev, struct ide_channel * 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);
 
@@ -139,21 +373,43 @@ static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * chan
 
        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
@@ -176,7 +432,7 @@ static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * chan
        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;
@@ -193,6 +449,8 @@ static void atapi_identify_device(struct ide_drive * drive) {
 
     drive->transfer_length = 512;
     drive->transfer_index = 0;
+
+
     memset(drive_id->buf, 0, sizeof(drive_id->buf));
 
     drive_id->fixed_drive = 1;
index 0d6e0b2..233470a 100644 (file)
@@ -67,6 +67,8 @@ static inline const char * device_type_to_str(v3_ide_dev_type_t type) {
 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 {
@@ -98,19 +100,30 @@ struct ide_drive {
     // 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));
 
 
@@ -145,17 +158,25 @@ struct ide_internal {
 
 
 
-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) ||
@@ -203,7 +224,6 @@ static void drive_reset(struct ide_drive * drive) {
 
 
     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
@@ -244,14 +264,10 @@ static void ide_abort_command(struct vm_device * dev, struct ide_channel * chann
 }
 
 
-
-
 // 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);
@@ -318,7 +334,8 @@ static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_d
     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;
@@ -346,40 +363,127 @@ static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_d
 }
 
 
-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;
 }
@@ -610,7 +714,7 @@ static int init_ide(struct vm_device * dev) {
                   &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, 
@@ -631,7 +735,7 @@ static int init_ide(struct vm_device * dev) {
                   &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, 
index 3595c36..c7668de 100644 (file)
@@ -498,15 +498,13 @@ static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_de
        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++;
@@ -531,7 +529,7 @@ static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_de
                if (controller->buffer_index >= 512) {
                    controller->status.drq = 0;
                }
-      
+               
                return length;
            }
        case 0xa0: //send packet cmd