]> xenbits.xen.org Git - xenclient/ioemu-pq.git/commitdiff
CD claims exclusive lock of write and releases on eject
authorAndrew McNeil <andrewmcn@bob.uk.xensource.com>
Fri, 20 Nov 2009 15:06:36 +0000 (15:06 +0000)
committerAndrew McNeil <andrewmcn@bob.uk.xensource.com>
Fri, 20 Nov 2009 15:06:36 +0000 (15:06 +0000)
master/atapi-pass-through

index fa009a85c4bc6471b9a6295c75afddb69d604616..a01939c50e8e2ab1c26b1e9078723a6dea96ba46 100644 (file)
@@ -105,10 +105,10 @@ 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..4373a2c
+index 0000000..09cf6bb
 --- /dev/null
 +++ b/hw/atapi-pt.c
-@@ -0,0 +1,1361 @@
+@@ -0,0 +1,1446 @@
 +#include <scsi/sg.h>
 +#include <utime.h>
 +#include <time.h>
@@ -131,117 +131,20 @@ index 0000000..4373a2c
 +#ifdef DEBUG_IDE_ATAPI_PT
 +# define DEBUG_PRINTF(Args...) atapi_dprintf(Args)
 +# define DEBUG_HEXDUMP(addr, count) atapi_dhexdump(addr, count)
