diff mbox

[RFC] ATAPI-SCSI bridge GSoC project

Message ID 1437245366-20156-1-git-send-email-abezzubikov@ispras.ru
State New
Headers show

Commit Message

Aleksandr Bezzubikov July 18, 2015, 6:49 p.m. UTC
atapi: ATAPI-SCSI bridge device created
       private SCSI bus added to bridge
       ATAPI inquiry command can use a bridge
---
 hw/ide/atapi.c         |  36 +++++--
 hw/ide/core.c          | 207 +++++++++++++++++++--------------------
 hw/ide/internal.h      | 257 +++++++++++++++++++++++++------------------------
 hw/ide/qdev.c          |  43 ++++++++-
 hw/scsi/scsi-disk.c    |   4 +-
 include/hw/scsi/scsi.h |   6 +-
 6 files changed, 310 insertions(+), 243 deletions(-)

Comments

Thomas Huth July 20, 2015, 6:59 a.m. UTC | #1
On 18/07/15 20:49, Alexander Bezzubikov wrote:
> atapi: ATAPI-SCSI bridge device created
>        private SCSI bus added to bridge
>        ATAPI inquiry command can use a bridge

 Hi!

Not everybody is familiar with your GSoC project, so it would be great
if you could be a little bit more verbose in your patch description.

Unfortunately, your patch also has some style issues, please make sure
to read http://qemu-project.org/Contribute/SubmitAPatch first.
Especially:
- Your patch lacks a Signed-off-by line
- scripts/checkpatch.pl reports quite a bunch of errors
- Make sure to CC the right maintainers

Hope that helps,
 Thomas
Stefan Hajnoczi July 20, 2015, 11:03 a.m. UTC | #2
On Sat, Jul 18, 2015 at 09:49:26PM +0300, Alexander Bezzubikov wrote:
> atapi: ATAPI-SCSI bridge device created
>        private SCSI bus added to bridge
>        ATAPI inquiry command can use a bridge

Multiple items is a clue that this patch should be split up into patches
with smaller logical changes.

That will also allow you to write commit descriptions that give the
rationale for the changes.
diff mbox

Patch

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 950e311..e931e0e 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -635,6 +635,20 @@  static void cmd_request_sense(IDEState *s, uint8_t *buf)
     ide_atapi_cmd_reply(s, 18, max_len);
 }
 
