]> xenbits.xen.org Git - xenclient/ioemu-pq.git/commitdiff
Merge atapi-passthrough and atapi-pt-write
authorAndrew McNeil <andrewmcn@bob.uk.xensource.com>
Tue, 15 Sep 2009 11:23:24 +0000 (12:23 +0100)
committerAndrew McNeil <andrewmcn@bob.uk.xensource.com>
Tue, 15 Sep 2009 11:23:24 +0000 (12:23 +0100)
master/atapi-pass-through
master/series

index 1bc525c2a0cd295bd9da6097a289baa7b316032c..4c1889fac81fbf20cd7c3d0c43fdbe96b0f23c14 100644 (file)
@@ -105,11 +105,13 @@ index 0000000..f9bdee9
 +#endif /* !BLOCK_RAW_POSIX_H */
 diff --git a/hw/atapi-pt.c b/hw/atapi-pt.c
 new file mode 100644
-index 0000000..413cc47
+index 0000000..47ee6af
 --- /dev/null
 +++ b/hw/atapi-pt.c
-@@ -0,0 +1,1427 @@
+@@ -0,0 +1,1163 @@
++#include <utime.h>
 +#include <sys/stat.h>
++#include <fcntl.h>
 +
 +#undef DEBUG_IDE_ATAPI_PT
 +
@@ -591,6 +593,183 @@ index 0000000..413cc47
 +}
 +
 +
