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
[palacios.git] / palacios / src / devices / atapi.h
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;