-+# define CHECK_SAME_VALUE(Val1, Val2)                                   \
-+    do {                                                                \
-+    if ((Val1) != (Val2))                                           \
-+      dprintf("[\e[1;32m!VALUE\e[m] %s:%d, %s=%d %s=%d\n",         \
-+          __PRETTY_FUNCTION__, __LINE__, #Val1, (Val1),        \
-+             #Val2, (Val2));                                      \
-+    } while (0)
 +#else
 +# define DEBUG_PRINTF(Args...)
 +# define DEBUG_HEXDUMP(addr, count)
-+# define CHECK_SAME_VALUE(Val1, Val2)
 +#endif /* DEBUG_IDE_ATAPI_PT */
 +
 +
 +#define IDE_ATAPI_PT_DEBUG_ENABLE_FILE   "/etc/debugcdrom"
 +#define IDE_ATAPI_PT_NEW_CD_FILE   "/var/lock/xen-cd-new"
 +#define IDE_ATAPI_PT_EJECT_CD_FILE "/var/lock/xen-cd-eject"
++#define IDE_ATAPI_PT_EXCLUSIVE_CD_FILE "/var/lock/xen-cd-exclusive"
 +#define IDE_ATAPI_PT_DEBUG_FILE_TEMPLATE "/var/log/cdrom-%d.log"
 +
-+/* The generic packet command opcodes for CD/DVD Logical Units,
-+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-+static const struct {
-+    unsigned short packet_command;
-+    const char * const text;
-+} packet_command_texts[] = {
-+    { GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
-+    { GPCMD_REQUEST_SENSE, "Request Sense" },
-+    { GPCMD_FORMAT_UNIT, "Format Unit" },
-+    { GPCMD_INQUIRY, "Inquiry" },
-+    { GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
-+    { GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
-+    { GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
-+    { GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
-+    { GPCMD_READ_10, "Read 10" },
-+    { GPCMD_WRITE_10, "Write 10" },
-+    { GPCMD_SEEK, "Seek" },
-+    { GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
-+    { GPCMD_VERIFY_10, "Verify 10" },
-+    { GPCMD_FLUSH_CACHE, "Flush Cache" },
-+    { GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
-+    { GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
-+    { GPCMD_READ_HEADER, "Read Header" },
-+    { GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
-+    { GPCMD_GET_CONFIGURATION, "Get Configuration" },
-+    { GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
-+    { GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
-+    { GPCMD_GET_EVENT_STATUS_NOTIFICATION,
-+      "Get Event Status Notification" },
-+    { GPCMD_PAUSE_RESUME, "Pause/Resume" },
-+    { GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
-+    { GPCMD_READ_DISC_INFO, "Read Disc Info" },
-+    { GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
-+    { GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
-+    { GPCMD_SEND_OPC, "Send OPC" },
-+    { GPCMD_MODE_SELECT_10, "Mode Select 10" },
-+    { GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
-+    { GPCMD_MODE_SENSE_10, "Mode Sense 10" },
-+    { GPCMD_CLOSE_TRACK, "Close Track" },
-+    { GPCMD_BLANK, "Blank" },
-+    { GPCMD_SEND_EVENT, "Send Event" },
-+    { GPCMD_SEND_KEY, "Send Key" },
-+    { GPCMD_REPORT_KEY, "Report Key" },
-+    { GPCMD_LOAD_UNLOAD, "Load/Unload" },
-+    { GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
-+    { GPCMD_READ_12, "Read 12" },
-+    { GPCMD_GET_PERFORMANCE, "Get Performance" },
-+    { GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
-+    { GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
-+    { GPCMD_SET_STREAMING, "Set Streaming" },
-+    { GPCMD_READ_CD_MSF, "Read CD MSF" },
-+    { GPCMD_SCAN, "Scan" },
-+    { GPCMD_SET_SPEED, "Set Speed" },
-+    { GPCMD_PLAY_CD, "Play CD" },
-+    { GPCMD_MECHANISM_STATUS, "Mechanism Status" },
-+    { GPCMD_READ_CD, "Read CD" },
-+    { GPCMD_READ_BUFFER_CAPACITY, "Read Buffer Capacity" },
-+    { GPCMD_READ_BUFFER, "Read Buffer" },
-+    { GPCMD_SEND_CUE_SHEET, "Send Cue Sheet" },
-+    { 0, 0 }
-+};
-+
-+static const char *atapi_cmd_to_str(int cmd)
-+{
-+    int i;
-+
-+    for (i = 0; packet_command_texts[i].text; ++i)
-+        if (packet_command_texts[i].packet_command == cmd)
-+            return packet_command_texts[i].text;
-+    return 0;
-+}
-+
-+/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-+static const char * const sense_key_texts[16] = {
-+    "No sense data",
-+    "Recovered error",
-+    "Not ready",
-+    "Medium error",
-+    "Hardware error",
-+    "Illegal request",
-+    "Unit attention",
-+    "Data protect",
-+    "Blank check",
-+    "(reserved)",
-+    "(reserved)",
-+    "Aborted command",
-+    "(reserved)",
-+    "(reserved)",
-+    "Miscompare",
-+    "(reserved)",
-+};
 +
++/* Debug, utility and error handling functions */
 +
 +
 +/* From Table 459 of the SFF8090 Ver. 4 (Mt. Fuji) draft standard.
@@ -606,7 +509,6 @@ index 0000000..4373a2c
 +            return sense_data_texts[i].text;
 +}
 +
-+
 +static void atapi_dprintf(const char *fmt, ...)
 +{
 +    struct stat st;
@@ -618,32 +520,32 @@ index 0000000..4373a2c
 +    int l;
 +    static int sol = 1;
 +    if (debug_enabled == 0)
-+      return;
++        return;
 +    if (debug_enabled < 0) {
-+      if (stat(IDE_ATAPI_PT_DEBUG_ENABLE_FILE, &st) == 0)
-+          debug_enabled = 1;
-+      else {
-+          debug_enabled = 0;
-+          return;
-+      }
++        if (stat(IDE_ATAPI_PT_DEBUG_ENABLE_FILE, &st) == 0)
++            debug_enabled = 1;
++        else {
++            debug_enabled = 0;
++            return;
++        }
 +    }
 +    if (debug_fd<0) {
-+      sprintf(debugbuf, IDE_ATAPI_PT_DEBUG_FILE_TEMPLATE, domid);
-+      debug_fd=open(debugbuf, O_WRONLY | O_CREAT | O_APPEND, 0666);
++        sprintf(debugbuf, IDE_ATAPI_PT_DEBUG_FILE_TEMPLATE, domid);
++        debug_fd=open(debugbuf, O_WRONLY | O_CREAT | O_APPEND, 0666);
 +    }
 +    l = 0;
 +    if (sol) {
-+      gettimeofday(&tv, NULL);
-+      l = snprintf(debugbuf, sizeof(debugbuf)-1, "[%02d:%02d:%02d.%03ld] ",
-+                    tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tv.tv_usec/1000);
++        gettimeofday(&tv, NULL);
++        l = snprintf(debugbuf, sizeof(debugbuf)-1, "[%02d:%02d:%02d.%03ld] ",
++                     tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tv.tv_usec/1000);
 +    }
 +    va_start(args, fmt);
 +    l += vsnprintf(debugbuf+l, sizeof(debugbuf)-1-l, fmt, args);
 +    va_end(args);
 +    if ((l>0) && debugbuf[l-1] == '\n') {
-+      sol = 1;
++        sol = 1;
 +    } else {
-+      sol = 0;
++        sol = 0;
 +    }
 +    debugbuf[sizeof(debugbuf)-1] = '\0';
 +    write(debug_fd, debugbuf, strlen(debugbuf));
@@ -655,14 +557,14 @@ index 0000000..4373a2c
 +    int i, j;
 +    
 +    for (i = 0; i < len; i += 16) {
-+      for (j = 0; j < 16 && i + j < len; j++)
-+          atapi_dprintf("%02x ", p[i + j]);
-+      for (; j < 16; j++)
-+          atapi_dprintf("   ");
-+      atapi_dprintf(" ");
-+      for (j = 0; j < 16 && i + j < len; j++)
-+          atapi_dprintf("%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
-+      atapi_dprintf("\n");
++       for (j = 0; j < 16 && i + j < len; j++)
++           atapi_dprintf("%02x ", p[i + j]);
++       for (; j < 16; j++)
++           atapi_dprintf("   ");
++       atapi_dprintf(" ");
++       for (j = 0; j < 16 && i + j < len; j++)
++           atapi_dprintf("%c", (p[i + j] < ' ' || p[i + j] > 0x7e) ? '.' : p[i + j]);
++       atapi_dprintf("\n");
 +    }
 +}
 +
@@ -697,158 +599,216 @@ index 0000000..4373a2c
 + * 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
++
++struct ide_atapi_pt_size_defn {
++    int len_const;
++    int len_offset;
++    int len_size;
++    int block_size;
++};
++
++#define CDFSZ CD_FRAMESIZE  // define a shorter name to make the following table easier to read
++
++/* The following per command information is taken from the SFF8090 (Mt. Fuji) standard,
++ * versions 4 and 7.
++ */
++static const struct {
++    struct ide_atapi_pt_size_defn dout_size;
++    struct ide_atapi_pt_size_defn buffer_size;
++    struct ide_atapi_pt_size_defn din_size;
++    const char *name;
++} atapi_pt_cmd_info[256] = {
++    // dout         buffer          din             name
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Test Unit Ready"},              // 0x00 GPCMD_TEST_UNIT_READY
++    {{-1}},{{-1}},                                                                   // 0x01-0x02
++    {{ 0, 0, 0, 0}, { 0, 4, 1, 1},  { 8, 7, 1, 1},  "Request Sense"},                // 0x03 GPCMD_REQUEST_SENSE
++    {{12, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Format Unit"},                  // 0x04 GPCMD_FORMAT_UNIT
++    {{-1}},{{-1}},  {{-1}},                                                          // 0x05-0x07
++    {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},{{-1}},                   // 0x08-0x0f
++    {{-1}},{{-1}},                                                                   // 0x10-0x11
++    {{ 0, 0, 0, 0}, { 0, 4, 1, 1},  { 5, 4, 1, 1},  "Inquiry"},                      // 0x12 GPCMD_INQUIRY
++    {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},{{-1}},                   // 0x13-0xa
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Start/Stop Unit"},              // 0x1b GPCMD_START_STOP_UNIT
++    {{-1}},{{-1}},                                                                   // 0x1c-0x1d
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Prevent/Allow Medium Removal"}, // 0x1e GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL
++    {{-1}},{{-1}},  {{-1}},{{-1}},                                                   // 0x1f-0x22
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 4, 3, 1, 1},  "Read Format Capacities"},       // 0x23 GPCMD_READ_FORMAT_CAPACITIES
++    {{-1}},                                                                          // 0x24
++    {{ 0, 0, 0, 0}, { 8, 0, 0, 0},  { 8, 0, 0, 0},  "Read Cd/Dvd Capacity"},         // 0x25 GPCMD_READ_CDVD_CAPACITY
++    {{-1}},{{-1}},                                                                   // 0x26-0x27
++    {{ 0, 0, 0, 0}, {0,7,2,CDFSZ},  {-1, 0, 0, 0},  "Read 10"},                      // 0x28 GPCMD_READ_10
++    {{-1}},                                                                          // 0x29
++    {{0,7,2,CDFSZ}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Write 10"},                     // 0x2a GPCMD_WRITE_10
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Seek"},                         // 0x2b GPCMD_SEEK
++    {{-1}},{{-1}},                                                                   // 0x2c-0x2d
++    {{0,7,2,CDFSZ}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Write and Verify 10"},          // 0x2e GPCMD_WRITE_AND_VERIFY_10
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Verify 10"},                    // 0x2f GPCMD_VERIFY_10
++    {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},                                          // 0x30-0x34
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Flush Cache"},                  // 0x35 GPCMD_FLUSH_CACHE
++    {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},                                          // 0x36-0x3a
++    {{ 0, 6, 3, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Write Buffer"},                 // 0x3b GPCMD_WRITE_BUFFER
++    {{ 0, 0, 0, 0}, { 0, 6, 3, 1},  { 4, 1, 3, 1},  "Read Buffer"},                  // 0x3c GPCMD_READ_BUFFER
++    {{-1}},{{-1}},  {{-1}},{{-1}},  {{-1}},                                          // 0x3d-0x41
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 4, 2, 2, 1},  "Read Subchannel"},              // 0x42 GPCMD_READ_SUBCHANNEL
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Read Table of Contents"},       // 0x43 GPCMD_READ_TOC_PMA_ATIP
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 8, 0, 0, 0},  "Read Header"},                  // 0x44 GPCMD_READ_HEADER
++    {{ 0, 0, 0, 0}, {0,7,2,CDFSZ},  {-1, 0, 0, 0},  "Play Audio 10"},                // 0x45 GPCMD_PLAY_AUDIO_10
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 4, 0, 4, 1},  "Get Configuration"},            // 0x46 GPCMD_GET_CONFIGURATION
++    {{ 0, 0, 0, 0}, {-1, 0, 0, 0},  {-1, 0, 0, 0},  "Play Audio MSF"},               // 0x47 GPCMD_PLAY_AUDIO_MSF
++    {{-1}},{{-1}},                                                                   // 0x48-0x49
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Get Event Status Notification"},// 0x4a GPCMD_GET_EVENT_STATUS_NOTIFICATION
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Pause/Resume"},                 // 0x4b GPCMD_PAUSE_RESUME
++    {{-1}},{{-1}},                                                                   // 0x4c-0x4d
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Stop Play/Scan"},               // 0x4e GPCMD_STOP_PLAY_SCAN
++    {{-1}},{{-1}},                                                                   // 0x4f-0x50
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Read Disc Info"},               // 0x51 GPCMD_READ_DISC_INFO
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Read Track Rzone Info"},        // 0x52 GPCMD_READ_TRACK_RZONE_INFO
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Reserve Rzone Track"},          // 0x53 GPCMD_RESERVE_RZONE_TRACK
++    {{ 0, 7, 2, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Send OPC"},                     // 0x54 GPCMD_SEND_OPC
++    {{ 0, 7, 2, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Mode Select 10"},               // 0x55 GPCMD_MODE_SELECT_10
++    {{-1}},{{-1}},                                                                   // 0x56-0x57
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Repair Rzone Track"},           // 0x58 GPCMD_REPAIR_RZONE_TRACK
++    {{-1}},                                                                          // 0x59
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Mode Sense 10"},                // 0x5a GPCMD_MODE_SENSE_10
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Close Track"},                  // 0x5b GPCMD_CLOSE_TRACK
++    {{ 0, 0, 0, 0}, { 0, 7, 2, 1},  { 2, 0, 2, 1},  "Read Buffer Capacity"},         // 0x5c GPCMD_READ_BUFFER_CAPACITY
++    {{ 0, 6, 3, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Send Cue Sheet"},               // 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},  "Blank"},                        // 0xa1 GPCMD_BLANK
++    {{ 0, 8, 2, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Send Event"},                   // 0xa2 GPCMD_SEND_EVENT
++    {{ 0, 8, 2, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Send Key"},                     // 0xa3 GPCMD_SEND_KEY
++    {{ 0, 0, 0, 0}, { 0, 8, 2, 1},  { 2, 0, 2, 1},  "Report Key"},                   // 0xa4 GPCMD_REPORT_KEY
++    {{-1}},                                                                          // 0xa5
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Load/Unload"},                  // 0xa6 GPCMD_LOAD_UNLOAD
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Set Read-ahead"},               // 0xa7 GPCMD_SET_READ_AHEAD
++    {{ 0, 0, 0, 0}, {0,6,4,CDFSZ},  {-1, 0, 0, 0},  "Read 12"},                      // 0xa8 GPCMD_READ_12
++    {{-1}},                                                                          // 0xa9
++    {{0,6,4,CDFSZ}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Write 12"},                     // 0xaa GPCMD_WRITE_12
++    {{-1}},                                                                          // 0xab
++    {{ 0, 0, 0, 0}, { 0, 8, 2, 1},  { 4, 0, 4, 1},  "Get Performance"},              // 0xac GPCMD_GET_PERFORMANCE
++    {{ 0, 0, 0, 0}, { 0, 8, 2, 1},  { 2, 0, 2, 1},  "Read DVD Structure"},           // 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},  "Set Streaming"},                // 0xb6 GPCMD_SET_STREAMING
++    {{-1}},{{-1}},                                                                   // 0xb7-0xb8
++    {{ 0, 0, 0, 0}, {-1, 0, 0, 0},  {-1, 0, 0, 0},  "Read CD MSF"},                  // 0xb9 GPCMD_READ_CD_MSF
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Scan"},                         // 0xba GPCMD_SCAN
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Set Speed"},                    // 0xbb GPCMD_SET_SPEED
++    {{ 0, 0, 0, 0}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Play CD"},                      // 0xbc GPCMD_PLAY_CD
++    {{ 0, 0, 0, 0}, { 0, 8, 2, 1},  { 8, 6, 2, 1},  "Mechanism Status"},             // 0xbd GPCMD_MECHANISM_STATUS
++    {{ 0, 0, 0, 0}, { 0, 6, 3, 1},  {-1, 0, 0, 0},  "Read CD"},                      // 0xbe GPCMD_READ_CD
++    {{ 0, 8, 2, 1}, { 0, 0, 0, 0},  { 0, 0, 0, 0},  "Send DVD Structure"},           // 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 const char *atapi_cmd_to_str(uint8_t cmd)
++{
++    if(atapi_pt_cmd_info[cmd].name == NULL)
++        return "Unrecognised command";
++
++    return atapi_pt_cmd_info[cmd].name;
++}
++
++enum {
++    ide_atapi_pt_size_dout,      // size of dout
++    ide_atapi_pt_size_buffer,  // size of din buffer
++    ide_atapi_pt_size_din        // size of din data
++};
 +
-+static uint32_t ide_atapi_pt_read_field_at_offset(uint8_t *data, int offset, int field_size)
++static uint32_t ide_atapi_pt_get_data_size(int size_select, uint8_t command, uint8_t *data)
 +{
-+    switch(field_size)
++    const struct ide_atapi_pt_size_defn *size_defn;
++      uint32_t size;
++
++    switch(size_select)
++    {
++    case ide_atapi_pt_size_dout:
++        size_defn = &atapi_pt_cmd_info[command].dout_size;
++        break;
++
++    case ide_atapi_pt_size_buffer:
++        size_defn = &atapi_pt_cmd_info[command].buffer_size;
++        break;
++
++    case ide_atapi_pt_size_din:
++        size_defn = &atapi_pt_cmd_info[command].din_size;
++        break;
++
++    default:
++        DEBUG_PRINTF("Invalid data size selection %d\n", size_select);
++        assert(0);
++        return -1;
++    }
++      
++    switch(size_defn->len_size)
 +    {
 +    case 0:
-+        return 0;
++        size = 0;
++        break;
++
 +    case 1:
-+        return data[offset];
++        size = data[size_defn->len_offset];
++        break;
++
 +    case 2:
-+        return ube16_to_cpu(data + offset);
++        size = ube16_to_cpu(data + size_defn->len_offset);
++        break;
++
 +    case 3:
-+        return ube24_to_cpu(data + offset);
++        size = ube24_to_cpu(data + size_defn->len_offset);
++        break;
++
 +    case 4:
-+        return ube32_to_cpu(data + offset);
++        size = ube32_to_cpu(data + size_defn->len_offset);
++        break;
++
 +    default:
++        DEBUG_PRINTF("Invalid data size length in table, command 0x%02x, select %d, size %d\n",
++                     command, size_select, size_defn->len_size);
 +        assert(0);
 +        return -1;
 +    }
-+}
 +
++      size *= size_defn->block_size;
++      size += size_defn->len_const;
++      return size;
++}
 +
-+static void ide_atapi_pt_set_error(IDEState *s, int sense_key, int asc, int error)
++static void ide_atapi_pt_set_error(IDEState *s, int sense_key, int asc, int ascq, int error)
 +{
 +    s->atapi_pt.sense.sense_key  = sense_key;
 +    s->atapi_pt.sense.asc        = asc;
++    s->atapi_pt.sense.ascq       = ascq;
 +    s->atapi_pt.sense.error_code = error;
++
++    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,
++                 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));
++
 +    s->status  = READY_STAT | ERR_STAT;
 +    s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
 +    ide_set_irq(s);
@@ -861,10 +821,16 @@ index 0000000..4373a2c
 +    ide_set_irq(s);
 +}
 +
++
++/* Thread worker function.
++ * This is the only function that knows which ioctl / other mechanism we use to
++ * send our command.
++ * This is the BSG SG_IO v4 implementation.
++ */
 +static void *ide_atapi_pt_sgio_worker_thread(void *arg)
 +{
 +    int r;
-+    volatile struct sg_io_v4 *cmd;
++    struct sg_io_v4 cmd;
 +    BDRVRawState *raw_state;
 +    volatile IDEState *s = (volatile IDEState *)arg;
 +
@@ -873,9 +839,56 @@ index 0000000..4373a2c
 +        pthread_cond_wait(&s->atapi_pt.sgio_cv, &s->atapi_pt.sgio_mutex);
 +
 +        /* Send command and wait for reply, SG_IO ioctl*/
-+        cmd = &s->atapi_pt.cmd;
++        memset(&cmd, 0, sizeof(cmd));
++        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.timeout          = s->atapi_pt.timeout;
++        cmd.din_xferp        = (__u64)s->io_buffer;
++        cmd.din_xfer_len     = s->atapi_pt.din_xfer_len;
++        cmd.dout_xferp       = (__u64)s->io_buffer;
++        cmd.dout_xfer_len    = s->atapi_pt.dout_xfer_len;
++
 +        raw_state = s->bs->opaque;
-+        r = ioctl(raw_state->fd, SG_IO, cmd);
++
++        if(cmd.dout_xfer_len > 0)
++        {
++            DEBUG_PRINTF("Writing %d bytes      0x", cmd.dout_xfer_len);
++            DEBUG_HEXDUMP(s->io_buffer,
++                          cmd.dout_xfer_len < 0x40 ? cmd.dout_xfer_len : 0x40);
++        }
++
++        if(cmd.timeout != 15000)
++            DEBUG_PRINTF("Timeout %d msec\n", cmd.timeout);
++
++        r = ioctl(raw_state->fd, SG_IO, &cmd);
++
++        s->atapi_pt.result = 0;
++
++        if(r)
++            s->atapi_pt.result = r;
++        else if(cmd.driver_status)
++            s->atapi_pt.result = cmd.driver_status;
++        else if(cmd.transport_status)
++            s->atapi_pt.result = cmd.transport_status;
++        else if(cmd.device_status)
++            s->atapi_pt.result = cmd.device_status;
++
++        if(s->atapi_pt.result)
++        {
++            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,
++                         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));
++        }
 +
 +        /* Unlock _before_ signalling parent */
 +        pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
@@ -883,20 +896,22 @@ index 0000000..4373a2c
 +    }
 +}
 +
++
++/* Functions to manage worker thread */
++
 +static int ide_atapi_pt_aio_flush(void *unused)
 +{
 +    return 0;
 +}
 +
-+static void ide_atapi_pt_do_sg_io_complete(void *unused);
++static void ide_atapi_pt_cmd_complete(void *arg);
 +
 +static void ide_atapi_pt_setup_sgio_thread(IDEState *s)
 +{
 +    int fds[2];
 +
-+    //DEBUG_PRINTF("%s\n", __FUNCTION__);
-+    if (pipe(fds) < 0) {
-+      fprintf(stderr, "atapi-pt failed to create pipe: %m\n");
++    if(pipe(fds) < 0) {
++        DEBUG_PRINTF("atapi-pt failed to create pipe: %m\n");
 +        exit(1);
 +    }
 +    s->atapi_pt.sgio_rfd = fds[0];
@@ -904,214 +919,31 @@ index 0000000..4373a2c
 +
 +    pthread_mutex_init(&s->atapi_pt.sgio_mutex, NULL);
 +    pthread_cond_init (&s->atapi_pt.sgio_cv, NULL);
++
 +    if (pthread_create(&s->atapi_pt.sgio_thread, NULL, ide_atapi_pt_sgio_worker_thread, (void *)s))
 +    {
-+      DEBUG_PRINTF("Create CD-ROM worker thread failed\n");
-+      fprintf(stderr, "Create CD-ROM worker thread failed\n");
-+      exit(1);
++       DEBUG_PRINTF("Create CD-ROM worker thread failed\n");
++       exit(1);
 +    }
-+    qemu_aio_set_fd_handler(s->atapi_pt.sgio_rfd, ide_atapi_pt_do_sg_io_complete, NULL, ide_atapi_pt_aio_flush, (void *)s);
++    qemu_aio_set_fd_handler(s->atapi_pt.sgio_rfd, ide_atapi_pt_cmd_complete, NULL, ide_atapi_pt_aio_flush, (void *)s);
 +}
 +
 +/* Call with ide_atapi_sgio_mutex held */
-+static void ide_atapi_pt_do_sg_io(IDEState *s, int timeout)
++static void ide_atapi_pt_do_sg_io(IDEState *s)
 +{
-+    //DEBUG_PRINTF("%s\n", __FUNCTION__);
-+    struct sg_io_v4 *cmd = &s->atapi_pt.cmd;
-+    assert(cmd->din_xfer_len != (__u32)-1);
-+
-+    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;
-+
-+    cmd->timeout = timeout ? timeout : 15000;
-+
-+    if ((s->atapi_pt.request[0] == GPCMD_REPORT_KEY) || (s->atapi_pt.request[0] == GPCMD_SEND_KEY)) {
-+      if (cmd->dout_xfer_len > 0) {
-+          DEBUG_PRINTF("write:\n");
-+          DEBUG_HEXDUMP(cmd->dout_xferp, cmd->dout_xfer_len);
-+      }
-+    }
-+
 +    /* Poke worker thread to send command using SG_IO ioctl */
 +    pthread_cond_signal(&s->atapi_pt.sgio_cv);
 +    pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
 +}
++ 
 +
-+static void ide_atapi_pt_do_sg_io_complete(void *arg)
-+{
-+    IDEState *s = (IDEState *)arg;
-+    struct sg_io_v4 *cmd = &s->atapi_pt.cmd;
-+    BDRVRawState *raw_state = s->bs->opaque;
-+    int r;
-+    uint8_t cmd_code = s->atapi_pt.request[0];
-+    uint32_t din_desired;
-+
-+    //DEBUG_PRINTF("%s\n", __FUNCTION__);
-+    assert(s);
-+
-+    /* Get return code of ioctl from worker thread's fd */
-+    read(s->atapi_pt.sgio_rfd, &r, sizeof(int));
-+
-+    if (r) {
-+      DEBUG_PRINTF("[ATAPI] SG_IO is a very naughty boy: %d\n", r);
-+    }
-+
-+    if ((s->atapi_pt.request[0] == GPCMD_REPORT_KEY) || (s->atapi_pt.request[0] == GPCMD_SEND_KEY)) {
-+      if (cmd->din_xfer_len > 0) {
-+          DEBUG_PRINTF("read:\n");
-+          DEBUG_HEXDUMP(cmd->din_xferp, cmd->din_xfer_len);
-+      }
-+    }
-+
-+    if(s->atapi_pt.request[0] == GPCMD_GET_EVENT_STATUS_NOTIFICATION)
-+    {
-+        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)
-+            {
-+              if (s->atapi_pt.new_cd_time != file_stat.st_ctime) {
-+              /* There's been a new media message that we haven't seen yet */
-+                  s->atapi_pt.new_cd_time = file_stat.st_ctime;
-+                  DEBUG_PRINTF("[ATAPI] new media message spotted\n");
-+
-+                  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(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,
-+                     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);
-+        return;
-+    }
-+
-+    if(cmd->din_xfer_len == 0)
-+    {
-+        // Nothing else to do
-+        ide_atapi_cmd_ok(s);
-+        return;
-+    }
-+
-+
-+    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;
-+
-+    ide_atapi_cmd_reply(s, din_desired, cmd->din_xfer_len);
-+}
++/* Functions to fetch dout data from where-ever it comes from */
 +
 +/* Call with ide_atapi_sgio_mutex held */
 +static void ide_atapi_pt_dout_fetch_pio_done(IDEState *s)
 +{
 +    ide_transfer_stop(s);
-+    ide_atapi_pt_do_sg_io(s, 0);
++    ide_atapi_pt_do_sg_io(s);
 +}
 +
 +/* Call with ide_atapi_sgio_mutex held */
@@ -1128,7 +960,7 @@ index 0000000..4373a2c
 +    }
 +
 +    i = dma_buf_rw(bm, 0);
-+    ide_atapi_pt_do_sg_io(s, 0);
++    ide_atapi_pt_do_sg_io(s);
 +}
 +
 +/* Call with ide_atapi_sgio_mutex held */
@@ -1138,13 +970,13 @@ index 0000000..4373a2c
 +    {
 +        /* DMA */
 +        s->io_buffer_index = 0;
-+        s->io_buffer_size = s->atapi_pt.cmd.dout_xfer_len;
++        s->io_buffer_size = s->atapi_pt.dout_xfer_len;
 +        ide_dma_start(s, ide_atapi_pt_dout_fetch_dma_done);
 +        return;
 +    }
 +
 +    /* PIO */
-+    s->packet_transfer_size = s->atapi_pt.cmd.dout_xfer_len;
++    s->packet_transfer_size = s->atapi_pt.dout_xfer_len;
 +    s->io_buffer_size = 0;
 +    s->elementary_transfer_size = 0;
 +    s->io_buffer_index = 0;
@@ -1153,12 +985,15 @@ index 0000000..4373a2c
 +    s->nsector = (s->nsector & ~7) &
 +        ~ATAPI_INT_REASON_IO &
 +        ~ATAPI_INT_REASON_CD;
-+    ide_transfer_start(s, s->io_buffer, s->atapi_pt.cmd.dout_xfer_len,
++    ide_transfer_start(s, s->io_buffer, s->atapi_pt.dout_xfer_len,
 +                       ide_atapi_pt_dout_fetch_pio_done);
 +    ide_set_irq(s);
 +    return;
 +}
 +
++
++/* Functions to handle atapi commands */
++
 +static int ide_atapi_pt_read_cd_block_size(const uint8_t *io_buffer)
 +{
 +    int sector_type = (io_buffer[2] >> 2) & 7;
@@ -1281,60 +1116,136 @@ index 0000000..4373a2c
 +}
 +
 +
++static void ide_atapi_pt_ejected(void)
++{
++    /* The media has been ejected, remove exclusivity lock */
++    remove(IDE_ATAPI_PT_EXCLUSIVE_CD_FILE);
++}
++
++
 +static void ide_atapi_pt_cmd(IDEState *s)
 +{
-+    int timeout;
-+    struct sg_io_v4 *cmd = &s->atapi_pt.cmd;
 +    uint8_t cmd_code;
++    FILE *fp;
++    int exclusive_domain;
 +
-+
-+    //DEBUG_PRINTF("%s (before mutex)\n", __FUNCTION__);
-+    if (pthread_mutex_trylock(&s->atapi_pt.sgio_mutex)) {
-+        fprintf(stderr, "ide_atapi_pt_cmd() called with existing request processing - ignored!\n");
++    if(pthread_mutex_trylock(&s->atapi_pt.sgio_mutex)) {
++        DEBUG_PRINTF("ide_atapi_pt_cmd() called with existing request processing - ignored!\n");
 +        return;
 +    }
 +
-+    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->timeout          = 15000; // 15 seconds
-+    timeout = 0;
-+
++    s->atapi_pt.timeout = 15000;
 +    s->status |= BUSY_STAT;
 +
-+    cmd->din_xferp = (__u64)s->io_buffer;
-+    cmd->dout_xferp = (__u64)s->io_buffer;
++    DEBUG_PRINTF("[ATAPI] sending command: 0x%02x (\e[0;32m%s\e[m) dma=%d domain=%d\n",
++                 cmd_code, atapi_cmd_to_str(cmd_code), s->atapi_dma, domid);
++    DEBUG_PRINTF("    ");
++    DEBUG_HEXDUMP(s->atapi_pt.request, ATAPI_PACKET_SIZE);
++
++    if(cmd_code == GPCMD_REQUEST_SENSE)
++    {
++        int actual_size;
 +
-+    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);
++        // send the previous sense command
++        DEBUG_PRINTF("=== REQUEST SENSE ===  "
++                     "atapi_cmd_error: sense=0x%x asc=0x%x error=0x%x\n",
++                     s->atapi_pt.sense.sense_key,
++                     s->atapi_pt.sense.asc,
++                     s->atapi_pt.sense.error_code);
 +
-+    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);
++        int max_size = ide_atapi_pt_get_data_size(ide_atapi_pt_size_buffer,
++                                                  GPCMD_REQUEST_SENSE,
++                                                  s->atapi_pt.request);
 +
++        int size = ide_atapi_pt_get_data_size(ide_atapi_pt_size_din,
++                                              GPCMD_REQUEST_SENSE,
++                                              (uint8_t *)&s->atapi_pt.sense);
 +
-+    DEBUG_PRINTF("[ATAPI] sending command: 0x%02x (\e[0;32m%s\e[m, in %d out %d)\n",
-+                 cmd_code, atapi_cmd_to_str(cmd_code), cmd->din_xfer_len, cmd->dout_xfer_len);
++        memcpy(s->io_buffer, &s->atapi_pt.sense, sizeof(s->atapi_pt.sense));
++        
++        actual_size = (size < max_size) ? size : max_size;
++        DEBUG_PRINTF("%d bytes sense data: 0x", actual_size);
++        DEBUG_HEXDUMP(s->io_buffer, actual_size);
 +
-+    if ((cmd_code == GPCMD_REPORT_KEY) || (cmd_code == GPCMD_SEND_KEY)) {
-+      DEBUG_PRINTF("Command dump:\n");
-+      DEBUG_HEXDUMP(s->atapi_pt.request, 11);
-+      DEBUG_PRINTF("dout_xfer_len: %d din_xfer_len: %d\n", 
-+                   cmd->dout_xfer_len, cmd->din_xfer_len);
++        ide_atapi_cmd_reply(s, size, max_size);
++        pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
++        return;
 +    }
-+ 
++
++    s->atapi_pt.dout_xfer_len = ide_atapi_pt_get_data_size(ide_atapi_pt_size_dout,
++                                                           cmd_code, s->atapi_pt.request);
++
++    s->atapi_pt.din_xfer_len = ide_atapi_pt_get_data_size(ide_atapi_pt_size_buffer,
++                                                          cmd_code, s->atapi_pt.request);
++
++    exclusive_domain = 0;
++    fp = fopen(IDE_ATAPI_PT_EXCLUSIVE_CD_FILE, "r");
++    if(fp != NULL) {
++        fscanf(fp, "%d", &exclusive_domain);
++        fclose(fp);
++    }
++
++    if(exclusive_domain != domid && exclusive_domain != 0)
++    {
++        uint8_t sense[18] = {0x70, 0, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x3a, 1, 0, 0, 0, 0};
++        
++        if(cmd_code == GPCMD_INQUIRY && s->atapi_pt.request[1] == 0)
++        {
++            uint8_t inquiry_data[96] = {0x05, 0x80, 0x05, 0x32, 0x5b, 0x00, 0x00, 0x00, 0x48, 0x4c, 0x2d, 0x44, 0x54, 0x2d, 0x53, 0x54,
++                                        0x43, 0x44, 0x52, 0x57, 0x44, 0x56, 0x44, 0x20, 0x4d, 0x55, 0x31, 0x30, 0x4e, 0x20, 0x20, 0x20,
++                                        0x41, 0x31, 0x30, 0x36, 0x41, 0x42, 0x4c, 0x44, 0x41, 0x20, 0x20, 0x20, 0x30, 0x38, 0x2f, 0x31,
++                                        0x31, 0x2f, 0x31, 0x38, 0x20, 0x20, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++                                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++                                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
++
++            DEBUG_PRINTF("Sending fake inquiry reply:\n");
++            DEBUG_HEXDUMP(inquiry_data, 96);
++            memcpy(s->io_buffer, inquiry_data, 96);
++            memset(&s->atapi_pt.sense, 0, 18);
++            ide_atapi_cmd_reply(s, 96, s->atapi_pt.din_xfer_len);
++            pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
++            return;
++        }
++
++        if(cmd_code == GPCMD_MODE_SENSE_10)
++        {
++            sense[2] = 5;
++            sense[12] = 0x24;
++                      sense[13] = 0;
++        }
++
++        DEBUG_PRINTF("Blocking command due to exclusivity lock\n");
++        memcpy(&s->atapi_pt.sense, sense, 18);
++        ide_atapi_pt_set_error(s, sense[2], sense[12], sense[13], 0x70);
++        pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
++        return;
++    }
++
++    /* Claim exclusive use if we're doing any kind of writing */
++    if(cmd_code == GPCMD_BLANK || cmd_code == GPCMD_CLOSE_TRACK || cmd_code == GPCMD_FLUSH_CACHE ||
++       cmd_code == GPCMD_FORMAT_UNIT || cmd_code == GPCMD_SEND_DVD_STRUCTURE || cmd_code == GPCMD_SEND_OPC ||
++       cmd_code == GPCMD_WRITE_10 || cmd_code == GPCMD_WRITE_12 || cmd_code == GPCMD_WRITE_AND_VERIFY_10 ||
++       cmd_code == GPCMD_WRITE_BUFFER)
++    {
++        int fd;
++        DEBUG_PRINTF("Claiming exclusive lock while writing\n");
++        fd = open(IDE_ATAPI_PT_EXCLUSIVE_CD_FILE, O_WRONLY | O_CREAT, 0666);
++        if(fd < 3) {
++            DEBUG_PRINTF("Could not open CD exclusivity lock file for writing\n");
++        } else {
++            char buf[8] = "\0\0\0\0\0\0\0\0";
++
++            snprintf(buf, sizeof(buf) - 1, "%d", domid);
++            write(fd, buf, strlen(buf));
++            close(fd);
++        }
++    }
++
++    //    DEBUG_PRINTF("dout_xfer_len = %d, buffer size = %d\n",
++    //                 s->atapi_pt.dout_xfer_len, s->atapi_pt.din_xfer_len);
++
 +    /* A few commands need special attention */
 +    switch(cmd_code)
 +    {
@@ -1344,17 +1255,29 @@ index 0000000..4373a2c
 +            ide_atapi_cmd_ok(s);
 +        else  // lock
 +            ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
-+                                   ASC_INV_FIELD_IN_CMD_PACKET, 0x70);
++                                   ASC_INV_FIELD_IN_CMD_PACKET, 0, 0x70);
 +
 +        pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
 +        return;
 +
++    case GPCMD_FLUSH_CACHE: // bigger timeout while flushing write cache
++        s->atapi_pt.timeout = 1000 * 60;
++        break;
++
++    case GPCMD_SEND_OPC: // bigger timeout while sending OPC
++        s->atapi_pt.timeout = 1000 * 60;
++        break;
++
++    case GPCMD_FORMAT_UNIT: // bigger timeout while formatting
++        //        s->atapi_pt.timeout = 1000 * 60 * 20;
++        break;
++
 +    case GPCMD_BLANK: // bigger timeout while blanking
-+        timeout = 1000 * 60 * 80; // 80 mins
++        s->atapi_pt.timeout = 1000 * 60 * 80; // 80 mins
 +        break;
 +
 +    case GPCMD_CLOSE_TRACK:
-+        timeout = 1000 * 60 * 5; // 5 mins
++        s->atapi_pt.timeout = 1000 * 60 * 5; // 5 mins
 +        break;
 +
 +    case GPCMD_WRITE_BUFFER:
@@ -1364,34 +1287,20 @@ index 0000000..4373a2c
 +            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);
++                                   ASC_INV_FIELD_IN_CMD_PACKET, 0, 0x70);
 +            pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
 +            return;
 +        }
 +
 +        break;
 +
-+    case GPCMD_REQUEST_SENSE:
-+    {
-+        // send the previous sense command
-+        DEBUG_PRINTF("=== REQUEST SENSE ===  "
-+                     "atapi_cmd_error: sense=0x%x asc=0x%x error=0x%x\n",
-+                     s->atapi_pt.sense.sense_key,
-+                     s->atapi_pt.sense.asc,
-+                     s->atapi_pt.sense.error_code);
-+
-+        int max_size = s->io_buffer[4];
-+
-+        int size = 8 + s->atapi_pt.sense.add_sense_len;
-+        memcpy(s->io_buffer, &s->atapi_pt.sense, sizeof (s->atapi_pt.sense));
-+        ide_atapi_cmd_reply(s, size, max_size);
-+      pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
-+        return;
-+    }
++    case GPCMD_WRITE_10:  // bigger timout while writing
++        s->atapi_pt.timeout = 1000 * 60;
++        break;        
 +
 +    case GPCMD_READ_CD:
 +        // We read blocks, not bytes
-+        cmd->din_xfer_len *= ide_atapi_pt_read_cd_block_size(s->io_buffer);
++        s->atapi_pt.din_xfer_len *= ide_atapi_pt_read_cd_block_size(s->io_buffer);
 +        break;
 +
 +    case GPCMD_READ_CD_MSF:
@@ -1403,8 +1312,7 @@ index 0000000..4373a2c
 +            MSF_TO_FRAMES(s->io_buffer[6], s->io_buffer[7], s->io_buffer[8]);
 +        int block_count = ending_frame - starting_frame;
 +        int block_size = ide_atapi_pt_read_cd_block_size(s->io_buffer);
-+
-+        cmd->din_xfer_len = block_count * block_size;
++        s->atapi_pt.din_xfer_len = block_count * block_size;
 +        break;
 +    }
 +
@@ -1415,7 +1323,7 @@ index 0000000..4373a2c
 +        int ending_frame =
 +            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;
++        s->atapi_pt.din_xfer_len = block_count * CD_FRAMESIZE;
 +        break;
 +    }
 +
@@ -1425,53 +1333,230 @@ index 0000000..4373a2c
 +        {
 +        case 0:  // performance
 +            if((s->io_buffer[1] & 3) == 0)  // nominal performance, 16 byte descriptors
-+                cmd->din_xfer_len *= 16;
++                s->atapi_pt.din_xfer_len *= 16;
 +            else  // exceptions, 6 byte descriptors
-+                cmd->din_xfer_len *= 6;
++                s->atapi_pt.din_xfer_len *= 6;
 +
-+            cmd->din_xfer_len += 8;  // 8 bytes of header
 +            break;
 +
 +        case 1:  // unusable area
-+            cmd->din_xfer_len *= 8;  // 8 byte descriptors
-+            cmd->din_xfer_len += 8;  // 8 bytes of header
++            s->atapi_pt.din_xfer_len *= 8;  // 8 byte descriptors
 +            break;
 +
 +        case 2:  // defect status
-+            cmd->din_xfer_len *= 2048;  // 2048 byte descriptors
-+            cmd->din_xfer_len += 8;     // 8 bytes of header
++            s->atapi_pt.din_xfer_len *= 2048;  // 2048 byte descriptors
++            break;
++
++        case 3:  // write speed
++            s->atapi_pt.din_xfer_len *= 16;  // 16 byte descriptors
++            break;
++
++        case 4:  // DBI data
++            s->atapi_pt.din_xfer_len *= 8;  // 8 byte descriptors
 +            break;
 +
 +        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);
++                                   ASC_INV_FIELD_IN_CMD_PACKET, 0, 0x70);
 +            pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
 +            return;
 +        }
