Prepare for introducing asynchronous I/O in floppy disks by adding support for asynchronous DMA operations.
We won't need idle bottom halves (or bottom halves in general) anymore. QEMU works just fine without them with synchronous I/O (though the floppy drive will stall the guest and be "impolite"), so remove all that code for that now for simplicity. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> Signed-off-by: Li Zhi Hui <zhihu...@linux.vnet.ibm.com> --- hw/dma.c | 80 +++++++++++++++++++++++++++++++++++++---------------------- hw/fdc.c | 1 + hw/isa.h | 2 + hw/sun4m.c | 3 ++ hw/sun4u.c | 3 ++ 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/hw/dma.c b/hw/dma.c index 0a9322d..dd5c57d 100644 --- a/hw/dma.c +++ b/hw/dma.c @@ -45,6 +45,8 @@ struct dma_regs { uint8_t eop; DMA_transfer_handler transfer_handler; void *opaque; + bool channel_running; + bool channel_is_asynchronous; }; #define ADDR 0 @@ -138,6 +140,8 @@ static inline void init_chan (struct dma_cont *d, int ichan) r = d->regs + ichan; r->now[ADDR] = r->base[ADDR] << d->dshift; r->now[COUNT] = 0; + r->channel_running = false; + r->channel_is_asynchronous = false; } static inline int getff (struct dma_cont *d) @@ -327,7 +331,7 @@ void DMA_release_DREQ (int nchan) DMA_run(); } -static void channel_run (int ncont, int ichan) +static void channel_do_transfer(int ncont, int ichan) { int n; struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; @@ -351,46 +355,64 @@ static void channel_run (int ncont, int ichan) ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); } -static QEMUBH *dma_bh; +static void channel_run(int ncont, int nchan) +{ + struct dma_cont *d = &dma_controllers[ncont]; + struct dma_regs *r = &d->regs[nchan]; + + int mask = 1 << nchan; + while ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { + assert(!r->channel_running); + r->channel_running = true; + channel_do_transfer(ncont, nchan); + if (r->channel_is_asynchronous) { + break; + } + r->channel_running = false; + } +} static void DMA_run (void) { - struct dma_cont *d; int icont, ichan; - int rearm = 0; - static int running = 0; - - if (running) { - rearm = 1; - goto out; - } else { - running = 1; - } - - d = dma_controllers; - for (icont = 0; icont < 2; icont++, d++) { + for (icont = 0; icont < 2; icont++) { for (ichan = 0; ichan < 4; ichan++) { - int mask; - - mask = 1 << ichan; - - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - channel_run (icont, ichan); - rearm = 1; + struct dma_regs *r = &dma_controllers[icont].regs[ichan]; + if (!r->channel_running) { + channel_run(icont, ichan); } } } +} - running = 0; -out: - if (rearm) - qemu_bh_schedule_idle(dma_bh); +void DMA_set_channel_async(int nchan, bool val) +{ + int icont, ichan; + struct dma_regs *r; + + icont = nchan > 3; + ichan = nchan & 3; + r = &dma_controllers[icont].regs[ichan]; + r->channel_is_asynchronous = val; } -static void DMA_run_bh(void *unused) +void DMA_set_return(int nret, int nchan) { - DMA_run(); + struct dma_regs *r; + struct dma_cont *d; + int icont, ichan; + + icont = nchan > 3; + ichan = nchan & 3; + d = dma_controllers; + r = &d[icont].regs[ichan]; + r->now[COUNT] = nret; + assert(r->channel_is_asynchronous); + assert(r->channel_running); + r->channel_running = false; + r->channel_is_asynchronous = false; + channel_run(icont, ichan); } void DMA_register_channel (int nchan, @@ -560,6 +582,4 @@ void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) high_page_enable ? 0x488 : -1, cpu_request_exit); vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]); vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]); - - dma_bh = qemu_bh_new(DMA_run_bh, NULL); } diff --git a/hw/fdc.c b/hw/fdc.c index cb4cd25..5684a05 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -1189,6 +1189,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, fdctrl = opaque; if (fdctrl->msr & FD_MSR_RQM) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); + DMA_release_DREQ(fdctrl->dma_chann); return 0; } cur_drv = get_cur_drv(fdctrl); diff --git a/hw/isa.h b/hw/isa.h index f7bc4b5..4f19c74 100644 --- a/hw/isa.h +++ b/hw/isa.h @@ -97,4 +97,6 @@ void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit); void DMA_register_channel (int nchan, DMA_transfer_handler transfer_handler, void *opaque); +void DMA_set_channel_async(int nchan, bool val); +void DMA_set_return(int nret, int nchan); #endif diff --git a/hw/sun4m.c b/hw/sun4m.c index 34088ad..f3b8027 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -147,6 +147,9 @@ int DMA_write_memory (int nchan, void *buf, int pos, int size) { return 0; } + +void DMA_set_channel_async(int nchan, bool val) {} +void DMA_set_return(int nret, int nchan) {} void DMA_hold_DREQ (int nchan) {} void DMA_release_DREQ (int nchan) {} void DMA_schedule(int nchan) {} diff --git a/hw/sun4u.c b/hw/sun4u.c index fe33138..abf3426 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -110,6 +110,9 @@ int DMA_write_memory (int nchan, void *buf, int pos, int size) { return 0; } + +void DMA_set_channel_async(int nchan, bool val) {} +void DMA_set_return(int nret, int nchan) {} void DMA_hold_DREQ (int nchan) {} void DMA_release_DREQ (int nchan) {} void DMA_schedule(int nchan) {} -- 1.7.4.1