+#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>
+#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.
+ return sense_data_texts[i].text;
+}
+
-+
+static void atapi_dprintf(const char *fmt, ...)
+{
+ struct stat st;
+ 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));
+ 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");
+ }
+}
+
+ * 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);
+ 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;
+
+ 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);
+ }
+}
+
++
++/* 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];
+
+ 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 */
+ }
+
+ 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 */
+ {
+ /* 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;
+ 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;
+}
+
+
++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)
+ {
+ 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:
+ 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:
+ 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;
+ }
+
+ 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;
+ }
+
+ {
+ 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 @@
#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
+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;
/* 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 */
/* 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;
}
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. */
#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];
}
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;
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
}
}
-@@ -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,
}
}
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;
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) {
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;
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;
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:
{
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;
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);
* 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)
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
*/
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,
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;
}
+#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;
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)