++
++        s->atapi_pt.din_xfer_len += 8;  // 8 bytes of header
++        break;
++
++    case GPCMD_LOAD_UNLOAD:
++        if((s->io_buffer[4] & 3) == 2)  /* Eject command, remove exclusivity lock */
++            ide_atapi_pt_ejected();
++
 +        break;
 +    }
 +
-+    if(cmd->dout_xfer_len == (__u32)-1)
++    /* Clear sense data */
++    memset(&s->atapi_pt.sense, 0, sizeof(s->atapi_pt.sense));
++
++    if(s->atapi_pt.dout_xfer_len == (__u32)-1)
 +    {
-+        DEBUG_PRINTF("[UNHANDLED SCSI COMMAND] 0x%02x\n", cmd_code);
-+      ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
-+                             ASC_ILLEGAL_OPCODE, 0x70);
-+      pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
-+      return;
++        DEBUG_PRINTF("[\e[3;31mUNHANDLED SCSI COMMAND\e[m]\n");
++        DEBUG_PRINTF("    ");
++        DEBUG_HEXDUMP(s->atapi_pt.request, ATAPI_PACKET_SIZE);
++        ide_atapi_pt_set_error(s, SENSE_ILLEGAL_REQUEST,
++                               ASC_ILLEGAL_OPCODE, 0, 0x70);
++        pthread_mutex_unlock(&s->atapi_pt.sgio_mutex);
++        return;
 +    }
 +
