+static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
+{
+ rc4030State *s = opaque;
+ target_phys_addr_t entry_addr;
+ target_phys_addr_t dma_addr, phys_addr;
+ dma_pagetable_entry entry;
+ int index, dev_to_mem;
+ int ncpy, i;
+
+ s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
+
+ /* Check DMA channel consistency */
+ dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1;
+ if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) ||
+ (is_write != dev_to_mem)) {
+ s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR;
+ return;
+ }
+
+ if (len > s->dma_regs[n][DMA_REG_COUNT])
+ len = s->dma_regs[n][DMA_REG_COUNT];
+
+ dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
+ i = 0;
+ for (;;) {
+ if (i == len) {
+ s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
+ break;
+ }
+
+ ncpy = DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE - 1));
+ if (ncpy > len - i)
+ ncpy = len - i;
+
+ /* Get DMA translation table entry */
+ index = dma_addr / DMA_PAGESIZE;
+ if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
+ s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR;
+ break;
+ }
+ entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
+ /* XXX: not sure. should we really use only lowest bits? */
+ entry_addr &= 0x7fffffff;
+ cpu_physical_memory_rw(entry_addr, (uint8_t *)&entry, sizeof(entry), 0);
+
+ /* Read/write data at right place */
+ phys_addr = entry.frame + (dma_addr & (DMA_PAGESIZE - 1));
+ cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
+
+ i += ncpy;
+ dma_addr += ncpy;
+ s->dma_regs[n][DMA_REG_COUNT] -= ncpy;
+ }
+
+#ifdef DEBUG_RC4030_DMA
+ {
+ int i, j;
+ printf("rc4030 dma: Copying %d bytes %s host %p\n",
+ len, is_write ? "from" : "to", buf);
+ for (i = 0; i < len; i += 16) {
+ int n = min(16, len - i);
+ for (j = 0; j < n; j++)
+ printf("%02x ", buf[i + j]);
+ while (j++ < 16)
+ printf(" ");
+ printf("| ");
+ for (j = 0; j < n; j++)
+ printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
+ printf("\n");
+ }
+ }
+#endif
+}
+
+struct rc4030DMAState {
+ void *opaque;
+ int n;
+};
+
+static void rc4030_dma_read(void *dma, uint8_t *buf, int len)
+{
+ rc4030_dma s = dma;
+ rc4030_do_dma(s->opaque, s->n, buf, len, 0);
+}
+
+static void rc4030_dma_write(void *dma, uint8_t *buf, int len)
+{
+ rc4030_dma s = dma;
+ rc4030_do_dma(s->opaque, s->n, buf, len, 1);
+}
+
+static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
+{
+ rc4030_dma *s;
+ struct rc4030DMAState *p;
+ int i;
+
+ s = (rc4030_dma *)qemu_mallocz(sizeof(rc4030_dma) * n);
+ p = (struct rc4030DMAState *)qemu_mallocz(sizeof(struct rc4030DMAState) * n);
+ for (i = 0; i < n; i++) {
+ p->opaque = opaque;
+ p->n = i;
+ s[i] = p;
+ p++;
+ }
+ return s;
+}
+
+qemu_irq *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
+ rc4030_dma **dmas,
+ rc4030_dma_function *dma_read, rc4030_dma_function *dma_write)