+static void inquiry_wrapper(IDEState *s, uint8_t *buf)
+{
+    IDEDevice *dev = s->bus->master;
+    SCSIDevice *scsi_dev = scsi_device_find(&dev->scsi_bus, 0, 0, 0);
+    SCSIRequest *req = scsi_req_new(scsi_dev, 0, 0, buf, NULL);
+    
+    int max_len = scsi_disk_emulate_inquiry(req, buf);
+    int idx = 36;
+    
+    ide_atapi_cmd_reply(s, idx, max_len);
+    
+    return;
+}
+
 static void cmd_inquiry(IDEState *s, uint8_t *buf)
 {
     uint8_t page_code = buf[2];
@@ -1170,7 +1184,7 @@  enum {
     CHECK_READY = 0x02,
 };
 
-static const struct {
+static const struct atapi_command {
     void (*handler)(IDEState *s, uint8_t *buf);
     int flags;
 } atapi_cmd_table[0x100] = {
@@ -1193,11 +1207,17 @@  static const struct {
     [ 0xbd ] = { cmd_mechanism_status,              0 },
     [ 0xbe ] = { cmd_read_cd,                       CHECK_READY },
     /* [1] handler detects and reports not ready condition itself */
-};
+},
+  bridge_cmd_table[0x100] = {
+    [ 0x12 ] = { inquiry_wrapper,                   ALLOW_UA }
+  };
 
 void ide_atapi_cmd(IDEState *s)
 {
     uint8_t *buf;
+    
+    const struct atapi_command *cmd_table = (s->drive_kind == IDE_BRIDGE &&
+        bridge_cmd_table[s->io_buffer[0]].handler) ? bridge_cmd_table : atapi_cmd_table;
 
     buf = s->io_buffer;
 #ifdef DEBUG_IDE_ATAPI
@@ -1217,7 +1237,7 @@  void ide_atapi_cmd(IDEState *s)
      * here, is pending.
      */
     if (s->sense_key == UNIT_ATTENTION &&
-        !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
+        !(cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
         ide_atapi_cmd_check_status(s);
         return;
     }
@@ -1228,7 +1248,7 @@  void ide_atapi_cmd(IDEState *s)
      * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
      * states rely on this behavior.
      */
-    if (!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA) &&
+    if (!(cmd_table[s->io_buffer[0]].flags & ALLOW_UA) &&
         !s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) {
 
         if (s->cdrom_changed == 1) {
@@ -1243,7 +1263,7 @@  void ide_atapi_cmd(IDEState *s)
     }
 
     /* Report a Not Ready condition if appropriate for the command */
-    if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
+    if ((cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
         (!media_present(s) || !blk_is_inserted(s->blk)))
     {
         ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
@@ -1251,10 +1271,10 @@  void ide_atapi_cmd(IDEState *s)
     }
 
     /* Execute the command */
-    if (atapi_cmd_table[s->io_buffer[0]].handler) {
-        atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
+    if (cmd_table[s->io_buffer[0]].handler) {
+        cmd_table[s->io_buffer[0]].handler(s, buf);
         return;
     }
 
     ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
-}
+}
\ No newline at end of file
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 122e955..1097553 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -288,52 +288,52 @@  static void ide_cfata_identify(IDEState *s)
 
     cur_sec = s->cylinders * s->heads * s->sectors;
 
-    put_le16(p + 0, 0x848a);			/* CF Storage Card signature */
-    put_le16(p + 1, s->cylinders);		/* Default cylinders */
-    put_le16(p + 3, s->heads);			/* Default heads */
-    put_le16(p + 6, s->sectors);		/* Default sectors per track */
+    put_le16(p + 0, 0x848a);            /* CF Storage Card signature */
+    put_le16(p + 1, s->cylinders);      /* Default cylinders */
+    put_le16(p + 3, s->heads);          /* Default heads */
+    put_le16(p + 6, s->sectors);        /* Default sectors per track */
     /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */
     /* *(p + 8) := nb_sectors       -- see ide_cfata_identify_size */
     padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
-    put_le16(p + 22, 0x0004);			/* ECC bytes */
-    padstr((char *) (p + 23), s->version, 8);	/* Firmware Revision */
+    put_le16(p + 22, 0x0004);           /* ECC bytes */
+    padstr((char *) (p + 23), s->version, 8);   /* Firmware Revision */
     padstr((char *) (p + 27), s->drive_model_str, 40);/* Model number */
 #if MAX_MULT_SECTORS > 1
     put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
 #else
     put_le16(p + 47, 0x0000);
 #endif
-    put_le16(p + 49, 0x0f00);			/* Capabilities */
-    put_le16(p + 51, 0x0002);			/* PIO cycle timing mode */
-    put_le16(p + 52, 0x0001);			/* DMA cycle timing mode */
-    put_le16(p + 53, 0x0003);			/* Translation params valid */
-    put_le16(p + 54, s->cylinders);		/* Current cylinders */
-    put_le16(p + 55, s->heads);			/* Current heads */
-    put_le16(p + 56, s->sectors);		/* Current sectors */
-    put_le16(p + 57, cur_sec);			/* Current capacity */
-    put_le16(p + 58, cur_sec >> 16);		/* Current capacity */
-    if (s->mult_sectors)			/* Multiple sector setting */
+    put_le16(p + 49, 0x0f00);           /* Capabilities */
+    put_le16(p + 51, 0x0002);           /* PIO cycle timing mode */
+    put_le16(p + 52, 0x0001);           /* DMA cycle timing mode */
+    put_le16(p + 53, 0x0003);           /* Translation params valid */
+    put_le16(p + 54, s->cylinders);     /* Current cylinders */
+    put_le16(p + 55, s->heads);         /* Current heads */
+    put_le16(p + 56, s->sectors);       /* Current sectors */
+    put_le16(p + 57, cur_sec);          /* Current capacity */
+    put_le16(p + 58, cur_sec >> 16);        /* Current capacity */
+    if (s->mult_sectors)            /* Multiple sector setting */
         put_le16(p + 59, 0x100 | s->mult_sectors);
     /* *(p + 60) := nb_sectors       -- see ide_cfata_identify_size */
     /* *(p + 61) := nb_sectors >> 16 -- see ide_cfata_identify_size */
-    put_le16(p + 63, 0x0203);			/* Multiword DMA capability */
-    put_le16(p + 64, 0x0001);			/* Flow Control PIO support */
-    put_le16(p + 65, 0x0096);			/* Min. Multiword DMA cycle */
-    put_le16(p + 66, 0x0096);			/* Rec. Multiword DMA cycle */
-    put_le16(p + 68, 0x00b4);			/* Min. PIO cycle time */
-    put_le16(p + 82, 0x400c);			/* Command Set supported */
-    put_le16(p + 83, 0x7068);			/* Command Set supported */
-    put_le16(p + 84, 0x4000);			/* Features supported */
-    put_le16(p + 85, 0x000c);			/* Command Set enabled */
-    put_le16(p + 86, 0x7044);			/* Command Set enabled */
-    put_le16(p + 87, 0x4000);			/* Features enabled */
-    put_le16(p + 91, 0x4060);			/* Current APM level */
-    put_le16(p + 129, 0x0002);			/* Current features option */
-    put_le16(p + 130, 0x0005);			/* Reassigned sectors */
-    put_le16(p + 131, 0x0001);			/* Initial power mode */
-    put_le16(p + 132, 0x0000);			/* User signature */
-    put_le16(p + 160, 0x8100);			/* Power requirement */
-    put_le16(p + 161, 0x8001);			/* CF command set */
+    put_le16(p + 63, 0x0203);           /* Multiword DMA capability */
+    put_le16(p + 64, 0x0001);           /* Flow Control PIO support */
+    put_le16(p + 65, 0x0096);           /* Min. Multiword DMA cycle */
+    put_le16(p + 66, 0x0096);           /* Rec. Multiword DMA cycle */
+    put_le16(p + 68, 0x00b4);           /* Min. PIO cycle time */
+    put_le16(p + 82, 0x400c);           /* Command Set supported */
+    put_le16(p + 83, 0x7068);           /* Command Set supported */
+    put_le16(p + 84, 0x4000);           /* Features supported */
+    put_le16(p + 85, 0x000c);           /* Command Set enabled */
+    put_le16(p + 86, 0x7044);           /* Command Set enabled */
+    put_le16(p + 87, 0x4000);           /* Features enabled */
+    put_le16(p + 91, 0x4060);           /* Current APM level */
+    put_le16(p + 129, 0x0002);          /* Current features option */
+    put_le16(p + 130, 0x0005);          /* Reassigned sectors */
+    put_le16(p + 131, 0x0001);          /* Initial power mode */
+    put_le16(p + 132, 0x0000);          /* User signature */
+    put_le16(p + 160, 0x8100);          /* Power requirement */
+    put_le16(p + 161, 0x8001);          /* CF command set */
 
     ide_cfata_identify_size(s);
     s->identify_set = 1;
@@ -348,7 +348,7 @@  static void ide_set_signature(IDEState *s)
     /* put signature */
     s->nsector = 1;
     s->sector = 1;
-    if (s->drive_kind == IDE_CD) {
+    if (s->drive_kind == IDE_CD || s->drive_kind == IDE_BRIDGE) {
         s->lcyl = 0x14;
         s->hcyl = 0xeb;
     } else if (s->blk) {
@@ -500,16 +500,16 @@  int64_t ide_get_sector(IDEState *s)
     int64_t sector_num;
     if (s->select & 0x40) {
         /* lba */
-	if (!s->lba48) {
-	    sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
-		(s->lcyl << 8) | s->sector;
-	} else {
-	    sector_num = ((int64_t)s->hob_hcyl << 40) |
-		((int64_t) s->hob_lcyl << 32) |
-		((int64_t) s->hob_sector << 24) |
-		((int64_t) s->hcyl << 16) |
-		((int64_t) s->lcyl << 8) | s->sector;
-	}
+    if (!s->lba48) {
+        sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+        (s->lcyl << 8) | s->sector;
+    } else {
+        sector_num = ((int64_t)s->hob_hcyl << 40) |
+        ((int64_t) s->hob_lcyl << 32) |
+        ((int64_t) s->hob_sector << 24) |
+        ((int64_t) s->hcyl << 16) |
+        ((int64_t) s->lcyl << 8) | s->sector;
+    }
     } else {
         sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
             (s->select & 0x0f) * s->sectors + (s->sector - 1);
@@ -521,19 +521,19 @@  void ide_set_sector(IDEState *s, int64_t sector_num)
 {
     unsigned int cyl, r;
     if (s->select & 0x40) {
-	if (!s->lba48) {
+    if (!s->lba48) {
             s->select = (s->select & 0xf0) | (sector_num >> 24);
             s->hcyl = (sector_num >> 16);
             s->lcyl = (sector_num >> 8);
             s->sector = (sector_num);
-	} else {
-	    s->sector = sector_num;
-	    s->lcyl = sector_num >> 8;
-	    s->hcyl = sector_num >> 16;
-	    s->hob_sector = sector_num >> 24;
-	    s->hob_lcyl = sector_num >> 32;
-	    s->hob_hcyl = sector_num >> 40;
-	}
+    } else {
+        s->sector = sector_num;
+        s->lcyl = sector_num >> 8;
+        s->hcyl = sector_num >> 16;
+        s->hob_sector = sector_num >> 24;
+        s->hob_lcyl = sector_num >> 32;
+        s->hob_hcyl = sector_num >> 40;
+    }
     } else {
         cyl = sector_num / (s->heads * s->sectors);
         r = sector_num % (s->heads * s->sectors);
@@ -946,13 +946,13 @@  static void ide_cfata_metadata_inquiry(IDEState *s)
     memset(p, 0, 0x200);
     spd = ((s->mdata_size - 1) >> 9) + 1;
 
-    put_le16(p + 0, 0x0001);			/* Data format revision */
-    put_le16(p + 1, 0x0000);			/* Media property: silicon */
-    put_le16(p + 2, s->media_changed);		/* Media status */
-    put_le16(p + 3, s->mdata_size & 0xffff);	/* Capacity in bytes (low) */
-    put_le16(p + 4, s->mdata_size >> 16);	/* Capacity in bytes (high) */
-    put_le16(p + 5, spd & 0xffff);		/* Sectors per device (low) */
-    put_le16(p + 6, spd >> 16);			/* Sectors per device (high) */
+    put_le16(p + 0, 0x0001);            /* Data format revision */
+    put_le16(p + 1, 0x0000);            /* Media property: silicon */
+    put_le16(p + 2, s->media_changed);      /* Media status */
+    put_le16(p + 3, s->mdata_size & 0xffff);    /* Capacity in bytes (low) */
+    put_le16(p + 4, s->mdata_size >> 16);   /* Capacity in bytes (high) */
+    put_le16(p + 5, spd & 0xffff);      /* Sectors per device (low) */
+    put_le16(p + 6, spd >> 16);         /* Sectors per device (high) */
 }
 
 static void ide_cfata_metadata_read(IDEState *s)
@@ -968,7 +968,7 @@  static void ide_cfata_metadata_read(IDEState *s)
     p = (uint16_t *) s->io_buffer;
     memset(p, 0, 0x200);
 
-    put_le16(p + 0, s->media_changed);		/* Media status */
+    put_le16(p + 0, s->media_changed);      /* Media status */
     memcpy(p + 1, s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9),
                     MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9),
                                     s->nsector << 9), 0x200 - 2));
@@ -1033,17 +1033,17 @@  static void ide_cmd_lba48_transform(IDEState *s, int lba48)
      * full sector count in ->nsector and ignore ->hob_nsector from now
      */
     if (!s->lba48) {
-	if (!s->nsector)
-	    s->nsector = 256;
+    if (!s->nsector)
+        s->nsector = 256;
     } else {
-	if (!s->nsector && !s->hob_nsector)
-	    s->nsector = 65536;
-	else {
-	    int lo = s->nsector;
-	    int hi = s->hob_nsector;
+    if (!s->nsector && !s->hob_nsector)
+        s->nsector = 65536;
+    else {
+        int lo = s->nsector;
+        int hi = s->hob_nsector;
 
-	    s->nsector = (hi << 8) | lo;
-	}
+        s->nsector = (hi << 8) | lo;
+    }
     }
 }
 
@@ -1072,43 +1072,43 @@  void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
     case 0:
         break;
     case 1:
-	ide_clear_hob(bus);
+    ide_clear_hob(bus);
         /* NOTE: data is written to the two drives */
-	bus->ifs[0].hob_feature = bus->ifs[0].feature;
-	bus->ifs[1].hob_feature = bus->ifs[1].feature;
+    bus->ifs[0].hob_feature = bus->ifs[0].feature;
+    bus->ifs[1].hob_feature = bus->ifs[1].feature;
         bus->ifs[0].feature = val;
         bus->ifs[1].feature = val;
         break;
     case 2:
-	ide_clear_hob(bus);
-	bus->ifs[0].hob_nsector = bus->ifs[0].nsector;
-	bus->ifs[1].hob_nsector = bus->ifs[1].nsector;
+    ide_clear_hob(bus);
+    bus->ifs[0].hob_nsector = bus->ifs[0].nsector;
+    bus->ifs[1].hob_nsector = bus->ifs[1].nsector;
         bus->ifs[0].nsector = val;
         bus->ifs[1].nsector = val;
         break;
     case 3:
-	ide_clear_hob(bus);
-	bus->ifs[0].hob_sector = bus->ifs[0].sector;
-	bus->ifs[1].hob_sector = bus->ifs[1].sector;
+    ide_clear_hob(bus);
+    bus->ifs[0].hob_sector = bus->ifs[0].sector;
+    bus->ifs[1].hob_sector = bus->ifs[1].sector;
         bus->ifs[0].sector = val;
         bus->ifs[1].sector = val;
         break;
     case 4:
-	ide_clear_hob(bus);
-	bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl;
-	bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl;
+    ide_clear_hob(bus);
+    bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl;
+    bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl;
         bus->ifs[0].lcyl = val;
         bus->ifs[1].lcyl = val;
         break;
     case 5:
-	ide_clear_hob(bus);
-	bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl;
-	bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl;
+    ide_clear_hob(bus);
+    bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl;
+    bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl;
         bus->ifs[0].hcyl = val;
         bus->ifs[1].hcyl = val;
         break;
     case 6:
-	/* FIXME: HOB readback uses bit 7 */
+    /* FIXME: HOB readback uses bit 7 */
         bus->ifs[0].select = (val & ~0x10) | 0xa0;
         bus->ifs[1].select = (val | 0x10) | 0xa0;
         /* select drive */
@@ -1144,7 +1144,7 @@  static bool cmd_data_set_management(IDEState *s, uint8_t cmd)
 
 static bool cmd_identify(IDEState *s, uint8_t cmd)
 {
-    if (s->blk && s->drive_kind != IDE_CD) {
+    if (s->blk && s->drive_kind != IDE_CD && s->drive_kind != IDE_BRIDGE) {
         if (s->drive_kind != IDE_CFATA) {
             ide_identify(s);
         } else {
@@ -1155,7 +1155,7 @@  static bool cmd_identify(IDEState *s, uint8_t cmd)
         ide_set_irq(s->bus);
         return false;
     } else {
-        if (s->drive_kind == IDE_CD) {
+        if (s->drive_kind == IDE_CD || s->drive_kind == IDE_BRIDGE) {
             ide_set_signature(s);
         }
         ide_abort_command(s);
@@ -1232,7 +1232,7 @@  static bool cmd_read_pio(IDEState *s, uint8_t cmd)
 {
     bool lba48 = (cmd == WIN_READ_EXT);
 
-    if (s->drive_kind == IDE_CD) {
+    if (s->drive_kind == IDE_CD || s->drive_kind == IDE_BRIDGE) {
         ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */
         ide_abort_command(s);
         return true;
@@ -1426,7 +1426,7 @@  static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd)
 {
     ide_set_signature(s);
 
-    if (s->drive_kind == IDE_CD) {
+    if (s->drive_kind == IDE_CD || s->drive_kind == IDE_BRIDGE) {
         s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet
                         * devices to return a clear status register
                         * with READY_STAT *not* set. */
@@ -1731,7 +1731,7 @@  abort_cmd:
 }
 
 #define HD_OK (1u << IDE_HD)
-#define CD_OK (1u << IDE_CD)
+#define CD_OK ((1u << IDE_CD) | (1u << IDE_BRIDGE))
 #define CFA_OK (1u << IDE_CFATA)
 #define HD_CFA_OK (HD_OK | CFA_OK)
 #define ALL_OK (HD_OK | CD_OK | CFA_OK)
@@ -1874,7 +1874,7 @@  uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         } else if (!hob) {
             ret = s->error;
         } else {
-	    ret = s->hob_feature;
+        ret = s->hob_feature;
         }
         break;
     case 2:
@@ -1883,7 +1883,7 @@  uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         } else if (!hob) {
             ret = s->nsector & 0xff;
         } else {
-	    ret = s->hob_nsector;
+        ret = s->hob_nsector;
         }
         break;
     case 3:
@@ -1892,7 +1892,7 @@  uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         } else if (!hob) {
             ret = s->sector;
         } else {
-	    ret = s->hob_sector;
+        ret = s->hob_sector;
         }
         break;
     case 4:
@@ -1901,7 +1901,7 @@  uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         } else if (!hob) {
             ret = s->lcyl;
         } else {
-	    ret = s->hob_lcyl;
+        ret = s->hob_lcyl;
         }
         break;
     case 5:
@@ -1910,7 +1910,7 @@  uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         } else if (!hob) {
             ret = s->hcyl;
         } else {
-	    ret = s->hob_hcyl;
+        ret = s->hob_hcyl;
         }
         break;
     case 6:
@@ -1978,7 +1978,7 @@  void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val)
         /* high to low */
         for(i = 0;i < 2; i++) {
             s = &bus->ifs[i];
-            if (s->drive_kind == IDE_CD)
+            if (s->drive_kind == IDE_CD || s->drive_kind == IDE_BRIDGE)
                 s->status = 0x00; /* NOTE: READY is _not_ set */
             else
                 s->status = READY_STAT | SEEK_STAT;
@@ -2210,7 +2210,7 @@  static void ide_resize_cb(void *opaque)
         ide_cfata_identify_size(s);
     } else {
         /* IDE_CD uses a different set of callbacks entirely. */
-        assert(s->drive_kind != IDE_CD);
+        assert(s->drive_kind != IDE_CD && s->drive_kind != IDE_BRIDGE);
         ide_identify_size(s);
     }
 }
@@ -2250,7 +2250,7 @@  int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
     s->smart_autosave = 1;
     s->smart_errors = 0;
     s->smart_selftest_count = 0;
-    if (kind == IDE_CD) {
+    if (kind == IDE_CD || kind == IDE_BRIDGE) {
         blk_set_dev_ops(blk, &ide_cd_block_ops, s);
         blk_set_guest_block_size(blk, 2048);
     } else {
@@ -2277,6 +2277,9 @@  int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
         case IDE_CD:
             strcpy(s->drive_model_str, "QEMU DVD-ROM");
             break;
+        case IDE_BRIDGE:
+            strcpy(s->drive_model_str, "QEMU ATAPI-SCSI bridge");
+            break;
         case IDE_CFATA:
             strcpy(s->drive_model_str, "QEMU MICRODRIVE");
             break;
@@ -2598,7 +2601,7 @@  static const VMStateDescription vmstate_ide_drive_pio_state = {
     .fields = (VMStateField[]) {
         VMSTATE_INT32(req_nb_sectors, IDEState),
         VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
-			     vmstate_info_uint8, uint8_t),
+                 vmstate_info_uint8, uint8_t),
         VMSTATE_INT32(cur_io_buffer_offset, IDEState),
         VMSTATE_INT32(cur_io_buffer_len, IDEState),
         VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
@@ -2697,4 +2700,4 @@  void ide_drive_get(DriveInfo **hd, int n)
     for (i = 0; i < n; i++) {
         hd[i] = drive_get_by_index(IF_IDE, i);
     }
-}
+}
\ No newline at end of file
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 30fdcbc..d5c5b70 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -12,6 +12,7 @@ 
 #include "sysemu/sysemu.h"
 #include "hw/block/block.h"
 #include "block/scsi.h"
+#include "hw/scsi/scsi.h"
 
 /* debug IDE devices */
 //#define DEBUG_IDE
@@ -29,81 +30,81 @@  typedef struct IDEDMAOps IDEDMAOps;
 #define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS)
 
 /* Bits of HD_STATUS */
-#define ERR_STAT		0x01
-#define INDEX_STAT		0x02
-#define ECC_STAT		0x04	/* Corrected error */
-#define DRQ_STAT		0x08
-#define SEEK_STAT		0x10
-#define SRV_STAT		0x10
-#define WRERR_STAT		0x20
-#define READY_STAT		0x40
-#define BUSY_STAT		0x80
+#define ERR_STAT        0x01
+#define INDEX_STAT      0x02
+#define ECC_STAT        0x04    /* Corrected error */
+#define DRQ_STAT        0x08
+#define SEEK_STAT       0x10
+#define SRV_STAT        0x10
+#define WRERR_STAT      0x20
+#define READY_STAT      0x40
+#define BUSY_STAT       0x80
 
 /* Bits for HD_ERROR */
-#define MARK_ERR		0x01	/* Bad address mark */
-#define TRK0_ERR		0x02	/* couldn't find track 0 */
-#define ABRT_ERR		0x04	/* Command aborted */
-#define MCR_ERR			0x08	/* media change request */
-#define ID_ERR			0x10	/* ID field not found */
-#define MC_ERR			0x20	/* media changed */
-#define ECC_ERR			0x40	/* Uncorrectable ECC error */
-#define BBD_ERR			0x80	/* pre-EIDE meaning:  block marked bad */
-#define ICRC_ERR		0x80	/* new meaning:  CRC error during transfer */
+#define MARK_ERR        0x01    /* Bad address mark */
+#define TRK0_ERR        0x02    /* couldn't find track 0 */
+#define ABRT_ERR        0x04    /* Command aborted */
+#define MCR_ERR         0x08    /* media change request */
+#define ID_ERR          0x10    /* ID field not found */
+#define MC_ERR          0x20    /* media changed */
+#define ECC_ERR         0x40    /* Uncorrectable ECC error */
+#define BBD_ERR         0x80    /* pre-EIDE meaning:  block marked bad */
+#define ICRC_ERR        0x80    /* new meaning:  CRC error during transfer */
 
 /* Bits of HD_NSECTOR */
-#define CD			0x01
-#define IO			0x02
-#define REL			0x04
-#define TAG_MASK		0xf8
+#define CD          0x01
+#define IO          0x02
+#define REL         0x04
+#define TAG_MASK        0xf8
 
 #define IDE_CMD_RESET           0x04
 #define IDE_CMD_DISABLE_IRQ     0x02
 
 /* ACS-2 T13/2015-D Table B.2 Command codes */
-#define WIN_NOP				0x00
+#define WIN_NOP             0x00
 /* reserved                             0x01..0x02 */
-#define CFA_REQ_EXT_ERROR_CODE		0x03 /* CFA Request Extended Error Code */
+#define CFA_REQ_EXT_ERROR_CODE      0x03 /* CFA Request Extended Error Code */
 /* reserved                             0x04..0x05 */
 #define WIN_DSM                         0x06
 /* reserved                             0x07 */
-#define WIN_DEVICE_RESET		0x08
+#define WIN_DEVICE_RESET        0x08
 /* reserved                             0x09..0x0a */
 /* REQUEST SENSE DATA EXT               0x0B */
 /* reserved                             0x0C..0x0F */
 #define WIN_RECAL                       0x10 /* obsolete since ATA4 */
 /* obsolete since ATA3, retired in ATA4 0x11..0x1F */
-#define WIN_READ			0x20 /* 28-Bit */
+#define WIN_READ            0x20 /* 28-Bit */
 #define WIN_READ_ONCE                   0x21 /* 28-Bit w/o retries, obsolete since ATA5 */
 /* obsolete since ATA4                  0x22..0x23 */
-#define WIN_READ_EXT			0x24 /* 48-Bit */
-#define WIN_READDMA_EXT			0x25 /* 48-Bit */
+#define WIN_READ_EXT            0x24 /* 48-Bit */
+#define WIN_READDMA_EXT         0x25 /* 48-Bit */
 #define WIN_READDMA_QUEUED_EXT          0x26 /* 48-Bit, obsolete since ACS2 */
-#define WIN_READ_NATIVE_MAX_EXT		0x27 /* 48-Bit */
+#define WIN_READ_NATIVE_MAX_EXT     0x27 /* 48-Bit */
 /* reserved                             0x28 */
-#define WIN_MULTREAD_EXT		0x29 /* 48-Bit */
+#define WIN_MULTREAD_EXT        0x29 /* 48-Bit */
 /* READ STREAM DMA EXT                  0x2A */
 /* READ STREAM EXT                      0x2B */
 /* reserved                             0x2C..0x2E */
 /* READ LOG EXT                         0x2F */
-#define WIN_WRITE			0x30 /* 28-Bit */
+#define WIN_WRITE           0x30 /* 28-Bit */
 #define WIN_WRITE_ONCE                  0x31 /* 28-Bit w/o retries, obsolete since ATA5 */
 /* obsolete since ATA4                  0x32..0x33 */
-#define WIN_WRITE_EXT			0x34 /* 48-Bit */
-#define WIN_WRITEDMA_EXT		0x35 /* 48-Bit */
-#define WIN_WRITEDMA_QUEUED_EXT		0x36 /* 48-Bit */
+#define WIN_WRITE_EXT           0x34 /* 48-Bit */
+#define WIN_WRITEDMA_EXT        0x35 /* 48-Bit */
+#define WIN_WRITEDMA_QUEUED_EXT     0x36 /* 48-Bit */
 #define WIN_SET_MAX_EXT                 0x37 /* 48-Bit, obsolete since ACS2 */
-#define WIN_SET_MAX_EXT			0x37 /* 48-Bit */
-#define CFA_WRITE_SECT_WO_ERASE		0x38 /* CFA Write Sectors without erase */
-#define WIN_MULTWRITE_EXT		0x39 /* 48-Bit */
+#define WIN_SET_MAX_EXT         0x37 /* 48-Bit */
+#define CFA_WRITE_SECT_WO_ERASE     0x38 /* CFA Write Sectors without erase */
+#define WIN_MULTWRITE_EXT       0x39 /* 48-Bit */
 /* WRITE STREAM DMA EXT                 0x3A */
 /* WRITE STREAM EXT                     0x3B */
 #define WIN_WRITE_VERIFY                0x3C /* 28-Bit, obsolete since ATA4 */
 /* WRITE DMA FUA EXT                    0x3D */
 /* obsolete since ACS2                  0x3E */
 /* WRITE LOG EXT                        0x3F */
-#define WIN_VERIFY			0x40 /* 28-Bit - Read Verify Sectors */
+#define WIN_VERIFY          0x40 /* 28-Bit - Read Verify Sectors */
 #define WIN_VERIFY_ONCE                 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_VERIFY_EXT			0x42 /* 48-Bit */
+#define WIN_VERIFY_EXT          0x42 /* 48-Bit */
 /* reserved                             0x43..0x44 */
 /* WRITE UNCORRECTABLE EXT              0x45 */
 /* reserved                             0x46 */
@@ -125,11 +126,11 @@  typedef struct IDEDMAOps IDEDMAOps;
 #define WIN_SEEK                        0x70 /* obsolete since ATA7 */
 /* reserved                             0x71-0x7F */
 /* vendor specific                      0x80-0x86 */
-#define CFA_TRANSLATE_SECTOR		0x87 /* CFA Translate Sector */
+#define CFA_TRANSLATE_SECTOR        0x87 /* CFA Translate Sector */
 /* vendor specific                      0x88-0x8F */
-#define WIN_DIAGNOSE			0x90
+#define WIN_DIAGNOSE            0x90
 #define WIN_SPECIFY                     0x91 /* set drive geometry translation, obsolete since ATA6 */
-#define WIN_DOWNLOAD_MICROCODE		0x92
+#define WIN_DOWNLOAD_MICROCODE      0x92
 /* DOWNLOAD MICROCODE DMA               0x93 */
 #define WIN_STANDBYNOW2                 0x94 /* retired in ATA4 */
 #define WIN_IDLEIMMEDIATE2              0x95 /* force drive to become "ready", retired in ATA4 */
@@ -139,31 +140,31 @@  typedef struct IDEDMAOps IDEDMAOps;
 #define WIN_SLEEPNOW2                   0x99 /* retired in ATA4 */
 /* vendor specific                      0x9A */
 /* reserved                             0x9B..0x9F */
-#define WIN_PACKETCMD			0xA0 /* Send a packet command. */
-#define WIN_PIDENTIFY			0xA1 /* identify ATAPI device	*/
+#define WIN_PACKETCMD           0xA0 /* Send a packet command. */
+#define WIN_PIDENTIFY           0xA1 /* identify ATAPI device   */
 #define WIN_QUEUED_SERVICE              0xA2 /* obsolete since ACS2 */
 /* reserved                             0xA3..0xAF */
-#define WIN_SMART			0xB0 /* self-monitoring and reporting */
+#define WIN_SMART           0xB0 /* self-monitoring and reporting */
 /* Device Configuration Overlay         0xB1 */
 /* reserved                             0xB2..0xB3 */
 /* Sanitize Device                      0xB4 */
 /* reserved                             0xB5 */
 /* NV Cache                             0xB6 */
 /* reserved for CFA                     0xB7..0xBB */
-#define CFA_ACCESS_METADATA_STORAGE	0xB8
+#define CFA_ACCESS_METADATA_STORAGE 0xB8
 /* reserved                             0xBC..0xBF */
-#define CFA_ERASE_SECTORS       	0xC0 /* microdrives implement as NOP */
+#define CFA_ERASE_SECTORS           0xC0 /* microdrives implement as NOP */
 /* vendor specific                      0xC1..0xC3 */
-#define WIN_MULTREAD			0xC4 /* read sectors using multiple mode*/
-#define WIN_MULTWRITE			0xC5 /* write sectors using multiple mode */
-#define WIN_SETMULT			0xC6 /* enable/disable multiple mode */
+#define WIN_MULTREAD            0xC4 /* read sectors using multiple mode*/
+#define WIN_MULTWRITE           0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT         0xC6 /* enable/disable multiple mode */
 #define WIN_READDMA_QUEUED              0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */
-#define WIN_READDMA			0xC8 /* read sectors using DMA transfers */
+#define WIN_READDMA         0xC8 /* read sectors using DMA transfers */
 #define WIN_READDMA_ONCE                0xC9 /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_WRITEDMA			0xCA /* write sectors using DMA transfers */
+#define WIN_WRITEDMA            0xCA /* write sectors using DMA transfers */
 #define WIN_WRITEDMA_ONCE               0xCB /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_WRITEDMA_QUEUED		0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */
-#define CFA_WRITE_MULTI_WO_ERASE	0xCD /* CFA Write multiple without erase */
+#define WIN_WRITEDMA_QUEUED     0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */
+#define CFA_WRITE_MULTI_WO_ERASE    0xCD /* CFA Write multiple without erase */
 /* WRITE MULTIPLE FUA EXT               0xCE */
 /* reserved                             0xCF..0xDO */
 /* CHECK MEDIA CARD TYPE                0xD1 */
@@ -173,33 +174,33 @@  typedef struct IDEDMAOps IDEDMAOps;
 /* obsolete since ATA3, retired in ATA4 0xDB..0xDD */
 #define WIN_DOORLOCK                    0xDE /* lock door on removable drives, obsolete since ATA8 */
 #define WIN_DOORUNLOCK                  0xDF /* unlock door on removable drives, obsolete since ATA8 */
-#define WIN_STANDBYNOW1			0xE0
-#define WIN_IDLEIMMEDIATE		0xE1 /* force drive to become "ready" */
-#define WIN_STANDBY             	0xE2 /* Set device in Standby Mode */
-#define WIN_SETIDLE1			0xE3
-#define WIN_READ_BUFFER			0xE4 /* force read only 1 sector */
-#define WIN_CHECKPOWERMODE1		0xE5
-#define WIN_SLEEPNOW1			0xE6
-#define WIN_FLUSH_CACHE			0xE7
-#define WIN_WRITE_BUFFER		0xE8 /* force write only 1 sector */
+#define WIN_STANDBYNOW1         0xE0
+#define WIN_IDLEIMMEDIATE       0xE1 /* force drive to become "ready" */
+#define WIN_STANDBY                 0xE2 /* Set device in Standby Mode */
+#define WIN_SETIDLE1            0xE3
+#define WIN_READ_BUFFER         0xE4 /* force read only 1 sector */
+#define WIN_CHECKPOWERMODE1     0xE5
+#define WIN_SLEEPNOW1           0xE6
+#define WIN_FLUSH_CACHE         0xE7
+#define WIN_WRITE_BUFFER        0xE8 /* force write only 1 sector */
 /* READ BUFFER DMA                      0xE9 */
-#define WIN_FLUSH_CACHE_EXT		0xEA /* 48-Bit */
+#define WIN_FLUSH_CACHE_EXT     0xEA /* 48-Bit */
 /* WRITE BUFFER DMA                     0xEB */
-#define WIN_IDENTIFY			0xEC /* ask drive to identify itself	*/
+#define WIN_IDENTIFY            0xEC /* ask drive to identify itself    */
 #define WIN_MEDIAEJECT                  0xED /* obsolete since ATA8 */
 /* obsolete since ATA4                  0xEE */
-#define WIN_SETFEATURES			0xEF /* set special drive features */
+#define WIN_SETFEATURES         0xEF /* set special drive features */
 #define IBM_SENSE_CONDITION             0xF0 /* measure disk temperature, vendor specific */
-#define WIN_SECURITY_SET_PASS		0xF1
-#define WIN_SECURITY_UNLOCK		0xF2
-#define WIN_SECURITY_ERASE_PREPARE	0xF3
-#define WIN_SECURITY_ERASE_UNIT		0xF4
-#define WIN_SECURITY_FREEZE_LOCK	0xF5
+#define WIN_SECURITY_SET_PASS       0xF1
+#define WIN_SECURITY_UNLOCK     0xF2
+#define WIN_SECURITY_ERASE_PREPARE  0xF3
+#define WIN_SECURITY_ERASE_UNIT     0xF4
+#define WIN_SECURITY_FREEZE_LOCK    0xF5
 #define CFA_WEAR_LEVEL                  0xF5 /* microdrives implement as NOP; not specified in T13! */
-#define WIN_SECURITY_DISABLE		0xF6
+#define WIN_SECURITY_DISABLE        0xF6
 /* vendor specific                      0xF7 */
-#define WIN_READ_NATIVE_MAX		0xF8 /* return the native maximum address */
-#define WIN_SET_MAX			0xF9
+#define WIN_READ_NATIVE_MAX     0xF8 /* return the native maximum address */
+#define WIN_SET_MAX         0xF9
 /* vendor specific                      0xFA..0xFF */
 
 /* set to 1 set disable mult support */
@@ -220,68 +221,68 @@  typedef struct IDEDMAOps IDEDMAOps;
 
 /* The generic packet command opcodes for CD/DVD Logical Units,
  * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-#define GPCMD_BLANK			    0xa1
-#define GPCMD_CLOSE_TRACK		    0x5b
-#define GPCMD_FLUSH_CACHE		    0x35
-#define GPCMD_FORMAT_UNIT		    0x04
-#define GPCMD_GET_CONFIGURATION		    0x46
+#define GPCMD_BLANK             0xa1
+#define GPCMD_CLOSE_TRACK           0x5b
+#define GPCMD_FLUSH_CACHE           0x35
+#define GPCMD_FORMAT_UNIT           0x04
+#define GPCMD_GET_CONFIGURATION         0x46
 #define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
-#define GPCMD_GET_PERFORMANCE		    0xac
-#define GPCMD_INQUIRY			    0x12
-#define GPCMD_LOAD_UNLOAD		    0xa6
-#define GPCMD_MECHANISM_STATUS		    0xbd
-#define GPCMD_MODE_SELECT_10		    0x55
-#define GPCMD_MODE_SENSE_10		    0x5a
-#define GPCMD_PAUSE_RESUME		    0x4b
-#define GPCMD_PLAY_AUDIO_10		    0x45
-#define GPCMD_PLAY_AUDIO_MSF		    0x47
-#define GPCMD_PLAY_AUDIO_TI		    0x48
-#define GPCMD_PLAY_CD			    0xbc
+#define GPCMD_GET_PERFORMANCE           0xac
+#define GPCMD_INQUIRY               0x12
+#define GPCMD_LOAD_UNLOAD           0xa6
+#define GPCMD_MECHANISM_STATUS          0xbd
+#define GPCMD_MODE_SELECT_10            0x55
+#define GPCMD_MODE_SENSE_10         0x5a
+#define GPCMD_PAUSE_RESUME          0x4b
+#define GPCMD_PLAY_AUDIO_10         0x45
+#define GPCMD_PLAY_AUDIO_MSF            0x47
+#define GPCMD_PLAY_AUDIO_TI         0x48
+#define GPCMD_PLAY_CD               0xbc
 #define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL  0x1e
-#define GPCMD_READ_10			    0x28
-#define GPCMD_READ_12			    0xa8
-#define GPCMD_READ_CDVD_CAPACITY	    0x25
-#define GPCMD_READ_CD			    0xbe
-#define GPCMD_READ_CD_MSF		    0xb9
-#define GPCMD_READ_DISC_INFO		    0x51
-#define GPCMD_READ_DVD_STRUCTURE	    0xad
-#define GPCMD_READ_FORMAT_CAPACITIES	    0x23
-#define GPCMD_READ_HEADER		    0x44
-#define GPCMD_READ_TRACK_RZONE_INFO	    0x52
-#define GPCMD_READ_SUBCHANNEL		    0x42
-#define GPCMD_READ_TOC_PMA_ATIP		    0x43
-#define GPCMD_REPAIR_RZONE_TRACK	    0x58
-#define GPCMD_REPORT_KEY		    0xa4
-#define GPCMD_REQUEST_SENSE		    0x03
-#define GPCMD_RESERVE_RZONE_TRACK	    0x53
-#define GPCMD_SCAN			    0xba
-#define GPCMD_SEEK			    0x2b
-#define GPCMD_SEND_DVD_STRUCTURE	    0xad
-#define GPCMD_SEND_EVENT		    0xa2
-#define GPCMD_SEND_KEY			    0xa3
-#define GPCMD_SEND_OPC			    0x54
-#define GPCMD_SET_READ_AHEAD		    0xa7
-#define GPCMD_SET_STREAMING		    0xb6
-#define GPCMD_START_STOP_UNIT		    0x1b
-#define GPCMD_STOP_PLAY_SCAN		    0x4e
-#define GPCMD_TEST_UNIT_READY		    0x00
-#define GPCMD_VERIFY_10			    0x2f
-#define GPCMD_WRITE_10			    0x2a
-#define GPCMD_WRITE_AND_VERIFY_10	    0x2e
+#define GPCMD_READ_10               0x28
+#define GPCMD_READ_12               0xa8
+#define GPCMD_READ_CDVD_CAPACITY        0x25
+#define GPCMD_READ_CD               0xbe
+#define GPCMD_READ_CD_MSF           0xb9
+#define GPCMD_READ_DISC_INFO            0x51
+#define GPCMD_READ_DVD_STRUCTURE        0xad
+#define GPCMD_READ_FORMAT_CAPACITIES        0x23
+#define GPCMD_READ_HEADER           0x44
+#define GPCMD_READ_TRACK_RZONE_INFO     0x52
+#define GPCMD_READ_SUBCHANNEL           0x42
+#define GPCMD_READ_TOC_PMA_ATIP         0x43
+#define GPCMD_REPAIR_RZONE_TRACK        0x58
+#define GPCMD_REPORT_KEY            0xa4
+#define GPCMD_REQUEST_SENSE         0x03
+#define GPCMD_RESERVE_RZONE_TRACK       0x53
+#define GPCMD_SCAN              0xba
+#define GPCMD_SEEK              0x2b
+#define GPCMD_SEND_DVD_STRUCTURE        0xad
+#define GPCMD_SEND_EVENT            0xa2
+#define GPCMD_SEND_KEY              0xa3
+#define GPCMD_SEND_OPC              0x54
+#define GPCMD_SET_READ_AHEAD            0xa7
+#define GPCMD_SET_STREAMING         0xb6
+#define GPCMD_START_STOP_UNIT           0x1b
+#define GPCMD_STOP_PLAY_SCAN            0x4e
+#define GPCMD_TEST_UNIT_READY           0x00
+#define GPCMD_VERIFY_10             0x2f
+#define GPCMD_WRITE_10              0x2a
+#define GPCMD_WRITE_AND_VERIFY_10       0x2e
 /* This is listed as optional in ATAPI 2.6, but is (curiously)
  * missing from Mt. Fuji, Table 57.  It _is_ mentioned in Mt. Fuji
  * Table 377 as an MMC command for SCSi devices though...  Most ATAPI
  * drives support it. */
-#define GPCMD_SET_SPEED			    0xbb
+#define GPCMD_SET_SPEED             0xbb
 /* This seems to be a SCSI specific CD-ROM opcode
  * to play data at track/index */
-#define GPCMD_PLAYAUDIO_TI		    0x48
+#define GPCMD_PLAYAUDIO_TI          0x48
 /*
  * From MS Media Status Notification Support Specification. For
  * older drives only.
  */
-#define GPCMD_GET_MEDIA_STATUS		    0xda
-#define GPCMD_MODE_SENSE_6		    0x1a
+#define GPCMD_GET_MEDIA_STATUS          0xda
+#define GPCMD_MODE_SENSE_6          0x1a
 
 #define ATAPI_INT_REASON_CD             0x01 /* 0 = data transfer */
 #define ATAPI_INT_REASON_IO             0x02 /* 1 = transfer to the host */
@@ -317,7 +318,7 @@  typedef struct IDEDMAOps IDEDMAOps;
 #define SMART_DISABLE         0xd9
 #define SMART_STATUS          0xda
 
-typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind;
+typedef enum { IDE_HD, IDE_CD, IDE_CFATA, IDE_BRIDGE } IDEDriveKind;
 
 typedef void EndTransferFunc(IDEState *);
 
@@ -341,7 +342,7 @@  enum ide_dma_cmd {
 };
 
 #define ide_cmd_is_read(s) \
-	((s)->dma_cmd == IDE_DMA_READ)
+    ((s)->dma_cmd == IDE_DMA_READ)
 
 /* NOTE: IDEState represents in fact one drive */
 struct IDEState {
@@ -492,6 +493,8 @@  struct IDEDevice {
     char *serial;
     char *model;
     uint64_t wwn;
+    
+    SCSIBus scsi_bus;
 };
 
 /* These are used for the error_status field of IDEBus */
@@ -580,4 +583,4 @@  void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev,
                  int bus_id, int max_units);
 IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
 
-#endif /* HW_IDE_INTERNAL_H */
+#endif /* HW_IDE_INTERNAL_H */
\ No newline at end of file
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 788b361..481cb6c 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -67,6 +67,16 @@  static char *idebus_get_fw_dev_path(DeviceState *dev)
     return g_strdup(path);
 }
 
+static const struct SCSIBusInfo atapi_scsi_info = {
+    .tcq = true,
+    .max_target = 0,
+    .max_lun = 0,
+    
+    .transfer_data = NULL,
+    .complete = NULL,
+    .cancel = NULL
+};
+
 static int ide_qdev_init(DeviceState *qdev)
 {
     IDEDevice *dev = IDE_DEVICE(qdev);
@@ -170,7 +180,7 @@  static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
     }
 
     blkconf_serial(&dev->conf, &dev->serial);
-    if (kind != IDE_CD) {
+    if (kind != IDE_CD && kind != IDE_BRIDGE) {
         blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255, &err);
         if (err) {
             error_report_err(err);
@@ -194,6 +204,12 @@  static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
 
     add_boot_device_path(dev->conf.bootindex, &dev->qdev,
                          dev->unit ? "/disk@1" : "/disk@0");
+    
+    if(kind == IDE_BRIDGE)
+    {
+        scsi_bus_new(&dev->scsi_bus, sizeof(dev->scsi_bus), &dev->qdev, &atapi_scsi_info, NULL);        
+        scsi_bus_legacy_handle_cmdline(&dev->scsi_bus, NULL);
+    }
 
     return 0;
 }
@@ -253,6 +269,11 @@  static int ide_cd_initfn(IDEDevice *dev)
     return ide_dev_initfn(dev, IDE_CD);
 }
 
+static int ide_bridge_initfn(IDEDevice *dev)
+{
+    return ide_dev_initfn(dev, IDE_BRIDGE);
+}
+
 static int ide_drive_initfn(IDEDevice *dev)
 {
     DriveInfo *dinfo = blk_legacy_dinfo(dev->conf.blk);
@@ -314,6 +335,23 @@  static const TypeInfo ide_cd_info = {
     .class_init    = ide_cd_class_init,
 };
 
+static void ide_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IDEDeviceClass *k = IDE_DEVICE_CLASS(klass);
+    k->init = ide_bridge_initfn;
+    dc->fw_name = "drive";
+    dc->desc = "virtual ATAPI-SCSI bridge";
+    dc->props = ide_cd_properties;
+}
+
+static const TypeInfo ide_bridge_info = {
+    .name           = "ide-bridge",
+    .parent         = TYPE_IDE_DEVICE,
+    .instance_size  = sizeof(IDEDrive),
+    .class_init     = ide_bridge_class_init
+};
+
 static Property ide_drive_properties[] = {
     DEFINE_IDE_DEV_PROPERTIES(),
     DEFINE_PROP_END_OF_LIST(),
@@ -360,8 +398,9 @@  static void ide_register_types(void)
     type_register_static(&ide_bus_info);
     type_register_static(&ide_hd_info);
     type_register_static(&ide_cd_info);
+    type_register_static(&ide_bridge_info);
     type_register_static(&ide_drive_info);
     type_register_static(&ide_device_type_info);
 }
 
-type_init(ide_register_types)
+type_init(ide_register_types)
\ No newline at end of file
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 54d71f4..2ecb540 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -527,7 +527,7 @@  static uint8_t *scsi_get_buf(SCSIRequest *req)
     return (uint8_t *)r->iov.iov_base;
 }
 
-static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
 {
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
     int buflen = 0;
@@ -2759,4 +2759,4 @@  static void scsi_disk_register_types(void)
     type_register_static(&scsi_disk_info);
 }
 
-type_init(scsi_disk_register_types)
+type_init(scsi_disk_register_types)
\ No newline at end of file
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index cdaf0f8..beecbbc 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -7,7 +7,7 @@ 
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
 
-#define MAX_SCSI_DEVS	255
+#define MAX_SCSI_DEVS   255
 
 #define SCSI_CMD_BUF_SIZE     16
 #define SCSI_SENSE_LEN      18
@@ -275,7 +275,9 @@  void scsi_device_unit_attention_reported(SCSIDevice *dev);
 int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
 SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
 
+int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf);
+
 /* scsi-generic.c. */
 extern const SCSIReqOps scsi_generic_req_ops;
 
-#endif
+#endif
\ No newline at end of file