-+    if(cmd->dout_xfer_len > 0)
++    if(s->atapi_pt.dout_xfer_len > 0)
 +    {
++        /* dout data is required, get it from somewhere */
 +        ide_atapi_pt_wcmd(s);
 +        return;
 +    }
 +
-+    ide_atapi_pt_do_sg_io(s, timeout);
++    ide_atapi_pt_do_sg_io(s);
++}
++
++static void ide_atapi_pt_cmd_complete(void *arg)
++{
++    IDEState *s = (IDEState *)arg;
++    int r;
++    uint8_t cmd_code = s->atapi_pt.request[0];
++    uint32_t din_desired;
++
++    assert(s);
++
++    /* Get return code of ioctl from worker thread's fd */
++    read(s->atapi_pt.sgio_rfd, &r, sizeof(int));
++
++    if(s->atapi_pt.result)
++    {
++        if(s->atapi_pt.request[0] == GPCMD_TEST_UNIT_READY &&
++           s->atapi_pt.sense.sense_key == 2 && s->atapi_pt.sense.asc == 0x3A)
++            /* No media, remove exclusivity lock */
++            ide_atapi_pt_ejected();
++
++        ide_atapi_pt_error(s);
++        return;
++    }
++
++    if(s->atapi_pt.request[0] == GPCMD_GET_EVENT_STATUS_NOTIFICATION)
++    {
++        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_EJECT_CD_FILE, O_WRONLY | O_TRUNC | O_CREAT, 0666);
++            if(fd < 0) {
++                DEBUG_PRINTF("Error touching eject CD file\n");
++            } else {
++                close(fd);
++                utime(IDE_ATAPI_PT_EJECT_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;
++
++            /* Remove exclusivity lock */
++            ide_atapi_pt_ejected();
++        }
++
++        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 */
++                s->atapi_pt.new_cd_time = file_stat.st_ctime;
++                DEBUG_PRINTF("[ATAPI] new media message spotted\n");
++
++                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.din_xfer_len == 0)
++    {
++        // Nothing else to do
++        ide_atapi_cmd_ok(s);
++        return;
++    }
++
++    din_desired = ide_atapi_pt_get_data_size(ide_atapi_pt_size_din, cmd_code, s->io_buffer);
++
++    /* 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 = s->atapi_pt.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_INV_FIELD_IN_CMD_PACKET, 0, 0x70);
++            return;
++        }
++    }
++
++    if(din_desired == (__u32)-1)
++        din_desired = s->atapi_pt.din_xfer_len;
++
++    DEBUG_PRINTF("Read %d bytes of data (buffer size %d)\n",
++                 din_desired, s->atapi_pt.din_xfer_len);
++
++    if(s->atapi_pt.din_xfer_len >= 0x80 && din_desired >= 0x80)
++        DEBUG_HEXDUMP(s->io_buffer, 0x80);
++    else if(s->atapi_pt.din_xfer_len < din_desired)
++        DEBUG_HEXDUMP(s->io_buffer, s->atapi_pt.din_xfer_len);
++    else
++        DEBUG_HEXDUMP(s->io_buffer, din_desired);
++
++    ide_atapi_cmd_reply(s, din_desired, s->atapi_pt.din_xfer_len);
 +}
 diff --git a/hw/ide.c b/hw/ide.c
-index e8d676e..2b58728 100644
+index e8d676e..37d0996 100644
 --- a/hw/ide.c
 +++ b/hw/ide.c
 @@ -22,6 +22,7 @@
@@ -1692,7 +1777,7 @@ index e8d676e..2b58728 100644
  #define ASC_ILLEGAL_OPCODE                   0x20
  #define ASC_LOGICAL_BLOCK_OOR                0x21
  #define ASC_INV_FIELD_IN_CMD_PACKET          0x24
-@@ -371,15 +399,51 @@
+@@ -371,15 +399,49 @@
  #define CFA_INVALID_ADDRESS     0x21
  #define CFA_ADDRESS_OVERFLOW    0x2f
  
@@ -1726,13 +1811,11 @@ index e8d676e..2b58728 100644
 +typedef struct ATAPIPassThroughState
 +{
 +    uint8_t              request[ATAPI_PACKET_SIZE];
-+    struct sg_io_v4      cmd;
 +    struct request_sense sense;
-+    void                 (*cmd_sent)(struct IDEState *);
-+
-+    uint32_t             reply_size_init;   // initial value
-+    uint32_t             reply_size_offset; // offset in s->io_buffer
-+    uint32_t             reply_size_len;    // length in byte (0, 1, 2, 3 or 4)
++    uint32_t             dout_xfer_len;
++    uint32_t             din_xfer_len;
++    uint32_t             timeout;  /* in milliseconds */
++    uint32_t             result;   /* 0 => OK */
 +
 +    time_t               new_cd_time;
 +    time_t               eject_time;
@@ -1748,7 +1831,7 @@ index e8d676e..2b58728 100644
  /* NOTE: IDEState represents in fact one drive */
  typedef struct IDEState {
      /* ide config */
-@@ -429,6 +493,10 @@ typedef struct IDEState {
+@@ -429,6 +491,10 @@ typedef struct IDEState {
      int lba;
      int cd_sector_size;
      int atapi_dma; /* true if dma is requested for the packet cmd */
@@ -1759,7 +1842,7 @@ index e8d676e..2b58728 100644
      /* ATA DMA state */
      int io_buffer_size;
      QEMUSGList sg;
-@@ -940,6 +1008,8 @@ static inline void ide_set_irq(IDEState *s)
+@@ -940,6 +1006,8 @@ static inline void ide_set_irq(IDEState *s)
          if (bm) {
              bm->status |= BM_STATUS_INT;
          }
@@ -1768,7 +1851,7 @@ index e8d676e..2b58728 100644
          qemu_irq_raise(s->irq);
      }
  }
-@@ -1321,9 +1391,9 @@ static void ide_sector_write(IDEState *s)
+@@ -1321,9 +1389,9 @@ static void ide_sector_write(IDEState *s)
             that at the expense of slower write performances. Use this
             option _only_ to install Windows 2000. You must disable it
             for normal use. */
@@ -1780,7 +1863,7 @@ index e8d676e..2b58728 100644
  #endif
      {
          ide_set_irq(s);
-@@ -1499,6 +1569,13 @@ static inline int ube16_to_cpu(const uint8_t *buf)
+@@ -1499,6 +1567,13 @@ static inline int ube16_to_cpu(const uint8_t *buf)
      return (buf[0] << 8) | buf[1];
  }
  
@@ -1794,7 +1877,7 @@ index e8d676e..2b58728 100644
  static inline int ube32_to_cpu(const uint8_t *buf)
  {
      return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-@@ -1567,12 +1644,14 @@ static void ide_atapi_io_error(IDEState *s, int ret)
+@@ -1567,12 +1642,14 @@ static void ide_atapi_io_error(IDEState *s, int ret)
  static void ide_atapi_cmd_reply_end(IDEState *s)
  {
      int byte_count_limit, size, ret;
@@ -1809,7 +1892,7 @@ index e8d676e..2b58728 100644
      if (s->packet_transfer_size <= 0) {
          /* end of transfer */
          ide_transfer_stop(s);
-@@ -1582,63 +1661,65 @@ static void ide_atapi_cmd_reply_end(IDEState *s)
+@@ -1582,63 +1659,65 @@ static void ide_atapi_cmd_reply_end(IDEState *s)
  #ifdef DEBUG_IDE_ATAPI
          printf("status=0x%x\n", s->status);
  #endif
@@ -1926,7 +2009,7 @@ index e8d676e..2b58728 100644
      }
  }
  
-@@ -1882,6 +1963,10 @@ static int ide_dvd_read_structure(IDEState *s, int format,
+@@ -1882,6 +1961,10 @@ static int ide_dvd_read_structure(IDEState *s, int format,
      }
  }
  
@@ -1937,7 +2020,7 @@ index e8d676e..2b58728 100644
  static void ide_atapi_cmd(IDEState *s)
  {
      const uint8_t *packet;
-@@ -1922,7 +2007,6 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -1922,7 +2005,6 @@ static void ide_atapi_cmd(IDEState *s)
                                  ASC_MEDIUM_NOT_PRESENT);
          }
          break;
@@ -1945,7 +2028,7 @@ index e8d676e..2b58728 100644
      case GPCMD_MODE_SENSE_10:
          {
              int action, code;
-@@ -1935,9 +2019,9 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -1935,9 +2017,9 @@ static void ide_atapi_cmd(IDEState *s)
              switch(action) {
              case 0: /* current values */
                  switch(code) {
@@ -1957,7 +2040,7 @@ index e8d676e..2b58728 100644
                      buf[3] = 0;
                      buf[4] = 0;
                      buf[5] = 0;
-@@ -1954,17 +2038,17 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -1954,17 +2036,17 @@ static void ide_atapi_cmd(IDEState *s)
                      buf[15] = 0x00;
                      ide_atapi_cmd_reply(s, 16, max_len);
                      break;
@@ -1979,7 +2062,7 @@ index e8d676e..2b58728 100644
                      buf[10] = 0x00;
                      buf[11] = 0x00;
  
-@@ -1987,6 +2071,7 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -1987,6 +2069,7 @@ static void ide_atapi_cmd(IDEState *s)
                      buf[27] = 0;
                      ide_atapi_cmd_reply(s, 28, max_len);
                      break;
@@ -1987,7 +2070,7 @@ index e8d676e..2b58728 100644
                  default:
                      goto error_cmd;
                  }
-@@ -2110,7 +2195,7 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -2110,7 +2193,7 @@ static void ide_atapi_cmd(IDEState *s)
          break;
      case GPCMD_MECHANISM_STATUS:
          {
@@ -1996,7 +2079,7 @@ index e8d676e..2b58728 100644
              cpu_to_ube16(buf, 0);
              /* no current LBA */
              buf[2] = 0;
-@@ -2125,7 +2210,6 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -2125,7 +2208,6 @@ static void ide_atapi_cmd(IDEState *s)
          {
              int format, msf, start_track, len;
              uint64_t total_sectors;
@@ -2004,7 +2087,7 @@ index e8d676e..2b58728 100644
              bdrv_get_geometry(s->bs, &total_sectors);
              total_sectors >>= 2;
              if (total_sectors == 0) {
-@@ -2279,7 +2363,7 @@ static void ide_atapi_cmd(IDEState *s)
+@@ -2279,7 +2361,7 @@ static void ide_atapi_cmd(IDEState *s)
                  max_len = 512;
  
              memset(buf, 0, max_len);
@@ -2013,7 +2096,7 @@ index e8d676e..2b58728 100644
               * the number of sectors from the media tells us which profile
               * to use as current.  0 means there is no media
               */
-@@ -2484,7 +2568,11 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+@@ -2484,7 +2566,11 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
  
          /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
          if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
@@ -2025,7 +2108,7 @@ index e8d676e..2b58728 100644
  
          switch(val) {
          case WIN_IDENTIFY:
-@@ -2727,7 +2815,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+@@ -2727,7 +2813,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
              else
                  s->status = READY_STAT | SEEK_STAT;
              s->error = 0x01; /* Device 0 passed, Device 1 passed or not
@@ -2034,7 +2117,7 @@ index e8d676e..2b58728 100644
                                */
              ide_set_irq(s);
              break;
-@@ -2748,7 +2836,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+@@ -2748,7 +2834,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
              s->atapi_dma = s->feature & 1;
              s->nsector = 1;
              ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
@@ -2043,7 +2126,7 @@ index e8d676e..2b58728 100644
              break;
          /* CF-ATA commands */
          case CFA_REQ_EXT_ERROR_CODE:
-@@ -3133,8 +3221,21 @@ static void ide_init2(IDEState *ide_state,
+@@ -3133,8 +3219,21 @@ static void ide_init2(IDEState *ide_state,
  
              if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
                  s->is_cdrom = 1;
@@ -2052,7 +2135,7 @@ index e8d676e..2b58728 100644
              }
 +#ifdef __linux__
 +            else if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM_PT) {
-+              ide_atapi_pt_setup_sgio_thread(s);
++                ide_atapi_pt_setup_sgio_thread(s);
 +                //              BDRVRawState *raw_state = s->bs->opaque;
 +                s->is_cdrom = 1;
 +                s->atapi_cmd = ide_atapi_pt_cmd;
@@ -2066,7 +2149,7 @@ index e8d676e..2b58728 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 f3b0dae..c9c7c7f 100644
+index cf7ad19..50f3a46 100644
 --- a/vl.c
 +++ b/vl.c
 @@ -2192,8 +2192,10 @@ static int bt_parse(const char *opt)