++/* For each SCSI command we need to know up to 3 data sizes. These are:
++ * 1. The amount of data to send to the LU.
++ * 2. The size of the buffer provided for data sent back from the LU.
++ * 3. The amount of data the LU wanted to send.
++ *
++ * These are all measured in bytes.
++ *
++ * The table ide_atapi_cmd_data_sizes specifies how to determine these sizes for
++ * each SCSI command. Each size is given by:
++ *
++ *   constant + (base * block_size)
++ *
++ * where base is a value specified within the command data. This is specified in
++ * our table as an offset into the data at which the value starts and the size of
++ * the value, in bytes. All base value are assumed to be MSB first (lowest offset).
++ *
++ * The number of bytes forming the base value can only take values between 0 and 4
++ * inclusive, with 0 indicating that there is no base value.
++ *
++ * The amount of data to send to the LU and the size of the receiving buffer are
++ * both determined from the CDB. A value of 0 means that no data is transfered and
++ * a value of -1 indicates that the table entry is invalid (eg undefined command).
++ *
++ * The amount of data the LU wanted to return is determined from the returned data.
++ * A value of 0 is invalid and a value of -1 indicates that this size is the same
++ * as the receiving buffer size.
++ *
++ * A few commands are too complex for this scheme and so are handled by code in
++ * ide_atapi_pt_cmd() and ide_atapi_pt_do_sg_io().
++ */
++static struct
++{
++    int dout_len_const;    // size of data to send to LU, dout
++    int dout_len_offset;
++    int dout_len_size;
++    int dout_block_size;
++
++    int alloc_len_const;   // size of buffer to receive din
++    int alloc_len_offset;
++    int alloc_len_size;
++    int alloc_block_size;
++
++    int din_len_const;     // size of data LU wanted to send, din
++    int din_len_offset;
++    int din_len_size;
++    int din_block_size;
++} atapi_data_sizes[256] = {
++    // dout        buffer       din
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x00 GPCMD_TEST_UNIT_READY
++    {-1},{-1},                                // 0x01-0x02
++    {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0},  // 0x03 GPCMD_REQUEST_SENSE
++    {12, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x04 GPCMD_FORMAT_UNIT
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x05-0x0c
++    {-1},{-1},{-1},{-1},{-1},                 // 0x0d-0x11
++    { 0, 0, 0, 0,  0, 4, 1, 1,  5, 4, 1, 1},  // 0x12 GPCMD_INQUIRY
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x13-0x1a
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x1b GPCMD_START_STOP_UNIT
++    {-1},{-1},                                // 0x1c-0x1d
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x1e GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL
++    {-1},{-1},{-1},{-1},                      // 0x1f-0x20
++    { 0, 0, 0, 0,  0, 7, 2, 1,  4, 3, 1, 1},  // 0x23 GPCMD_READ_FORMAT_CAPACITIES
++    {-1},                                     // 0x24
++    { 0, 0, 0, 0,  8, 0, 0, 0,  8, 0, 0, 0},  // 0x25 GPCMD_READ_CDVD_CAPACITY
++    {-1},{-1},                                // 0x26-0x27
++    { 0, 0, 0, 0,  0, 7, 2, CD_FRAMESIZE,
++                               -1, 0, 0, 0},  // 0x28 GPCMD_READ_10
++    {-1},                                     // 0x29
++    { 0, 7, 2, CD_FRAMESIZE,
++                   0, 0, 0, 0 , 0, 0, 0, 0},  // 0x2a GPCMD_WRITE_10
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x2b GPCMD_SEEK
++    {-1},{-1},                                // 0x2c-0x2d
++    { 0, 7, 2, CD_FRAMESIZE,
++                   0, 0, 0, 0,  0, 0, 0, 0},  // 0x2e GPCMD_WRITE_AND_VERIFY_10
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x2f GPCMD_VERIFY_10
++    {-1},{-1},{-1},{-1},{-1},                 // 0x30-0x34
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x35 GPCMD_FLUSH_CACHE
++    {-1},{-1},{-1},{-1},{-1},                 // 0x36-0x3a
++    { 0, 6, 3, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x3b GPCMD_WRITE_BUFFER
++    { 0, 0, 0, 0,  0, 6, 3, 1,  4, 1, 3, 1},  // 0x3c GPCMD_READ_BUFFER
++    {-1},{-1},{-1},{-1},{-1},                 // 0x3d-0x41
++    { 0, 0, 0, 0,  0, 7, 2, 1,  4, 2, 2, 1},  // 0x42 GPCMD_READ_SUBCHANNEL
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x43 GPCMD_READ_TOC_PMA_ATIP
++    { 0, 0, 0, 0,  0, 7, 2, 1,  8, 0, 0, 0},  // 0x44 GPCMD_READ_HEADER
++    { 0, 0, 0, 0,  0, 7, 2, CD_FRAMESIZE,
++                               -1, 0, 0, 0},  // 0x45 GPCMD_PLAY_AUDIO_10
++    { 0, 0, 0, 0,  0, 7, 2, 1,  4, 0, 4, 1},  // 0x46 GPCMD_GET_CONFIGURATION
++    { 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0},  // 0x47 GPCMD_PLAY_AUDIO_MSF
++    {-1},{-1},                                // 0x48-0x49
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x4a GPCMD_GET_EVENT_STATUS_NOTIFICATION
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x4b GPCMD_PAUSE_RESUME
++    {-1},{-1},                                // 0x4c-0x4d
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x4e GPCMD_STOP_PLAY_SCAN
++    {-1},{-1},                                // 0x4f-0x50
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x51 GPCMD_READ_DISC_INFO
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x52 GPCMD_READ_TRACK_RZONE_INFO
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x53 GPCMD_RESERVE_RZONE_TRACK
++    { 0, 7, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x54 GPCMD_SEND_OPC
++    { 0, 7, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x55 GPCMD_MODE_SELECT_10
++    {-1},{-1},                                // 0x56-0x57
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x58 GPCMD_REPAIR_RZONE_TRACK
++    {-1},                                     // 0x59
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x5a GPCMD_MODE_SENSE_10
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x5b GPCMD_CLOSE_TRACK
++    { 0, 0, 0, 0,  0, 7, 2, 1,  2, 0, 2, 1},  // 0x5c GPCMD_READ_BUFFER_CAPACITY
++    { 0, 6, 3, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0x5d GPCMD_SEND_CUE_SHEET
++    {-1},{-1},                                // 0x5e-0x5f
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x60-0x67
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x68-0x6f
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x70-0x77
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x78-0x7f
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x80-0x87
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x88-0x8f
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x90-0x97
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0x98-0x9f
++    {-1},                                     // 0xa0
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xa1 GPCMD_BLANK
++    { 0, 8, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xa2 GPCMD_SEND_EVENT
++    { 0, 8, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xa3 GPCMD_SEND_KEY
++    { 0, 0, 0, 0,  0, 8, 2, 1,  2, 0, 2, 1},  // 0xa4 GPCMD_REPORT_KEY
++    {-1},                                     // 0xa5
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xa6 GPCMD_LOAD_UNLOAD
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xa7 GPCMD_SET_READ_AHEAD
++    { 0, 0, 0, 0,  0, 6, 4, CD_FRAMESIZE,
++                               -1, 0, 0, 0},  // 0xa8 GPCMD_READ_12
++    {-1},                                     // 0xa9
++    { 0, 6, 4, CD_FRAMESIZE,
++                   0, 0, 0, 0,  0, 0, 0, 0},  // 0xaa GPCMD_WRITE_12
++    {-1},                                     // 0xab
++    { 0, 0, 0, 0,  0, 8, 2, 1,  4, 0, 4, 1},  // 0xac GPCMD_GET_PERFORMANCE
++    { 0, 0, 0, 0,  0, 8, 2, 1,  2, 0, 2, 1},  // 0xad GPCMD_READ_DVD_STRUCTURE
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xae-0xb5
++    { 0, 9, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xb6 GPCMD_SET_STREAMING
++    {-1},{-1},                                // 0xb7-0xb8
++    { 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0},  // 0xb9 GPCMD_READ_CD_MSF
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xba GPCMD_SCAN
++    { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xbb GPCMD_SET_SPEED - Not in docs
++    {-1},                                     // 0xbc
++    { 0, 0, 0, 0,  0, 8, 2, 1,  8, 6, 2, 1},  // 0xbd GPCMD_MECHANISM_STATUS
++    { 0, 0, 0, 0,  0, 6, 3, 1, -1, 0, 0, 0},  // 0xbe GPCMD_READ_CD
++    { 0, 8, 2, 1,  0, 0, 0, 0,  0, 0, 0, 0},  // 0xbf GPCMD_SEND_DVD_STRUCTURE
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xc0-0xc7
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xc8-0xcf
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xd0-0xd7
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xd8-0xdf
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xe0-0xe7
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xe8-0xef
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},  // 0xf0-0xf7
++    {-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1}   // 0xf8-0xff
++};
++
++// TODO: check these commands:
++//# define GPCMD_PLAY_CD                          0xbc
++//# define GPCMD_PLAYAUDIO_TI             0x48
++//# define GPCMD_GET_MEDIA_STATUS                 0xda
++
++
++static uint32_t ide_atapi_pt_read_field_at_offset(uint8_t *data, int offset, int field_size)
++{
++    switch(field_size)
++    {
++    case 0:
++        return 0;
++    case 1:
++        return data[offset];
++    case 2:
++        return ube16_to_cpu(data + offset);
++    case 3:
++        return ube24_to_cpu(data + offset);
++    case 4:
++        return ube32_to_cpu(data + offset);
++    default:
++        assert(0);
++        return -1;
++    }
++}
++
++
 +static void ide_atapi_pt_set_error(IDEState *s, int sense_key, int asc, int error)
 +{
 +    s->atapi_pt.sense.sense_key  = sense_key;
@@ -608,124 +787,173 @@ index 0000000..413cc47
 +    ide_set_irq(s);
 +}
 +
-+#if 0
-+static int cmd_count = 0;
-+static int finish_count = 0;
-+static int no_finish_count = 0;
-+
-+static void ide_atapi_pt_sg_io_finished(IDEState *s)
++static void ide_atapi_pt_do_sg_io(IDEState *s)
 +{
++    struct sg_io_v4 *cmd = &s->atapi_pt.cmd;
 +    BDRVRawState *raw_state = s->bs->opaque;
-+    int read_bytes;
-+    int i;
++    int r;
++    uint8_t cmd_code = s->atapi_pt.request[0];
++    uint32_t din_desired;
 +
-+    ++finish_count;
-+    DEBUG_PRINTF("****  finish   (%p, u=%p, raw=%p, fd=%d, %d-%d(%d))\n",
-+                 s, s->atapi_pt.cmd.usr_ptr,
-+                 raw_state, raw_state->fd, cmd_count,
-+                 finish_count + no_finish_count, no_finish_count);
++    assert(cmd->din_xfer_len != (__u32)-1);
 +
-+    DEBUG_PRINTF("Preread:  0x");
-+    for(i = 0; i < sizeof(s->atapi_pt.cmd); ++i)
-+        DEBUG_PRINTF("%02x", ((unsigned char *)&s->atapi_pt.cmd)[i]);
-+    DEBUG_PRINTF("\n");
++    s->atapi_pt.sense.error_code = 0;
++    s->atapi_pt.sense.sense_key = 0;
++    s->atapi_pt.sense.asc = 0;
++    s->atapi_pt.sense.ascq = 0;
 +
-+#if 1
-+    read_bytes = read(raw_state->fd, &s->atapi_pt.cmd, sizeof (s->atapi_pt.cmd));
 +
-+    DEBUG_PRINTF("Postread: 0x");
-+    for(i = 0; i < sizeof(s->atapi_pt.cmd); ++i)
-+        DEBUG_PRINTF("%02x", ((unsigned char *)&s->atapi_pt.cmd)[i]);
-+    DEBUG_PRINTF("\n");
-+#else
-+    DEBUG_PRINTF("Postread: 0x");
-+    for(i = 0; i < sizeof (s->atapi_pt.cmd); ++i) {
-+        read(raw_state->fd, &((unsigned char *)&s->atapi_pt.cmd)[i], 1);
-+        DEBUG_PRINTF("%02x", ((unsigned char *)&s->atapi_pt.cmd)[i]);
-+    }
-+    DEBUG_PRINTF("\n");
-+    read_bytes = i;
-+#endif
-+
-+    //    DEBUG_PRINTF("finish read\n");
++    /* Send command and wait for reply, SG_IO ioctl*/
++    r = ioctl(raw_state->fd, 0x2285, cmd);
 +
-+    if (read_bytes != sizeof (s->atapi_pt.cmd))
++    if(s->atapi_pt.request[0] == GPCMD_GET_EVENT_STATUS_NOTIFICATION)
 +    {
-+        ide_atapi_pt_error(s);
-+        s->atapi_pt.cmd_sent = NULL;
-+        return;
++        struct stat file_stat;
++        int fd;
++
++        if(s->io_buffer[2] == 4 && s->io_buffer[4] == 2)
++        {
++            /* This is a "new media" message, tell any other VMs */
++            DEBUG_PRINTF("[ATAPI] new media detected\n");
++
++            fd = open(IDE_ATAPI_PT_NEW_CD_FILE, O_WRONLY | O_TRUNC | O_CREAT, 0666);
++            if(fd < 0) {
++                DEBUG_PRINTF("Error touching new CD file\n");
++            } else {
++                close(fd);
++                utime(IDE_ATAPI_PT_NEW_CD_FILE, NULL);
++            }
++
++            if(stat(IDE_ATAPI_PT_NEW_CD_FILE, &file_stat) != 0)
++                DEBUG_PRINTF("Error writing to new CD file\n");
++            else
++                s->atapi_pt.new_cd_time = file_stat.st_ctime;
++        }
++
++        if(s->io_buffer[2] == 4 && s->io_buffer[4] == 3)
++        {
++            /* This is a "media removed" message, tell any other VMs */
++            DEBUG_PRINTF("[ATAPI] media removed\n");
++
++            fd = open(IDE_ATAPI_PT_NEW_CD_FILE, O_WRONLY | O_TRUNC | O_CREAT, 0666);
++            if(fd < 0) {
++                DEBUG_PRINTF("Error touching new CD file\n");
++            } else {
++                close(fd);
++                utime(IDE_ATAPI_PT_NEW_CD_FILE, NULL);
++            }
++
++            if(stat(IDE_ATAPI_PT_EJECT_CD_FILE, &file_stat) != 0)
++                DEBUG_PRINTF("Error writing to eject CD file\n");
++            else
++                s->atapi_pt.eject_time = file_stat.st_ctime;
++        }
++
++        if((s->io_buffer[2] == 4 && s->io_buffer[4] == 0 && s->io_buffer[5] == 2) ||
++           (s->io_buffer[4] == 0 && s->io_buffer[5] == 0 &&
++            s->io_buffer[6] == 0 && s->io_buffer[7] == 0))
++        {
++            /* This is a no activity message we can hijack if we need to */
++
++            if(stat(IDE_ATAPI_PT_NEW_CD_FILE, &file_stat) == 0)
++            {
++                /* There's been a new media message that we haven't seen yet */
++                DEBUG_PRINTF("[ATAPI] new media message spotted\n");
++                s->atapi_pt.new_cd_time = file_stat.st_ctime;
++
++                s->io_buffer[2] = 4;
++                s->io_buffer[4] = 2;
++                s->io_buffer[5] = 2;
++                s->io_buffer[6] = 0;
++                s->io_buffer[7] = 0;
++            }
++            else if(stat(IDE_ATAPI_PT_EJECT_CD_FILE, &file_stat) == 0 &&
++               s->atapi_pt.eject_time < file_stat.st_ctime)
++            {
++                /* There's been an eject message that we haven't seen yet */
++                DEBUG_PRINTF("[ATAPI] media removed message spotted\n");
++                s->atapi_pt.eject_time = file_stat.st_ctime;
++
++                s->io_buffer[2] = 4;
++                s->io_buffer[4] = 3;
++                s->io_buffer[5] = 1;
++                s->io_buffer[6] = 0;
++                s->io_buffer[7] = 0;
++            }
++        }
 +    }
 +
-+    if (s->atapi_pt.cmd.driver_status ||
-+        s->atapi_pt.cmd.transport_status ||
-+        s->atapi_pt.cmd.device_status)
-+    {
-+        DEBUG_PRINTF("[\e[1;31mERROR\e[m]\n"
-+                     "\tsense_key: 0x%02x (\e[0;35m%s\e[m)\n"
-+                     "\terror: 0x%02x\n"
-+                     "\tasc: 0x%02x, 0x%x (\e[0;35m%s\e[m)\n"
-+                     "\terrno: %d (%s)\n"
-+                     "\tdriver: %d, transport: %d, device: %d\n",
++    if(r || cmd->driver_status || cmd->transport_status ||
++       cmd->device_status) {
++        DEBUG_PRINTF("[\e[1;31mERROR\e[m] (%s)  sense: 0x%02x,%02x,%02x (%s)\n",
++                     atapi_cmd_to_str(s->atapi_pt.request[0]),
 +                     s->atapi_pt.sense.sense_key,
-+                     sense_key_texts[s->atapi_pt.sense.sense_key],
-+                     s->atapi_pt.sense.error_code,
 +                     s->atapi_pt.sense.asc,
 +                     s->atapi_pt.sense.ascq,
 +                     atapi_sense_to_str(s->atapi_pt.sense.sense_key,
 +                                        s->atapi_pt.sense.asc,
-+                                        s->atapi_pt.sense.ascq),
-+                     errno,
-+                     strerror(errno) ? : "(null)",
-+                     s->atapi_pt.cmd.driver_status,
-+                     s->atapi_pt.cmd.transport_status,
-+                     s->atapi_pt.cmd.device_status);
++                                        s->atapi_pt.sense.ascq));
 +        ide_atapi_pt_error(s);
-+        s->atapi_pt.cmd_sent = NULL;
 +        return;
 +    }
-+    if(s->atapi_pt.cmd_sent == NULL) {
-+        static int null_count = 0;
-+        ++null_count;
-+        DEBUG_PRINTF("\e[1;31m%s called with cmd_sent = NULL %d time%c\e[m\n",
-+                     __FUNCTION__, null_count, (null_count == 1) ? ' ' : 's');
++
++    if(cmd->din_xfer_len == 0)
++    {
++        // Nothing else to do
 +        ide_atapi_cmd_ok(s);
-+        s->atapi_pt.cmd_sent = NULL;
 +        return;
 +    }
-+    //    DEBUG_PRINTF("finish sent\n");
-+    s->atapi_pt.cmd_sent(s);
-+    s->atapi_pt.cmd_sent = NULL;
-+    DEBUG_PRINTF("****  finished (%p)\n", s);
-+}
 +
-+#define ATAPI_PT_SEND_PACKET                                            \
-+  do {                                                                  \
-+        BDRVRawState *raw_state = s->bs->opaque;                        \
-+        DEBUG_PRINTF("[ATAPI:%d] sending command: 0x%02x (\e[0;32m%s\e[m)\n", \
-+                     raw_state->fd, s->atapi_pt.request[0],             \
-+                     atapi_cmd_to_str(s->atapi_pt.request[0]));         \
-+        memset(&s->atapi_pt.sense, 0, sizeof (s->atapi_pt.sense));      \
-+        int wrote = write(raw_state->fd, &s->atapi_pt.cmd,              \
-+                          sizeof (s->atapi_pt.cmd));                    \
-+        if (wrote != sizeof (s->atapi_pt.cmd))                          \
-+            ide_atapi_pt_error(s);                                      \
-+    } while (0)
 +
-+static void ide_atapi_pt_read_finish(IDEState *s)
-+{
-+    assert(s->atapi_pt.cmd.dout_xfer_len > 0);
-+    s->atapi_pt.cmd.dout_xferp = (__u64)s->io_buffer;
-+    s->atapi_pt.cmd_sent = ide_atapi_cmd_ok;
-+    ATAPI_PT_SEND_PACKET;
++    din_desired = atapi_data_sizes[cmd_code].din_len_const +
++        (ide_atapi_pt_read_field_at_offset(s->io_buffer,
++                                           atapi_data_sizes[cmd_code].din_len_offset,
++                                           atapi_data_sizes[cmd_code].din_len_size) *
++         atapi_data_sizes[cmd_code].din_block_size);
++
++    /* cmd->din_xfer_len is the size of the buffer provided for the din data,
++     * din_desired in the size of the data the LU tried to send to us. Either one
++     * may be bigger. */
++
++    if(s->atapi_pt.request[0] == GPCMD_READ_BUFFER)
++    {
++        switch (s->atapi_pt.request[1] & 7)
++        {
++        case 0: // data with header, as specified in atapi_data_sizes table
++            break;
++
++        case 2: // data only
++            din_desired = cmd->din_xfer_len;
++            break;
++
++        case 3: // header only
++            din_desired = 4;
++            break;
++
++        case 1: // vendor specific
++        default:
++            DEBUG_PRINTF("\e[3;31mIllegal read buffer mode %d\e[m\n",
++                         s->io_buffer[1] & 7);
++            ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
++                                   ASC_ILLEGAL_OPCODE, 0x70);
++            return;
++        }
++    }
++
++    if(din_desired == (__u32)-1)
++        din_desired = cmd->din_xfer_len;
++
++    DEBUG_PRINTF("Reply, %d in %d\n", din_desired, cmd->din_xfer_len);
++    ide_atapi_cmd_reply(s, din_desired, cmd->din_xfer_len);
 +}
 +
-+static void ide_atapi_pt_read_pio_end(IDEState *s)
++static void ide_atapi_pt_dout_fetch_pio_done(IDEState *s)
 +{
 +    ide_transfer_stop(s);
-+    ide_atapi_pt_read_finish(s);
++    ide_atapi_pt_do_sg_io(s);
 +}
 +
-+static void ide_atapi_pt_read_dma_cb(void *opaque, int ret)
++static void ide_atapi_pt_dout_fetch_dma_done(void *opaque, int ret)
 +{
 +    BMDMAState *bm = opaque;
 +    IDEState *s = bm->ide_if;
@@ -737,11 +965,9 @@ index 0000000..413cc47
 +    }
 +
 +    i = dma_buf_rw(bm, 0);
-+    ide_atapi_pt_read_finish(s);
++    ide_atapi_pt_do_sg_io(s);
 +}
-+#endif
 +
-+#if 0
 +static void ide_atapi_pt_wcmd(IDEState *s)
 +{
 +    if (s->atapi_dma)
@@ -749,7 +975,7 @@ index 0000000..413cc47
 +        /* DMA */
 +        s->io_buffer_index = 0;
 +        s->io_buffer_size = s->atapi_pt.cmd.dout_xfer_len;
-+        ide_dma_start(s, ide_atapi_pt_read_dma_cb);
++        ide_dma_start(s, ide_atapi_pt_dout_fetch_dma_done);
 +        return;
 +    }
 +
@@ -764,44 +990,10 @@ index 0000000..413cc47
 +        ~ATAPI_INT_REASON_IO &
 +        ~ATAPI_INT_REASON_CD;
 +    ide_transfer_start(s, s->io_buffer, s->atapi_pt.cmd.dout_xfer_len,
-+                       ide_atapi_pt_read_pio_end);
++                       ide_atapi_pt_dout_fetch_pio_done);
 +    ide_set_irq(s);
 +    return;
 +}
-+#endif
-+
-+static void ide_atapi_pt_read_format_capacities_sent(IDEState *s)
-+{
-+    int size = (s->io_buffer[3] << 3) + 4;
-+    ide_atapi_cmd_reply(s, size, s->atapi_pt.cmd.din_xfer_len);
-+}
-+
-+static void ide_atapi_pt_standard_reply(IDEState *s)
-+{
-+    uint32_t size = s->atapi_pt.reply_size_init;
-+
-+    switch (s->atapi_pt.reply_size_len)
-+    {
-+    case 0:
-+        break;
-+    case 1:
-+        size += s->io_buffer[s->atapi_pt.reply_size_offset];
-+        break;
-+    case 2:
-+        size += ube16_to_cpu(s->io_buffer + s->atapi_pt.reply_size_offset);
-+        break;
-+    case 3:
-+        size += ube24_to_cpu(s->io_buffer + s->atapi_pt.reply_size_offset);
-+        break;
-+    case 4:
-+        size += ube32_to_cpu(s->io_buffer + s->atapi_pt.reply_size_offset);
-+        break;
-+    default:
-+        assert(0);
-+        break;
-+    }
-+    ide_atapi_cmd_reply(s, size, s->atapi_pt.cmd.din_xfer_len);
-+}
 +
 +static int ide_atapi_pt_read_cd_block_size(const uint8_t *io_buffer)
 +{
@@ -924,221 +1116,66 @@ index 0000000..413cc47
 +    return block_size;
 +}
 +
-+
 +static void ide_atapi_pt_cmd(IDEState *s)
 +{
 +    struct sg_io_v4 *cmd = &s->atapi_pt.cmd;
-+    int r;
-+    int command;
-+    BDRVRawState *raw_state = s->bs->opaque;
++    uint8_t cmd_code;
 +
-+    memset(cmd, 0, sizeof (*cmd));
++    memset(cmd, 0, sizeof(*cmd));
 +    memcpy(s->atapi_pt.request, s->io_buffer, ATAPI_PACKET_SIZE);
++    cmd_code = s->atapi_pt.request[0];
 +    cmd->guard            = 'Q';
 +    cmd->protocol         = 0;
 +    cmd->subprotocol      = 0;
 +    cmd->request_len      = ATAPI_PACKET_SIZE;
 +    cmd->request          = (__u64)s->atapi_pt.request;
 +    cmd->response         = (__u64)&s->atapi_pt.sense;
-+    cmd->max_response_len = sizeof (s->atapi_pt.sense);
++    cmd->max_response_len = sizeof(s->atapi_pt.sense);
 +    cmd->timeout          = 15000; // 15 seconds
 +
-+    s->status                    |= BUSY_STAT;
-+    s->atapi_pt.reply_size_init   = 0;
-+    s->atapi_pt.reply_size_offset = 0;
-+    s->atapi_pt.reply_size_len    = 0;
++    s->status |= BUSY_STAT;
 +
 +    cmd->din_xferp = (__u64)s->io_buffer;
-+    cmd->dout_xferp = (__u64)(s->io_buffer + ATAPI_PACKET_SIZE);
-+    s->atapi_pt.cmd_sent = ide_atapi_cmd_ok;
-+    command = s->io_buffer[0];
-+
-+    switch (s->io_buffer[0])
-+    {
-+        /*******************/
-+        /* SIMPLE COMMANDS */
-+        /*******************/
-+
-+    case GPCMD_BLANK: // bigger timeout while blanking
-+        cmd->timeout = 1000 * 60 * 80; // 80 mins
-+        goto simple_cmd;
-+    case GPCMD_CLOSE_TRACK:
-+        cmd->timeout = 1000 * 60 * 5; // 5 mins
-+        goto simple_cmd;
-+    case GPCMD_FLUSH_CACHE: // also called SYNCHRONIZE_CACHE
-+    case GPCMD_LOAD_UNLOAD:
-+    case GPCMD_PAUSE_RESUME:
-+    case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
-+    case GPCMD_REPAIR_RZONE_TRACK:
-+    case GPCMD_RESERVE_RZONE_TRACK:
-+    case GPCMD_SCAN:
-+    case GPCMD_SEEK:
-+    case GPCMD_SET_READ_AHEAD:
-+    case GPCMD_START_STOP_UNIT:
-+    case GPCMD_STOP_PLAY_SCAN:
-+    case GPCMD_TEST_UNIT_READY:
-+    case GPCMD_VERIFY_10:
-+    case GPCMD_SET_SPEED: /* FIXME: find the documentation */
-+    simple_cmd:
-+        CHECK_SAME_VALUE(s->lcyl, 0);
-+        CHECK_SAME_VALUE(s->hcyl, 0);
-+        break;
-+
-+        /******************/
-+        /* WRITE COMMANDS */
-+        /******************/
++    cmd->dout_xferp = (__u64)s->io_buffer;
 +
-+    case GPCMD_WRITE_10:
-+    case GPCMD_WRITE_AND_VERIFY_10:
-+        cmd->dout_xfer_len = ube16_to_cpu(s->io_buffer + 7) * CD_FRAMESIZE;
++    DEBUG_PRINTF("[ATAPI] sending command: 0x%02x (\e[0;32m%s\e[m)\n",
++                 cmd_code, atapi_cmd_to_str(cmd_code));
 +
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
++    cmd->dout_xfer_len = atapi_data_sizes[cmd_code].dout_len_const +
++        (ide_atapi_pt_read_field_at_offset(s->atapi_pt.request,
++                                           atapi_data_sizes[cmd_code].dout_len_offset,
++                                           atapi_data_sizes[cmd_code].dout_len_size) *
++         atapi_data_sizes[cmd_code].dout_block_size);
 +
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        break;
-+
-+    case GPCMD_WRITE_12:
-+        cmd->dout_xfer_len = ube32_to_cpu(s->io_buffer + 6);
++    cmd->din_xfer_len = atapi_data_sizes[cmd_code].alloc_len_const +
++        (ide_atapi_pt_read_field_at_offset(s->atapi_pt.request,
++                                           atapi_data_sizes[cmd_code].alloc_len_offset,
++                                           atapi_data_sizes[cmd_code].alloc_len_size) *
++         atapi_data_sizes[cmd_code].alloc_block_size);
 +
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
 +
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+    case GPCMD_WRITE_BUFFER:
++    /* A few commands need special attention */
++    switch(cmd_code)
 +    {
-+        int32_t parameter_list_length = ube24_to_cpu(s->io_buffer + 3);
-+        int8_t mode = s->io_buffer[1] & 0x03;
-+
-+        switch (mode)
-+        {
-+        case 0x0: // Combined header and data mode
-+            // The documentation is confusing because it says that parameter
-+            // list length contains all the data, but the buffer should be
-+            // greater than parameter list length + 4...
-+            cmd->dout_xfer_len = parameter_list_length + 4;
-+            break;
-+        case 0x2: // Data mode
-+            cmd->dout_xfer_len = parameter_list_length;
-+            break;
-+        case 0x1: // Vendor specific
-+        case 0x4: // Download microcode
-+        case 0x5: // Download microcode and save mode
-+        case 0x6: // Download microcode with offsets
-+        case 0x7: // Download microcode with offsets and save mode
-+        default:
-+            goto illegal_request;
-+        }
-+
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
-+
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
++    case GPCMD_BLANK: // bigger timeout while blanking
++        cmd->timeout = 1000 * 60 * 80; // 80 mins
 +        break;
-+    }
-+
-+    case GPCMD_SEND_CUE_SHEET:
-+        cmd->dout_xfer_len = ube24_to_cpu(s->io_buffer + 6);
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
 +
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
++    case GPCMD_CLOSE_TRACK:
++        cmd->timeout = 1000 * 60 * 5; // 5 mins
 +        break;
 +
-+    case GPCMD_MODE_SELECT_10:
-+        cmd->dout_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        CHECK_SAME_VALUE(s->lcyl | (s->hcyl << 8), cmd->dout_xfer_len);
-+
-+        DEBUG_PRINTF("Mode select 10  %d bytes\n", cmd->dout_xfer_len);
++    case GPCMD_WRITE_BUFFER:
++        if((s->io_buffer[1] & 7) != 0 && // Combined header and data mode
++           (s->io_buffer[1] & 7) != 2)   // Data mode
 +        {
-+            int i;
-+            DEBUG_PRINTF("0x");
-+            for(i = 0; i < 12; ++i)
-+                DEBUG_PRINTF("%02x ", s->io_buffer[i]);
-+            DEBUG_PRINTF("\n0x");
-+            for(i = 0; i < cmd->dout_xfer_len; ++i)
-+                DEBUG_PRINTF("%02x ", s->io_buffer[i + 12]);
-+            DEBUG_PRINTF("\n");
++            DEBUG_PRINTF("\e[3;31mIllegal write buffer mode %d\e[m\n",
++                         s->io_buffer[1] & 7);
++            ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
++                                   ASC_ILLEGAL_OPCODE, 0x70);
++            return;
 +        }
 +
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+    case GPCMD_SEND_KEY:
-+    case GPCMD_SEND_EVENT:
-+        cmd->dout_xfer_len = ube16_to_cpu(s->io_buffer + 8);
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
-+
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+    case GPCMD_SEND_OPC:
-+        cmd->dout_xfer_len = ube16_to_cpu(s->io_buffer + 7) << 3;
-+        CHECK_SAME_VALUE(s->lcyl | (s->hcyl << 8), cmd->dout_xfer_len);
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
-+
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+    case GPCMD_SET_STREAMING:
-+        cmd->dout_xfer_len = ube16_to_cpu(s->io_buffer + 9);
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
-+
-+        if (cmd->dout_xfer_len == 0)
-+            goto simple_cmd;
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+    case GPCMD_FORMAT_UNIT:
-+        cmd->dout_xfer_len = 12;
-+
-+        DEBUG_PRINTF("Write (%s) %d bytes\n", atapi_cmd_to_str(command),
-+                     cmd->dout_xfer_len);
-+
-+        //        ide_atapi_pt_wcmd(s);
-+        //        return;
-+        break;
-+
-+        /*****************/
-+        /* READ COMMANDS */
-+        /*****************/
-+
-+    case GPCMD_INQUIRY:
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        cmd->din_xfer_len = s->io_buffer[4];
-+        s->atapi_pt.reply_size_init = 5;
-+        s->atapi_pt.reply_size_offset = 4;
-+        s->atapi_pt.reply_size_len = 1;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        //        ATAPI_PT_SEND_PACKET;
 +        break;
 +
 +    case GPCMD_REQUEST_SENSE:
@@ -1153,185 +1190,15 @@ index 0000000..413cc47
 +        int max_size = s->io_buffer[4];
 +
 +        int size = 8 + s->atapi_pt.sense.add_sense_len;
-+
-+        //        printf("max_size: %d, add_sense_len: %d, sizeof: %lu\n",
-+        //               max_size, s->atapi_pt.sense.add_sense_len,
-+        //             sizeof (s->atapi_pt.sense));
 +        memcpy(s->io_buffer, &s->atapi_pt.sense, sizeof (s->atapi_pt.sense));
 +        ide_atapi_cmd_reply(s, size, max_size);
 +        return;
 +    }
 +
-+    case GPCMD_READ_DVD_STRUCTURE:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 8);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_READ_HEADER:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_MECHANISM_STATUS:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 8);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_offset = 6;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_REPORT_KEY:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 8);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        s->atapi_pt.reply_size_init = 2;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_READ_BUFFER_CAPACITY:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        s->atapi_pt.reply_size_init = 2;
-+        //        return;
-+        break;
-+
-+    case GPCMD_GET_PERFORMANCE:
-+        cmd->din_xfer_len = 8 + 8 * ube16_to_cpu(s->io_buffer + 8);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 4;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_READ_10:
-+    case GPCMD_READ_12:
-+    {
-+        int blocksize = 0, nbblocks;
-+
-+        switch (s->io_buffer[0]) {
-+        case GPCMD_READ_10:
-+            blocksize = CD_FRAMESIZE;
-+            nbblocks = ube16_to_cpu(s->io_buffer + 7);
-+            break;
-+        case GPCMD_READ_12:
-+            blocksize = CD_FRAMESIZE_RAW0;
-+            nbblocks = ube32_to_cpu(s->io_buffer + 6);
-+            break;
-+        default: assert(0);
-+        }
-+        cmd->din_xfer_len = nbblocks * blocksize;
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+    }
-+
-+    case GPCMD_READ_BUFFER:
-+        // TODO check this one is correct
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        cmd->din_xfer_len = ube24_to_cpu(s->io_buffer + 6);
-+
-+        switch (s->io_buffer[1] & 0x7)
-+        {
-+        case 0: // data with header
-+            s->atapi_pt.reply_size_init = 4;
-+            s->atapi_pt.reply_size_len = 3;
-+            s->atapi_pt.reply_size_offset = 1;
-+            break;
-+
-+        case 2: // data only
-+            s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+            break;
-+
-+        case 3: // header only
-+            s->atapi_pt.reply_size_init = 4;
-+            break;
-+
-+        case 1: // vendor specific
-+        default:
-+            goto illegal_request;
-+        }
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_READ_CDVD_CAPACITY:
-+        cmd->din_xfer_len = 8;
-+        CHECK_SAME_VALUE(s->lcyl | (s->hcyl << 8), cmd->din_xfer_len);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_MODE_SENSE_10:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        CHECK_SAME_VALUE(s->lcyl | (s->hcyl << 8), cmd->din_xfer_len);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        s->atapi_pt.reply_size_init = 2;
-+        //s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
-+    case GPCMD_READ_DISC_INFO:
-+    case GPCMD_READ_TOC_PMA_ATIP:
-+    case GPCMD_READ_TRACK_RZONE_INFO:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        s->atapi_pt.reply_size_init = 2;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
-+    case GPCMD_READ_SUBCHANNEL:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_len = 2;
-+        s->atapi_pt.reply_size_offset = 2;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+
 +    case GPCMD_READ_CD:
-+    {
-+        // command fields
-+        int block_count = ((s->io_buffer[6] << 16) |
-+                           ube16_to_cpu(s->io_buffer + 7));
-+        int block_size = ide_atapi_pt_read_cd_block_size(s->io_buffer);
-+
-+        cmd->din_xfer_len = block_count * block_size;
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
++        // We read blocks, not bytes
++        cmd->din_xfer_len *= ide_atapi_pt_read_cd_block_size(s->io_buffer);
 +        break;
-+    }
 +
 +    case GPCMD_READ_CD_MSF:
 +    {
@@ -1344,23 +1211,6 @@ index 0000000..413cc47
 +        int block_size = ide_atapi_pt_read_cd_block_size(s->io_buffer);
 +
 +        cmd->din_xfer_len = block_count * block_size;
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
-+    }
-+
-+    case GPCMD_PLAY_AUDIO_10:
-+    {
-+        int block_count = ube16_to_cpu(s->io_buffer + 7);
-+        cmd->din_xfer_len = block_count * CD_FRAMESIZE;
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
 +        break;
 +    }
 +
@@ -1372,169 +1222,55 @@ index 0000000..413cc47
 +            MSF_TO_FRAMES(s->io_buffer[6], s->io_buffer[7], s->io_buffer[8]);
 +        int block_count = ending_frame - starting_frame;
 +        cmd->din_xfer_len = block_count * CD_FRAMESIZE;
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = cmd->din_xfer_len;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
 +        break;
 +    }
 +
-+    case GPCMD_READ_FORMAT_CAPACITIES:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_read_format_capacities_sent;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
++    case GPCMD_GET_PERFORMANCE:
++        // din_xfer_len is currently the max number of descriptors
++        switch(s->io_buffer[10])
++        {
++        case 0:  // performance
++            if((s->io_buffer[1] & 3) == 0)  // nominal performance, 16 byte descriptors
++                cmd->din_xfer_len *= 16;
++            else  // exceptions, 6 byte descriptors
++                cmd->din_xfer_len *= 6;
 +
-+    case GPCMD_GET_CONFIGURATION:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 7);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = 4;
-+        s->atapi_pt.reply_size_len = 4;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
++            cmd->din_xfer_len += 8;  // 8 bytes of header
++            break;
 +
-+    case GPCMD_SEND_DVD_STRUCTURE:
-+        cmd->din_xfer_len = ube16_to_cpu(s->io_buffer + 8);
-+        //        cmd->din_xferp = (__u64)s->io_buffer;
-+        s->atapi_pt.cmd_sent = ide_atapi_pt_standard_reply;
-+        s->atapi_pt.reply_size_init = 2;
-+        s->atapi_pt.reply_size_len = 2;
-+        //        ATAPI_PT_SEND_PACKET;
-+        //        return;
-+        break;
++        case 1:  // unusable area
++            cmd->din_xfer_len *= 8;  // 8 byte descriptors
++            cmd->din_xfer_len += 8;  // 8 bytes of header
++            break;
 +
-+    case 0x01: // GPMODE_R_W_ERROR_PAGE ?
-+    case 0x1a: // GPMODE_POWER_PAGE ?
-+    case 0xfa:
-+    case 0xfd:
-+    case 0xf2:
-+    case 0xf3: // WIN_SECURITY_ERASE_PREPARE ?
-+    case 0xee: // WIN_IDENTIFY_DMA ?
-+    case 0xdf: // WIN_DOORUNLOCK ?
-+        DEBUG_PRINTF("[\e[3;31mILLEGAL?\e[m] 0x%02x, size: %d\n",
-+                     s->io_buffer[0], s->lcyl | (s->hcyl << 8));
-+    illegal_request:
-+        ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
-+                               ASC_ILLEGAL_OPCODE, 0x70);
-+        return;
++        case 2:  // defect status
++            cmd->din_xfer_len *= 2048;  // 2048 byte descriptors
++            cmd->din_xfer_len += 8;     // 8 bytes of header
++            break;
 +
