To avoid re-enter function fdctrl_transfer_handler, according paolo's suggestion, the patch is used to test.
Signed-off-by: Li Zhi Hui <zhihu...@linux.vnet.ibm.com> --- hw/dma.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- hw/isa.h | 2 ++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/hw/dma.c b/hw/dma.c index 0a9322d..0a59164 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) @@ -375,9 +379,20 @@ static void DMA_run (void) mask = 1 << ichan; - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - channel_run (icont, ichan); + while ((0 == (d->mask & mask)) && + (0 != (d->status & (mask << 4)))) { + struct dma_regs *r = &dma_controllers[icont].regs[ichan]; + if (r->channel_running) { + assert(r->channel_is_asynchronous); + break; + } + r->channel_running = true; + channel_run(icont, ichan); rearm = 1; + if (r->channel_is_asynchronous) { + break; + } + r->channel_running = false; } } } @@ -388,6 +403,35 @@ out: 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; +} + +void DMA_set_return(int nret, int nchan) +{ + 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; +} + + static void DMA_run_bh(void *unused) { DMA_run(); diff --git a/hw/isa.h b/hw/isa.h index 40373fb..07293d0 100644 --- a/hw/isa.h +++ b/hw/isa.h @@ -92,4 +92,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 -- 1.7.4.1