/* astro.c -- Solaris device driver for the SDSU SBus Interface Card. VERSION 2.0 1/16/96 */ /* Development notes: Writes to device registers may be buffered by the system and this may affect synchronization. For this driver, the BCR and AR registers can be read without side effects so reading the register after a write will verify the operation. The CSR may pose further problems. */ /* No standard include files outside of the directory can be included since this code runs in kernel space. Only functions and structures in Section 9 of the manual are usable here. (Yes, that means that , , and friends and there functions cannot be used). The header files and must be at the end of the include file list. These headers undefine several macros that are re-implemented as functions. */ #include #include #include #include #include #include #include #include #include #include #include "astro_impl.h" #include "astro_io.h" #include #include #include #include #include #include /* these two must be last */ #include /* Prototypes for main entry points pointed to in the cb_ops structure */ static int astro_open (dev_t *devp, int flag, int otyp, cred_t *credp); static int astro_close (dev_t dev, int flag, int otyp, cred_t *credp); static int astro_read (dev_t dev, struct uio *uiop, cred_t *credp); static int astro_write (dev_t dev, struct uio *uiop, cred_t *credp); static int astro_ioctl (dev_t dev, int cmd, int arg, int mode, cred_t *credp, int *rvalp); static struct cb_ops astro_cb_ops = { astro_open, astro_close, nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ astro_read, astro_write, astro_ioctl, nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* chpoll */ ddi_prop_op, /* prop_op */ NULL, (D_NEW | D_MP) }; /* Prototypes for functions pointed to by the dev_ops structure. */ static int astro_getinfo (dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int astro_identify (dev_info_t *dip); static int astro_attach (dev_info_t *dip, ddi_attach_cmd_t cmd); static int astro_detach (dev_info_t *dip, ddi_detach_cmd_t cmd); /* Notes: 1> the reset(9E) entry point is not supported by the present system (SunOS 5.4); 2> the devo_bus_ops field must be NULL since this is a leaf node driver. */ static struct dev_ops astro_ops = { DEVO_REV, 0, astro_getinfo, astro_identify, nulldev, astro_attach, astro_detach, nodev, /* reset */ &astro_cb_ops, NULL /* bus_ops */ }; /* The modldrv structure is similar to the old vdldrv structure but with some field changes. */ extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, "astro driver v1.5", &astro_ops }; /* The modlinkage structure is new in Solaris 2.x and is a necessary part of the autoconfiguration. */ static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; /* Anchor point for soft state information */ static void *statep; /* opaque */ /* Loadable Module Support The 3 entry points, _init(9e), _info(9e), and _fini(9e) must be implemented and must have these names. They are called by the kernel during module loading and are not available to user code. */ /* _init(9e) initializes a loadable module. It is called before any other routine and returns the evaluation of mod_install(9f). Any other work should be done prior to calling mod_install(9f) and must be undone if mod_install(9f) fails. */ int _init(void) { int err = ddi_soft_state_init (&statep, sizeof (struct astro_state), 1); if (err) return err; err = mod_install (&modlinkage); if (err) ddi_soft_state_fini (&statep); return err; } int _info (struct modinfo *modinfop) { return mod_info (&modlinkage, modinfop); } /* _fini(9e) prepares the module for unloading. It should deallocate any resources allocated by _init(9e). */ int _fini (void) { int err = mod_remove (&modlinkage); if (!err) ddi_soft_state_fini (&statep); return err; } /* Prototypes for other functions */ static u_int astro_intr (caddr_t instance); static int astro_strategy (struct buf *bp); static void astro_minphys (struct buf *bp); static void astro_dma_stop (struct astro_state *asp); static void astro_dma_chain (struct astro_state *asp); static void astro_dma_reset (struct astro_state *asp); /* Driver Configuration. The driver must provide the five entry points defined in this section. Notes: o There is an implicit mapping here between the instance number and the minor number such that instance == minor number. */ static int astro_getinfo (dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { struct astro_state *xsp; dev_t dev = (dev_t) arg; switch (infocmd) { case DDI_INFO_DEVT2INSTANCE: { *result = (void *) getminor (dev); return DDI_SUCCESS; } case DDI_INFO_DEVT2DEVINFO: { int instance = getminor (dev); struct astro_state *xsp = ddi_get_soft_state (statep, instance); if (xsp == NULL) return DDI_FAILURE; *result = (void *) xsp->dip; return DDI_SUCCESS; } default: return DDI_FAILURE; } } static int astro_identify (dev_info_t *dip) { if (strcmp (ddi_get_name (dip), prom_id_string_one) == 0) return DDI_IDENTIFIED; else if (strcmp (ddi_get_name (dip), prom_id_string_two) == 0) return DDI_IDENTIFIED; else if (strcmp (ddi_get_name (dip), prom_id_string_three) == 0) return DDI_IDENTIFIED; else { cmn_err (CE_WARN, "astro_identify: DDI_NOT_IDENTIFIED"); return DDI_NOT_IDENTIFIED; } } /* For a character device (memory-mapped), attach(9e) needs to: 1. allocate a state structure and initialize it; 2. map the device's registers; 3. add the interrupt handler; 4. initialize mutexes and condition variables; 5. create the device's minor node. */ static int astro_attach (dev_info_t *dip, ddi_attach_cmd_t cmd) { struct astro_state *asp = NULL; struct image_buf *ip; int instance = ddi_get_instance (dip); volatile u_long status; int j; enum { false, true } success = true; /* optimist! */ switch (cmd) { case DDI_ATTACH: { /* quit if device is slave-only -- can't do DMA then */ if (ddi_slaveonly (dip) == DDI_SUCCESS) { success = false; break; } /* quit if interrupt level is too high */ if (ddi_intr_hilevel (dip, 0) != 0) { cmn_err (CE_WARN, "astro: high-level interrupts are not supported"); success = false; break; } /* allocate the soft state structure */ if (ddi_soft_state_zalloc (statep, instance) != 0) { success = false; break; } asp = ddi_get_soft_state (statep, instance); asp->dip = dip; asp->busy = 0; ddi_set_driver_private(dip, (caddr_t)asp); /* Allocate kernel memory for three buffers */ for (j = 0; j < 3; j++) { ip = &asp->image_buf[j]; if (ddi_mem_alloc(asp->dip, &dma_limits, image_buf_size, 1, &ip->address, &ip->length) != DDI_SUCCESS) { cmn_err (CE_WARN,"astro_attach: allocation of buffers failed"); return DDI_FAILURE; } } /* map the device registers */ if (ddi_map_regs (dip, 0, (caddr_t *)&asp->regp, 0, sizeof (struct astro_regs)) != DDI_SUCCESS) { success = false; break; } if (ddi_map_regs (dip, 1, (caddr_t *)&asp->dchan, 0, sizeof (struct Dchan_regs)) != DDI_SUCCESS) { success = false; break; } if (ddi_map_regs (dip, 2, (caddr_t *)&asp->echan, 0, sizeof (struct Echan_regs)) != DDI_SUCCESS) { success = false; break; } /* register interrupt handler and initialize the locks */ /* first register a null handler */ if (ddi_add_intr (dip, 0, &asp->iblock_cookie, NULL, (u_int (*)(caddr_t))nulldev, NULL) != DDI_SUCCESS) { success = false; break; } /* then initialize locks */ mutex_init (&asp->mu, "astro mutex", MUTEX_DRIVER, (void *)&asp->iblock_cookie); cv_init (&asp->cv, "astro cv", CV_DRIVER, NULL); cv_init (&asp->bufcv, "astro bufcv", CV_DRIVER, NULL); /* remove the null handler */ ddi_remove_intr (dip, 0, asp->iblock_cookie); /* and register the real interrupt handler */ if (ddi_add_intr (dip, 0, &asp->iblock_cookie, (ddi_idevice_cookie_t *)NULL, astro_intr, (caddr_t)instance) != DDI_SUCCESS) { success = false; break; } /* create minor node */ if (ddi_create_minor_node (dip, "astro0", S_IFCHR, instance, "ddi_sdsu_astro", 0) != DDI_SUCCESS) { success = false; break; } /* create another minor node (for a dual read-out board) */ if (ddi_create_minor_node (dip, "astro1", S_IFCHR, instance+1, "ddi_sdsu_astro", 0) != DDI_SUCCESS) { success = false; break; } /* complete initializing soft state structure */ asp->dip = dip; asp->open = 0; /* put device in quiescent state */ status = asp->regp->csr; while (status & dmac_draining) status = asp->regp->csr; readback = asp->regp->csr; asp->regp->csr = status & ~dmac_en_dma; status = asp->regp->csr; asp->regp->csr = dmac_reset; readback = asp->regp->csr; asp->regp->csr = 0x0; /* these are not really dmac registers so clear them too */ readback = asp->regp->csr; asp->regp->unused = 0x0; readback = asp->regp->csr; asp->regp->ww = 0x0; ddi_report_dev (dip); } break; default: { return DDI_FAILURE; } } if (success == false) { /* deallocate any resources allocated above */ ddi_remove_minor_node (dip, NULL); /* removes all minor nodes */ ddi_remove_intr (dip, 0, asp->iblock_cookie); if (asp->regp != NULL) ddi_unmap_regs (dip, 0, (caddr_t *)&asp->regp, 0, sizeof (struct astro_regs)); if (asp->dchan != NULL) ddi_unmap_regs (dip, 1, (caddr_t *)&asp->dchan, 0, sizeof (struct Dchan_regs)); if (asp->echan != NULL) ddi_unmap_regs (dip, 2, (caddr_t *)&asp->echan, 0, sizeof (struct Echan_regs)); cv_destroy (&asp->cv); mutex_destroy (&asp->mu); if (asp != NULL) ddi_soft_state_free (asp, instance); return DDI_FAILURE; } else return DDI_SUCCESS; } /* Essentially, detach(9e) should undo all the resource allocations that attach(9e) successfully completes. */ static int astro_detach (dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance (dip); struct astro_state *asp = ddi_get_soft_state (statep, instance); int j; switch (cmd) { case DDI_DETACH: { /* pacify the device */ readback = asp->regp->csr; asp->regp->csr = dmac_reset; for (j = 0; j < 3; j++) { if (asp->image_buf[j].address != (caddr_t) -1) ddi_mem_free(asp->image_buf[j].address); } ddi_remove_minor_node (dip, NULL); /* removes all minor nodes */ ddi_remove_intr (dip, 0, asp->iblock_cookie); ddi_unmap_regs (dip, 0, (caddr_t *)&asp->regp, 0, sizeof (struct astro_regs)); ddi_unmap_regs (dip, 1, (caddr_t *)&asp->dchan, 0, sizeof (struct Dchan_regs)); ddi_unmap_regs (dip, 2, (caddr_t *)&asp->echan, 0, sizeof (struct Echan_regs)); cv_destroy (&asp->cv); mutex_destroy (&asp->mu); ddi_soft_state_free (statep, instance); } break; default: return DDI_FAILURE; } return DDI_SUCCESS; } /* Main entry points */ static int astro_open (dev_t *devp, int flag, int otyp, cred_t *credp) { int instance = getminor (*devp); struct astro_state *asp = ddi_get_soft_state (statep, instance); if (asp == NULL) return ENXIO; if (otyp != OTYP_CHR) return EINVAL; mutex_enter (&asp->mu); if (flag == FEXCL && asp->open > 0) /* honour exclusive lock flag */ return EAGAIN; asp->open++; astro_dma_reset(asp); readback = asp->regp->csr; asp->regp->ww = 0; readback = asp->regp->csr; /* flush to device */ mutex_exit (&asp->mu); return 0; } static int astro_close (dev_t dev, int flag, int otyp, cred_t *credp) { int instance = getminor (dev); struct astro_state *asp = ddi_get_soft_state (statep, instance); if (asp == NULL) /* sanity checks */ return ENXIO; if (otyp != OTYP_CHR) return EINVAL; mutex_enter (&asp->mu); asp->open = 0; astro_dma_reset(asp); readback = asp->regp->csr; /* flush to device */ asp->regp->ww = 0; readback = asp->regp->csr; /* flush to device */ mutex_exit (&asp->mu); return 0; } /* Read uses the DMA controller next transfer feature, so that during a transfer, the next byte and next address registers are used for >64K transfers. */ static int astro_read (dev_t dev, struct uio *uiop, cred_t *credp) { struct image_buf *ip; int value = 0, j, sleeping; u_int bytesize, byteremain; clock_t time1, time2; u_long bcr, csr; int instance = getminor (dev); struct astro_state *asp = ddi_get_soft_state (statep, instance); if (asp == NULL) return ENXIO; mutex_enter (&asp->mu); while (asp->busy) /* allow only 1 thread at a time */ { cv_wait (&asp->cv, &asp->mu); /* until interrupt serviced */ } asp->busy = 1; mutex_exit (&asp->mu); /* Setup image buffers */ for (j = 0; j < 3; j++) { /* asp->image_buf[j].address = (caddr_t) 0;*/ asp->image_buf[j].buf_handle = (caddr_t) 0; } for (j = 0; j < 3; j++) { ip = &asp->image_buf[j]; ip->operation = DDI_DMA_READ; ip->state = image_buf_waiting; if (ddi_dma_addr_setup(asp->dip, NULL, ip->address, ip->length, ip->operation, DDI_DMA_SLEEP, NULL, &dma_limits, &ip->buf_handle) != DDI_DMA_MAPPED) { cmn_err (CE_WARN,"astro_read: allocation of buffers failed"); value = ENOMEM; goto EXITREAD; } if (ddi_dma_htoc(ip->buf_handle, (off_t)0, &ip->buf_cookie) != DDI_SUCCESS) { cmn_err (CE_WARN,"astro_read: allocation of buffers failed"); value = ENOMEM; goto EXITREAD; } } asp->buf_numbusy = 0; asp->buf_numwaiting = 3; asp->buf_done = asp->buf_ready = asp->buf_busy = asp->buf_waiting = 0; bytesize = byteremain = uiop->uio_resid; while ((byteremain > 0) || (asp->buf_numwaiting < 3)) { if ((byteremain > 0) && (asp->buf_numwaiting > 0)) { ip = &asp->image_buf[asp->buf_waiting]; if (ip->state != image_buf_waiting) { value = ECHILD; mutex_enter (&asp->mu); astro_dma_stop(asp); mutex_exit (&asp->mu); goto EXITREAD; } if (byteremain > ip->length) ip->size = (((byteremain-1)< (ip->length)) ? (byteremain-1):(ip->length)); else ip->size = byteremain; byteremain -= ip->size; ip->state = image_buf_ready; asp->buf_numwaiting--; asp->buf_waiting++; if (asp->buf_waiting == 3) asp->buf_waiting = 0; mutex_enter (&asp->mu); astro_dma_chain(asp); mutex_exit (&asp->mu); } else if (asp->buf_numwaiting < 3) { mutex_enter (&asp->mu); ip = &asp->image_buf[asp->buf_done]; while ((ip->state != image_buf_done) && (ip->state != image_buf_error)) { drv_getparm(LBOLT, (u_long *)&time1); time2 = time1 + drv_usectohz(dma_read_timeout_usec); sleeping = cv_timedwait_sig(&asp->bufcv, &asp->mu, time2); if (sleeping <= 0) { csr = asp->regp->csr; bcr = asp->regp->bcr; astro_dma_stop(asp); value = ETIME; if (sleeping == -1) cmn_err (CE_WARN,"astro_read: dma timeout csr = 0x%x", csr); ip->size = ip->size - bcr; if (ddi_dma_sync(ip->buf_handle, 0, ip->size, DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) cmn_err (CE_WARN,"astro_read: Unable to sync buffer"); else if (uiomove(ip->address, ip->size, UIO_READ, uiop) != 0) cmn_err (CE_WARN,"astro_read: copy error to user space"); mutex_exit(&asp->mu); goto EXITREAD; } } if (ip->state == image_buf_error) { switch(ip->det_state) { case device_not_busy: value = ENOTBLK; break; case image_buf_not_busy: value = EDEADLK; break; case data_trans_error: value = ENOLCK; break; case bad_interrupt: value = ENOTEMPTY; break; case timed_out: value = ETIME; } astro_dma_stop(asp); mutex_exit(&asp->mu); goto EXITREAD; } if (ip->state != image_buf_done) { value = EPERM; astro_dma_stop(asp); mutex_exit(&asp->mu); goto EXITREAD; } mutex_exit(&asp->mu); if (ddi_dma_sync(ip->buf_handle, 0, ip->size, DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) { cmn_err (CE_WARN,"astro_read: Unable to sync buffer"); value = ESPIPE; mutex_enter(&asp->mu); astro_dma_stop(asp); mutex_exit(&asp->mu); goto EXITREAD; } value = uiomove(ip->address, ip->size, UIO_READ, uiop); if (value != 0) { cmn_err (CE_WARN,"astro_read: copy error to user space"); value = EPIPE; mutex_enter(&asp->mu); astro_dma_stop(asp); mutex_exit(&asp->mu); goto EXITREAD; } ip->state = image_buf_waiting; asp->buf_numwaiting++; asp->buf_done++; if (asp->buf_done == 3) asp->buf_done = 0; } } EXITREAD: mutex_enter (&asp->mu); for (j = 0; j < 3; j++) { if (asp->image_buf[j].buf_handle != (caddr_t) -1) ddi_dma_free(asp->image_buf[j].buf_handle); } asp->busy = 0; mutex_exit(&asp->mu); cv_signal(&asp->cv); return value; } /* astro_write is not used at this time. */ static int astro_write (dev_t dev, struct uio *uiop, cred_t *credp) { int instance = getminor (dev); struct astro_state *asp = ddi_get_soft_state (statep, instance); if (asp == NULL) return ENXIO; return 0; } /* Solaris 2.x Notes: ddi_copyin(9f) and ddi_copyout(9f) move data from/to user space to/from kernel space respectively. These must be used for this purpose. */ #ifdef __GNUC__ inline #endif static u_long extract_control_command (int cmd) { return (cmd & 0x80000000) ? ((cmd & 0x00ff) | 0x0100) : (cmd & 0x00ff); } #ifdef __GNUC__ inline #endif static u_long extract_byte_count (int cmd) { return (cmd >> 16) & 0xff; } /* Notes: o Locking should be used on a case by case basis, since it is not always necessary. Calls to allocation routines that might sleep should never be locked. */ static int astro_ioctl (dev_t dev, int cmd, int arg, int mode, cred_t *credp, int *rvalp) { int instance = getminor (dev); struct astro_state *asp = ddi_get_soft_state (statep, instance); int retval = 0; u_int iocmd; if (asp == NULL) return ENXIO; /* sanity check */ mutex_enter (&asp->mu); /* extract control command from cmd */ iocmd = extract_control_command (cmd); switch (iocmd) { case ASTRO_GET_CSR: { u_long csr = asp->regp->csr; if (ddi_copyout ((caddr_t) &csr, (caddr_t) arg, sizeof (csr), mode) != 0) retval = EFAULT; } break; case ASTRO_GET_AR: { u_long ar = asp->regp->ar; if (ddi_copyout ((caddr_t) &ar, (caddr_t) arg, sizeof (ar), mode) != 0) retval = EFAULT; } break; case ASTRO_GET_BCR: { u_long bcr = asp->regp->bcr; if (ddi_copyout ((caddr_t) &bcr, (caddr_t) arg, sizeof (bcr), mode) != 0) retval = EFAULT; } break; case ASTRO_SET_CSR: { long csr; if (ddi_copyin ((caddr_t) arg, (caddr_t) &csr, sizeof (csr), mode) != 0) retval = EFAULT; else { readback = asp->regp->csr; asp->regp->csr = csr; /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_SET_AR: { long ar; if (ddi_copyin ((caddr_t) arg, (caddr_t) &ar, sizeof (ar), mode) != 0) retval = EFAULT; else { readback = asp->regp->csr; asp->regp->ar = ar; /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_SET_BCR: { long bcr; if (ddi_copyin ((caddr_t) arg, (caddr_t) &bcr, sizeof (bcr), mode) != 0) retval = EFAULT; else { readback = asp->regp->csr; asp->regp->bcr = bcr; /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_WRITE_UNUSED_REG: { u_long bytes = extract_byte_count (cmd); u_long *ur = kmem_alloc (bytes, KM_SLEEP); u_int i; astro_dma_reset(asp); if (ddi_copyin ((caddr_t) arg, (caddr_t) ur, bytes, mode) != 0) retval = EFAULT; else { for (i=0; i<(bytes >> 2); i++) { readback = asp->regp->unused; asp->regp->unused = ur[i]; /* make sure register write gets flushed to device */ readback = asp->regp->csr; drv_usecwait (75); } } kmem_free (ur, bytes); } break; case ASTRO_SET_WW: { u_long ww; if (ddi_copyin ((caddr_t) arg, (caddr_t) &ww, sizeof (ww), mode) != 0) retval = EFAULT; else { readback = asp->regp->csr; asp->regp->ww = ww; /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_WRITE_DCHAN: { u_long bytes = extract_byte_count (cmd); u_long *dchan = kmem_alloc (bytes, KM_SLEEP); u_int i; if (ddi_copyin ((caddr_t) arg, (caddr_t) dchan, bytes, mode) != 0) retval = EFAULT; else { for (i=0; i<(bytes >> 2); i++) { readback = *(asp->dchan->dr); *(asp->dchan->dr) = dchan[i]; } } kmem_free (dchan, bytes); } break; case ASTRO_WRITE_ECHAN: { u_long bytes = extract_byte_count (cmd); u_short *echan = kmem_alloc (bytes, KM_SLEEP); u_int i; if (ddi_copyin ((caddr_t) arg, (caddr_t) echan, bytes, mode) != 0) retval = EFAULT; else { for (i=0; i<(bytes >> 2); i++) { readback = *(asp->echan->er); *(asp->echan->er + 6) = echan[i]; } } kmem_free (echan, bytes); } break; case ASTRO_RESET: astro_dma_reset; break; default: retval = ENOTTY; break; } mutex_exit (&asp->mu); return retval; } /* Other functions */ /* Reset DMA controller */ static void astro_dma_reset(struct astro_state *asp) { readback = asp->regp->csr; asp->regp->csr = (dmac_reset | dmac_write); readback = asp->regp->csr; asp->regp->csr = 0; readback = asp->regp->csr; asp->regp->csr = dmac_flush; readback = asp->regp->csr; /* flush to device */ } /* Stop a DMA transfer */ static void astro_dma_stop(struct astro_state *asp) { int j; astro_dma_reset(asp); for (j = 0; j < 3; j++) asp->image_buf[j].state = image_buf_waiting; } /* Start/continue DMA chaining */ static void astro_dma_chain (struct astro_state *asp) { struct image_buf *ip; u_long csr; u_long ar; u_long bcr; if (!asp->busy) { cmn_err(CE_WARN,"astro_dma_chain: not marked busy"); return; } ip = &asp->image_buf[asp->buf_ready]; if ((ip->state == image_buf_ready) && (asp->buf_numbusy < 2)) { csr = asp->regp->csr; if (csr & dmac_na_loaded) { return; } asp->buf_numbusy++; ip->state = image_buf_busy; csr = (dmac_en_next | dmac_en_dma | dmac_faster | dmac_en_cnt | dmac_int_en); if (ip->operation == DDI_DMA_READ) csr |= dmac_write; ar = ip->buf_cookie.dmac_address; bcr = ip->size; readback = asp->regp->csr; asp->regp->bcr = bcr; readback = asp->regp->csr; asp->regp->ar = ar; readback = asp->regp->csr; asp->regp->csr = csr; readback = asp->regp->csr; /* flush to device */ asp->buf_ready++; if (asp->buf_ready == 3) asp->buf_ready = 0; } } /* Interrupt handler */ static u_int astro_intr (caddr_t instance) { register struct image_buf *ip; register u_long csr; int j = 0; struct astro_state *asp = (struct astro_state *) ddi_get_soft_state(statep, (u_int)instance); if (asp == NULL) return DDI_INTR_UNCLAIMED; mutex_enter (&asp->mu); ip = &asp->image_buf[asp->buf_busy]; csr = asp->regp->csr; if (!(csr & (dmac_err_pend | dmac_int_pend))) { mutex_exit (&asp->mu); return DDI_INTR_UNCLAIMED; } if (!asp->busy) { ip->det_state = device_not_busy; cmn_err (CE_WARN, "astro_intr: device not busy"); } else if (csr & dmac_err_pend) { ip->det_state = data_trans_error; cmn_err (CE_WARN, "astro_intr: data transfer error csr = 0x%x", csr); } else if (csr & dmac_tc) { if ((asp->buf_numbusy == 0) || (ip->state != image_buf_busy)) { ip->det_state = image_buf_not_busy; goto FLUSH; } if (csr & dmac_write) { while (((csr = asp->regp->csr) & dmac_draining) && (j++ < 30)) { drv_usecwait(1); } } if(ip->size == tdl_size) csr = dmac_flush; else if (!(csr & dmac_en_next)) csr = dmac_flush; readback = asp->regp->csr; asp->regp->csr = csr; readback = asp->regp->csr; /* flush to device */ ip->state = image_buf_done; asp->buf_numbusy--; asp->buf_busy++; if (asp->buf_busy == 3) asp->buf_busy = 0; cv_signal(&asp->bufcv); if(ip->size == tdl_size) astro_dma_reset(asp); else astro_dma_chain(asp); mutex_exit (&asp->mu); return DDI_INTR_CLAIMED; } else { cmn_err (CE_WARN, "astro_intr: bad interrupt encountered csr = 0x%x", csr); ip->det_state = bad_interrupt; } FLUSH: astro_dma_reset(asp); ip->state = image_buf_error; cv_signal(&asp->bufcv); mutex_exit (&asp->mu); return DDI_INTR_CLAIMED; }