-+    default:
-+        DEBUG_PRINTF("[UNHANDLED] 0x%02x\n", s->io_buffer[0]);
-+        exit(1);
-+        return;
++        default:
++            DEBUG_PRINTF("\e[3;31mReserved get performance type %d\e[m\n",
++                         s->io_buffer[10]);
++            ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
++                                   ASC_ILLEGAL_OPCODE, 0x70);
++            return;
++        }
++        break;
 +    }
 +
-+
-+    s->atapi_pt.sense.error_code = 0;
-+    s->atapi_pt.sense.sense_key = 0;
-+    s->atapi_pt.sense.asc = 0;
-+    s->atapi_pt.sense.ascq = 0;
-+
-+    /* Send command and wait for reply, SG_IO ioctl*/
-+    r = ioctl(raw_state->fd, 0x2285, cmd);
-+
-+    if(command == GPCMD_GET_EVENT_STATUS_NOTIFICATION)
++    if(cmd->dout_xfer_len == (__u32)-1)
 +    {
-+        struct stat file_stat;
-+
-+        if(s->io_buffer[2] == 4 && s->io_buffer[4] == 2)
-+        {
-+            /* This is a "new media" message, tell any other VMs */
-+            DEBUG_PRINTF("[ATAPI] new media\n");
-+            system("touch " IDE_ATAPI_PT_NEW_CD_FILE);
-+
-+            if(stat(IDE_ATAPI_PT_NEW_CD_FILE, &file_stat) != 0)
-+                DEBUG_PRINTF("Error writing to new CD file\n");
-+            else
-+                s->atapi_pt.new_cd_time = file_stat.st_ctime;
-+        }
-+
-+        if(s->io_buffer[2] == 4 && s->io_buffer[4] == 3)
-+        {
-+            /* This is a "media removed" message, tell any other VMs */
-+            DEBUG_PRINTF("[ATAPI] media removed\n");
-+            system("touch " IDE_ATAPI_PT_EJECT_CD_FILE);
-+
-+            if(stat(IDE_ATAPI_PT_EJECT_CD_FILE, &file_stat) != 0)
-+                DEBUG_PRINTF("Error writing to eject CD file\n");
-+            else
-+                s->atapi_pt.eject_time = file_stat.st_ctime;
-+        }
-+
-+        if((s->io_buffer[2] == 4 && s->io_buffer[4] == 0 && s->io_buffer[5] == 2) ||
-+           (s->io_buffer[4] == 0 && s->io_buffer[5] == 0 &&
-+            s->io_buffer[6] == 0 && s->io_buffer[7] == 0))
-+        {
-+            /* This is a no activity message we can hijack if we need to */
-+
-+            if(stat(IDE_ATAPI_PT_NEW_CD_FILE, &file_stat) == 0 &&
-+               s->atapi_pt.new_cd_time < file_stat.st_ctime)
-+            {
-+                /* There's been a new media message that we haven't seen yet */
-+                DEBUG_PRINTF("[ATAPI] new media message spotted\n");
-+                s->atapi_pt.new_cd_time = file_stat.st_ctime;
-+
-+                s->io_buffer[2] = 4;
-+                s->io_buffer[4] = 2;
-+                s->io_buffer[5] = 2;
-+                s->io_buffer[6] = 0;
-+                s->io_buffer[7] = 0;
-+            }
-+            else if(stat(IDE_ATAPI_PT_EJECT_CD_FILE, &file_stat) == 0 &&
-+               s->atapi_pt.eject_time < file_stat.st_ctime)
-+            {
-+                /* There's been an eject message that we haven't seen yet */
-+                DEBUG_PRINTF("[ATAPI] media removed message spotted\n");
-+                s->atapi_pt.eject_time = file_stat.st_ctime;
-+
-+                s->io_buffer[2] = 4;
-+                s->io_buffer[4] = 3;
-+                s->io_buffer[5] = 1;
-+                s->io_buffer[6] = 0;
-+                s->io_buffer[7] = 0;
-+            }
-+        }
++        DEBUG_PRINTF("[UNHANDLED SCSI COMMAND] 0x%02x\n", cmd_code);
++        exit(1);
 +    }
 +
