static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
{
    DBDMA_io *io = opaque;
    MACIOIDEState *m = io->opaque;
    IDEState *s = idebus_active_if(&m->bus);
    int unaligned;

    if (ret < 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);
        ide_atapi_io_error(s, ret);
        io->remainder_len = 0;
        goto done;
    }

    if (!m->dma_active) {
        MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
                      s->nsector, io->len, s->status);
        /* data not ready yet, wait for the channel to get restarted */
        io->processing = false;
        return;
    }

    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);

    if (s->io_buffer_size > 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);

        s->packet_transfer_size -= s->io_buffer_size;

        s->io_buffer_index += s->io_buffer_size;
        s->lba += s->io_buffer_index >> 11;
        s->io_buffer_index &= 0x7ff;
    }

    s->io_buffer_size = MIN(io->len, s->packet_transfer_size);

    MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len,
                  io->len, s->packet_transfer_size);
    if (io->remainder_len && io->len) {
        /* guest wants the rest of its previous transfer */
        int remainder_len = MIN(io->remainder_len, io->len);

        MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len);

        cpu_physical_memory_write(io->addr, io->remainder + 0x200 -
                                  remainder_len, remainder_len);

        io->addr += remainder_len;
        io->len -= remainder_len;
        s->io_buffer_size = remainder_len;
        io->remainder_len -= remainder_len;
        /* treat remainder as individual transfer, start again */
        qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                         &address_space_memory);
        pmac_ide_atapi_transfer_cb(opaque, 0);
        return;
    }

    if (!s->packet_transfer_size) {
        MACIO_DPRINTF("end of transfer\n");
        ide_atapi_cmd_ok(s);
        m->dma_active = false;
    }

    if (io->len == 0) {
        MACIO_DPRINTF("end of DMA\n");
        goto done;
    }

    /* launch next transfer */

    /* handle unaligned accesses first, get them over with and only do the
       remaining bulk transfer using our async DMA helpers */
    unaligned = io->len & 0x1ff;
    if (unaligned) {
        int sector_num = (s->lba << 2) + (s->io_buffer_index >> 9);
        int nsector = io->len >> 9;

        MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
                      unaligned, io->addr + io->len - unaligned);

        blk_read(s->blk, sector_num + nsector, io->remainder, 1);
        cpu_physical_memory_write(io->addr + io->len - unaligned,
                                  io->remainder, unaligned);

        io->len -= unaligned;
    }

    MACIO_DPRINTF("io->len = %#x\n", io->len);

    qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                     &address_space_memory);
    qemu_sglist_add(&s->sg, io->addr, io->len);
    io->addr += s->io_buffer_size;
    io->remainder_len = MIN(s->packet_transfer_size - s->io_buffer_size,
                            (0x200 - unaligned) & 0x1ff);
    MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);

    /* We would read no data from the block layer, thus not get a callback.
       Just fake completion manually. */
    if (!io->len) {
        pmac_ide_atapi_transfer_cb(opaque, 0);
        return;
    }

    io->len = 0;

    MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n",
                  (s->lba << 2) + (s->io_buffer_index >> 9),
                  s->packet_transfer_size, s->dma_cmd);

    m->aiocb = dma_blk_read(s->blk, &s->sg,
                            (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
                            pmac_ide_atapi_transfer_cb, io);
    return;

done:
    MACIO_DPRINTF("done DMA\n");
    block_acct_done(blk_get_stats(s->blk), &s->acct);
    io->dma_end(opaque);
}

static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
{
    DBDMA_io *io = opaque;
    MACIOIDEState *m = io->opaque;
    IDEState *s = idebus_active_if(&m->bus);
    int unaligned;

    if (ret < 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);
        ide_atapi_io_error(s, ret);
        io->remainder_len = 0;
        goto done;
    }

    if (!m->dma_active) {
        MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
                      s->nsector, io->len, s->status);
        /* data not ready yet, wait for the channel to get restarted */
        io->processing = false;
        return;
    }

    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);

    if (s->io_buffer_size > 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);

        s->packet_transfer_size -= s->io_buffer_size;

        s->io_buffer_index += s->io_buffer_size;
        s->lba += s->io_buffer_index >> 11;
        s->io_buffer_index &= 0x7ff;
    }

    s->io_buffer_size = MIN(io->len, s->packet_transfer_size);

    MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len,
                  io->len, s->packet_transfer_size);
    if (io->remainder_len && io->len) {
        /* guest wants the rest of its previous transfer */
        int remainder_len = MIN(io->remainder_len, io->len);

        MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len);

        cpu_physical_memory_write(io->addr, io->remainder + 0x200 -
                                  remainder_len, remainder_len);

        io->addr += remainder_len;
        io->len -= remainder_len;
        s->io_buffer_size = remainder_len;
        io->remainder_len -= remainder_len;
        /* treat remainder as individual transfer, start again */
        qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                         &address_space_memory);
        pmac_ide_atapi_transfer_cb(opaque, 0);
        return;
    }

    if (!s->packet_transfer_size) {
        MACIO_DPRINTF("end of transfer\n");
        ide_atapi_cmd_ok(s);
        m->dma_active = false;
    }

    if (io->len == 0) {
        MACIO_DPRINTF("end of DMA\n");
        goto done;
    }

    /* launch next transfer */

    /* handle unaligned accesses first, get them over with and only do the
       remaining bulk transfer using our async DMA helpers */
    unaligned = io->len & 0x1ff;
    if (unaligned) {
        int sector_num = (s->lba << 2) + (s->io_buffer_index >> 9);
        int nsector = io->len >> 9;

        MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
                      unaligned, io->addr + io->len - unaligned);

        blk_read(s->blk, sector_num + nsector, io->remainder, 1);
        cpu_physical_memory_write(io->addr + io->len - unaligned,
                                  io->remainder, unaligned);

        io->len -= unaligned;
    }

    MACIO_DPRINTF("io->len = %#x\n", io->len);

    qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                     &address_space_memory);
    qemu_sglist_add(&s->sg, io->addr, io->len);
    io->addr += s->io_buffer_size;
    io->remainder_len = MIN(s->packet_transfer_size - s->io_buffer_size,
                            (0x200 - unaligned) & 0x1ff);
    MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);

    /* We would read no data from the block layer, thus not get a callback.
       Just fake completion manually. */
    if (!io->len) {
        pmac_ide_atapi_transfer_cb(opaque, 0);
        return;
    }

    io->len = 0;

    MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n",
                  (s->lba << 2) + (s->io_buffer_index >> 9),
                  s->packet_transfer_size, s->dma_cmd);

    m->aiocb = dma_blk_read(s->blk, &s->sg,
                            (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
                            pmac_ide_atapi_transfer_cb, io);
    return;

done:
    MACIO_DPRINTF("done DMA\n");
    block_acct_done(blk_get_stats(s->blk), &s->acct);
    io->dma_end(opaque);
}
