From: Andrew McNeil Date: Fri, 20 Nov 2009 15:06:36 +0000 (+0000) Subject: CD claims exclusive lock of write and releases on eject X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=a2933d97520533762e1c728b035a6a8583acb692;p=xenclient%2Fioemu-pq.git CD claims exclusive lock of write and releases on eject --- diff --git a/master/atapi-pass-through b/master/atapi-pass-through index fa009a8..a01939c 100644 --- a/master/atapi-pass-through +++ b/master/atapi-pass-through @@ -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 +#include +#include @@ -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)