-+    if(r || cmd->driver_status || cmd->transport_status ||
-+       cmd->device_status) {
-+        /*
-+        DEBUG_PRINTF("[\e[1;31mERROR\e[m]\n"
-+                     "\tcommand 0x%02x (%s)\n"
-+                     "\terrno: %d (%s)\n"
-+                     "\tsense: 0x%02x,%02x,%02x (%s)\n"
-+                     "\tdriver: %d, transport: %d, device: %d\n",
-+                     command, atapi_cmd_to_str(command),
-+                     errno,
-+                     strerror(errno) ? : "(null)",
-+                     s->atapi_pt.sense.sense_key,
-+                     s->atapi_pt.sense.asc,
-+                     s->atapi_pt.sense.ascq,
-+                     atapi_sense_to_str(s->atapi_pt.sense.sense_key,
-+                                        s->atapi_pt.sense.asc,
-+                                        s->atapi_pt.sense.ascq),
-+                     cmd->driver_status,
-+                     cmd->transport_status,
-+                     cmd->device_status);
-+        */
-+        DEBUG_PRINTF("[\e[1;31mERROR\e[m] (%s)  sense: 0x%02x,%02x,%02x (%s)\n",
-+                     atapi_cmd_to_str(command),
-+                     s->atapi_pt.sense.sense_key,
-+                     s->atapi_pt.sense.asc,
-+                     s->atapi_pt.sense.ascq,
-+                     atapi_sense_to_str(s->atapi_pt.sense.sense_key,
-+                                        s->atapi_pt.sense.asc,
-+                                        s->atapi_pt.sense.ascq));
-+        ide_atapi_pt_error(s);
++    if(cmd->dout_xfer_len > 0)
++    {
++        ide_atapi_pt_wcmd(s);
 +        return;
 +    }
 +
-+    s->atapi_pt.cmd_sent(s);
++    ide_atapi_pt_do_sg_io(s);
 +}
 diff --git a/hw/ide.c b/hw/ide.c
 index e8d676e..6c1d60a 100644
@@ -2125,7 +1861,7 @@ index e8d676e..6c1d60a 100644
          s->drive_serial = drive_serial++;
          strncpy(s->drive_serial_str, drive_get_serial(s->bs),
 diff --git a/vl.c b/vl.c
-index cd354de..b5dfbd5 100644
+index f3b0dae..ae461c8 100644
 --- a/vl.c
 +++ b/vl.c
 @@ -2192,8 +2192,10 @@ static int bt_parse(const char *opt)
index 2b162e928ce0580f9dfda852e7138d69862bfa4f..9a556512b6272509ba14c8aa3cafca81b45fd827 100644 (file)
@@ -30,4 +30,3 @@ new-input-code
 atapi-pass-through
 pv_driver_throttling_disabled
 move-iso-cdrom
-atapi-pt-write