static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)

{

    IDEState *s = opaque;

    int data_offset, n;



    if (ret < 0) {

        ide_atapi_io_error(s, ret);

        goto eot;

    }



    if (s->io_buffer_size > 0) {

        /*

         * For a cdrom read sector command (s->lba != -1),

         * adjust the lba for the next s->io_buffer_size chunk

         * and dma the current chunk.

         * For a command != read (s->lba == -1), just transfer

         * the reply data.

         */

        if (s->lba != -1) {

            if (s->cd_sector_size == 2352) {

                n = 1;

                cd_data_to_raw(s->io_buffer, s->lba);

            } else {

                n = s->io_buffer_size >> 11;

            }

            s->lba += n;

        }

        s->packet_transfer_size -= s->io_buffer_size;

        if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0)

            goto eot;

    }



    if (s->packet_transfer_size <= 0) {

        s->status = READY_STAT | SEEK_STAT;

        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;

        ide_set_irq(s->bus);

        goto eot;

    }



    s->io_buffer_index = 0;

    if (s->cd_sector_size == 2352) {

        n = 1;

        s->io_buffer_size = s->cd_sector_size;

        data_offset = 16;

    } else {

        n = s->packet_transfer_size >> 11;

        if (n > (IDE_DMA_BUF_SECTORS / 4))

            n = (IDE_DMA_BUF_SECTORS / 4);

        s->io_buffer_size = n * 2048;

        data_offset = 0;

    }

#ifdef DEBUG_AIO

    printf("aio_read_cd: lba=%u n=%d\n", s->lba, n);

#endif



    s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);

    s->bus->dma->iov.iov_len = n * 4 * 512;

    qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);



    s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2,

                                       &s->bus->dma->qiov, n * 4,

                                       ide_atapi_cmd_read_dma_cb, s);

    return;



eot:

    block_acct_done(bdrv_get_stats(s->bs), &s->acct);

    ide_set_inactive(s, false);

}
