/* IKON Corporation 2617 Western Ave. Seattle, WA 98121 (206) 728-6465 code module for Solaris 2.0 driver for Sbus hardcopy boards 20 October, 1992 BETA release 23 October, 1992 added dma_limit structure to ddi_dma_buf_setup call in strategy - rather than use (unknown) system defaults added test for dma mapping that crosses 16Mbte boundary - it shouldn't happen - but ..buf_setup doesn`t guarantee it! 27 October, 1992 fixed places that set bp->b_flags to B_ERROR without setting bp->b_error to an errno. also corrected bug that set b_flags w/ = instead of |= . 2 November, 1992 changed all CE_WARN to CE_CONT in cmn_err calls 12 November, 1992 added modinfo banner define - so i wouldn`t forget to update modinfo date when driver mods made! 16 November, 1992 added comments to int rountine about possibility of "interrupt not serviced" warning from kernel (harmless & very unlikely!) 10 December, 1992 added test for err_pending to interrupt code that decides if it is our interrupt. err_pending can cause an int without setting int_pending. we previously only tested int_pending, so we would refuse an interrupt caused by an error 23 October, 1995 modified IHCPIO_CMD_MASK in ihcp_io.h to only decode the bottom 16 bits of an ioctl command. the top 3 bits used to contain the _IOW and _IOR stuff - which is not needed in solaris. we now only decode the magic character and the command integer. this requires that _IOW and _IOR commands not share a common integer value. this was done to maintain compatibility with Versatec's solaris driver. */ #define MODINFOBANNER "IKON hardcopy - 23 Oct, 1995" /************************************************************************ * * * This driver is provided at no charge to IKON's customers * * in the hope that it will assist them in understanding and * * using IKON's Sbus hardcopy interface products. This code * * is intended to be a working and (relatively!) bug free driver * * when running on the machine and OS rev available to IKON. * * IKON will attempt to keep this code running on current OS and * * hardware from SUN - and others - but does not guarantee this. * * The user is encouraged to contact IKON with comments, * * suggestions, and BUG REPORTS. * ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ihcp_io.h" #include "ihcp_reg.h" #include "ihcp_var.h" static void *state_head; /* opaque handle top of state structs */ /* These are the entry points into our driver that are called when the driver is loaded, during a system call, or in response to an interrupt. */ static int ihcp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int ihcp_identify(dev_info_t *dip); static int ihcp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int ihcp_open(dev_t *dev, int openflags, int otyp, cred_t *credp); static int ihcp_close(dev_t dev, int openflags, int otyp, cred_t *credp); static int ihcp_write(dev_t dev, struct uio *uiop, cred_t *credp); static int ihcp_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp, int *rvalp); static u_int ihcp_intr(); static int ihcp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); /* These are internal private functions - although strategy is called by physio, it is not made visible externally */ static int ihcp_strategy(struct buf *bp) ; static void ihcp_timeout(struct ihcp_unit *unit_p) ; static int ihcp_rdy_wait(struct ihcp_unit *unit_p) ; static int ihcp_half_wait(struct ihcp_unit *unit_p) ; /* When our driver is loaded or unloaded, the system calls our _init or _fini routine with a modlinkage structure. The modlinkage structure contains: modlinkage-> modldrv-> dev_ops-> cb_ops cb_ops contains the normal driver entry points and is roughly equivalent to the cdevsw & bdevsw structures in previous releases. dev_ops contains, in addition to the pointer to cb_ops, the routines that support loading and unloading our driver. */ static struct cb_ops ihcp_cb_ops = { ihcp_open, ihcp_close, nulldev, /* not a block driver */ nodev, /* no print routine */ nodev, /* no dump routine */ nodev, /* no read routine; write-only device */ ihcp_write, ihcp_ioctl, nodev, /* no devmap routine */ nulldev, /* no mmap routine */ nulldev, /* no segmap routine */ nochpoll, /* no chpoll routine */ ddi_prop_op, 0, /* not a STREAMS driver */ D_NEW | D_MP, /* safe for multi-thread/multi-processor */ }; /* NOTE: declare and initialize the dev_ops structure. if it is necessary to prevent the system from unloading the driver on a timed basis (it will if no driver calls are made for some (unknown!) length of time) replace ihcp_detach with nodev. this will keep the driver from unloading. if this is done, it will be necessary to do a reconfiguration boot any time a new version of the driver is to be loaded. */ static struct dev_ops ihcp_ops = { DEVO_REV, /* DEVO_REV indicated by manual */ 0, /* device reference count */ ihcp_getinfo, ihcp_identify, nulldev, /* device probe for non-self-id */ ihcp_attach, ihcp_detach, /* REPLACE W/nodev IF DRIVER SHOULD STAY ATTACHED */ nodev, /* device reset routine */ &ihcp_cb_ops, (struct bus_ops *)0, /* bus operations */ }; extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, MODINFOBANNER, &ihcp_ops, }; static struct modlinkage modlinkage = { MODREV_1, /* MODREV_1 indicated by manual */ (void *)&modldrv, NULL, /* termination of list of linkage structures */ }; /* When our driver is loaded, ihcp_identify() is called with a dev_info_t full of information from the FCode on our board. */ static int ihcp_identify(dev_info_t *dip) { char *dev_name; dev_name = ddi_get_name(dip); if (strcmp(dev_name, IHCP_NAME) == 0) { return (DDI_IDENTIFIED); } else { return (DDI_NOT_IDENTIFIED); } } /* ihcp_attach gets called if ihcp_identify returns DDI_IDENTIFIED This is a very paranoid attatch routine. We take all the knowledge we have about our board and check it against what has been filled in for us from our FCode. */ static int ihcp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { volatile register struct dmaio_regs *dregs_p ; volatile register struct ihcp_regs *iregs_p ; register struct ihcp_unit *unit_p; int instance; int result; int error; char minor_node_name[6] ; if (cmd != DDI_ATTACH) return (DDI_FAILURE); instance = ddi_get_instance(dip); /* we have room for only 6 characters in node name - inculding null at end of string */ if((instance < 0) || (instance > 9)) { cmn_err(CE_CONT,"ihcp_attach: instance %d: instance out of range!\n",instance) ; return (DDI_FAILURE) ; } if (ddi_soft_state_zalloc(state_head, instance) != 0) { cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_soft_state_zalloc failure!\n",instance) ; return (DDI_FAILURE); } unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head, instance); ddi_set_driver_private(dip, (caddr_t)unit_p); unit_p->dip = dip; unit_p->saved_instance = instance ; /* interrupt number is 0 (we only use one interrupt level) ddi_idevice_cookie_t is not used a magic cookie for later use in MUTEXes will be returned in unit_p->iblock_cookie */ if (ddi_add_intr(dip, 0, &unit_p->iblock_cookie, (ddi_idevice_cookie_t *) NULL, ihcp_intr, (caddr_t) instance) != DDI_SUCCESS) { cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_add_intr error!\n",instance) ; return (DDI_FAILURE); } /* initialize MUTEX; we will use this MUTEX later on to lock our instance structure */ mutex_init(&unit_p->mutex, "ihcp mutex", MUTEX_DRIVER, (void *)&unit_p->iblock_cookie); /* we have a block of dma registers and a block of working registers check to make sure nreg==2 and the registers are the correct sizes */ error = ddi_dev_nregs(dip,&result); if (error == DDI_FAILURE || result != 2) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_dev_nreg error!\n",instance) ; return (DDI_FAILURE); } ddi_dev_regsize(dip, 0, (off_t *)&result); if (result != sizeof (struct dmaio_regs)) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_dev_regsize error!\n",instance) ; return (DDI_FAILURE); } ddi_dev_regsize(dip, 1, (off_t *)&result); if (result != sizeof (struct ihcp_regs)) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_dev_regsize error!\n",instance) ; return (DDI_FAILURE); } /* the registers look OK, so let's map them in so we can use them */ if (ddi_map_regs(dip, 0, (caddr_t *)&(unit_p->dma_regs_p), 0, sizeof (struct dmaio_regs)) != DDI_SUCCESS) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_map_regs error!\n",instance) ; return (DDI_FAILURE); } if (ddi_map_regs(dip, 1, (caddr_t *)&(unit_p->ihcp_regs_p), 0, sizeof (struct ihcp_regs)) != DDI_SUCCESS) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); ddi_unmap_regs(dip, 0, (caddr_t *)&(unit_p->dma_regs_p), 0, sizeof (struct dmaio_regs)); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_map_regs error!\n",instance) ; return (DDI_FAILURE); } /* ddi_create_minor_node creates an entry in an internal kernel table; the actual entry in the file system is created by drvconfig(1) when you run add_drv(1); DDI_PSEUDO seems like a strange node type - it was recommended by the Sbus folks! we will save the instance as our minor # for future availability to write and ioctl routines use string format to generate ihcpx node name */ sprintf(minor_node_name,"ihcp%d",instance) ; if (ddi_create_minor_node(dip, minor_node_name, S_IFCHR, instance, DDI_PSEUDO, NULL) == DDI_FAILURE) { mutex_destroy(&unit_p->mutex); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); ddi_soft_state_free(state_head, instance); ddi_unmap_regs(dip, 0, (caddr_t *)&(unit_p->dma_regs_p), 0, sizeof (struct dmaio_regs)); ddi_unmap_regs(dip, 1, (caddr_t *)&(unit_p->ihcp_regs_p), 0, sizeof (struct ihcp_regs)); cmn_err(CE_CONT,"ihcp_attach: instance %d: ddi_create_minor_node error!\n",instance) ; return (DDI_FAILURE); } /* initialize some unit structure elements */ unit_p->unit_flags = 0 ; unit_p->dma_time = HZ * DMA_TIME_DEF ; unit_p->fifo_time = HZ * FIFO_TIME_DEF ; /* get the board type and save it first get a pointer at the regs - we do it this way so that we can work through a volatile pointer - just in case the compiler gets cute! */ iregs_p = unit_p->ihcp_regs_p ; dregs_p = unit_p->dma_regs_p ; unit_p->unit_flags = iregs_p->Ihcp_ds_co & IHCP_BOARD_BITS ; /* set and clear the dma chip's reset bit. if left set the system will panic when a dma xfer is attempted */ dregs_p->Dmacsr = DMAIO_DEV_RESET ; dregs_p->Dmacsr = 0 ; /* pulse the Dmaio flush bit - reset doesn't clear the t/c flag */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* master clear the IKON registers and fifo. this does not send a reset to the device. that is done by the application code if needed. it is also done when the bus is reset. */ iregs_p->Ihcp_mc = 0 ; /*any value works*/ /* if versatec type board, set to default print/plot mode this is not done by the board at sysclear usually we want to emulate the VME board and power up in normal print mode - but we will give the user the option to change the default NOTE THAT THE SAME DEFAULT IS USED FOR ALL BOARDS!!!!!! */ if((iregs_p->Ihcp_ds_co & IHCP_BOARD_BITS) != IHCP_CENTRONICS) iregs_p->Ihcp_ds_co = IHCP_MODESET_RYON | MODE_DEF ; /* save mode in unit structure */ unit_p->mode = MODE_DEF ; /* set the board to the default speed NOTE THAT THE SAME SPEED IS USED FOR ALL BOARDS!!!! */ iregs_p->Ihcp_lf = SPEED_DEF ; /* we do not enable interrupts until we actually intend to sleep - unlike some VME drivers that keep ints on all the time - scary! */ /* we have commented out the attach message to avoid lots of console messages as the driver is loaded and unloaded (arbitrarily) by the kernel cmn_err(CE_CONT,"ihcp_attach: instance %d: attached!\n",instance) ; */ ddi_report_dev(dip); return (DDI_SUCCESS); } /* This is a pretty generic getinfo routine as described in the manual. */ static int ihcp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { register int error; register struct ihcp_unit *unit_p; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head, getminor((dev_t)arg)); if (unit_p == NULL) { *result = NULL; cmn_err(CE_CONT,"ihcp_getinfo: null unit_pointer!\n") ; error = DDI_FAILURE; } else { mutex_enter(&unit_p->mutex); *result = unit_p->dip; mutex_exit(&unit_p->mutex); error = DDI_SUCCESS; } break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)getminor((dev_t)arg); error = DDI_SUCCESS; break; default: *result = NULL; error = DDI_FAILURE; cmn_err(CE_CONT,"ihcp_getinfo: unrecognized info_command!\n") ; } return (error); } /* _init, _info, and _fini support loading and unloading the driver. */ static int _init(void) { register int error; if ((error = ddi_soft_state_init(&state_head, sizeof (struct ihcp_unit), 1)) != 0) return (error); if ((error = mod_install(&modlinkage)) != 0) ddi_soft_state_fini(&state_head); return (error); } static int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int _fini(void) { int status; if ((status = mod_remove(&modlinkage)) != 0) return (status); ddi_soft_state_fini(&state_head); return (status); } /* When our driver is unloaded, ihcp_detach cleans up and frees the resources we allocated in ihcp_attach. */ static int ihcp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { register struct ihcp_unit *unit_p; /* will point to this */ int instance; if (cmd != DDI_DETACH) return (DDI_FAILURE); instance = ddi_get_instance(dip); unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head, instance); /* turn off the board (including interrupts) BUT DO NOT FLUSH THE FIFO - a timed detach could theoretically happen while there was still stuff in the fifo. attach will do a mclr - hopefully it won`t clobber a previous job that was still in the fifo when a timed detach was done! */ /* set and clear the dma chip's reset bit. */ unit_p->dma_regs_p->Dmacsr = DMAIO_DEV_RESET ; unit_p->dma_regs_p->Dmacsr = 0 ; /* pulse the Dmaio flush bit - reset doesn't clear the t/c flag */ unit_p->dma_regs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* Remove the minor node created in attach */ ddi_remove_minor_node(dip, NULL); ddi_unmap_regs(dip, 0, (caddr_t *)&unit_p->dma_regs_p, 0, sizeof (struct dmaio_regs)); ddi_unmap_regs(dip, 1, (caddr_t *)&unit_p->ihcp_regs_p, 0, sizeof (struct ihcp_regs)); ddi_remove_intr(unit_p->dip, 0, unit_p->iblock_cookie); mutex_destroy(&unit_p->mutex); ddi_soft_state_free(state_head, instance); /* message print commented out so we don't clutter up the console as the driver is loaded and unloaded by the kernel cmn_err(CE_CONT,"ihcp_detach: instance %d: detached!\n",instance) ; */ return (DDI_SUCCESS); } /* ihcp_open is called in response to the open(2) system call */ static int ihcp_open(dev_t *dev, int openflags, int otyp, cred_t *credp) { int retval = 0; int instance ; register struct ihcp_unit *unit_p; instance = getminor(*dev) ; unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head, instance); /* Verify instance structure */ if (unit_p == NULL) { cmn_err(CE_CONT,"ihcp_open: null unit_pointer!\n"); return (ENXIO); } /* Verify we are being opened as a character device */ if (otyp != OTYP_CHR) { cmn_err(CE_CONT,"ihcp_open: instance %d: wrong open_type!\n",instance); return (EINVAL); } /* Verify that we are being opened for write - WRITE ONLY DEVICE!! */ if(!(openflags & FWRITE)) { cmn_err(CE_CONT,"ihcp_open: instance %d: write only device!\n",instance); return(ENOTTY) ; } /* lock unit structure (& flags & registers) */ mutex_enter(&unit_p->mutex); /* start MUTEX */ /* Only allow a single open */ if (unit_p->unit_open != FALSE) { retval = EBUSY; } else { /* mark device as open */ unit_p->unit_open = TRUE; /* clear unit flags - but not board type */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS ; /* set default timeouts again not really necessary - but handy during testing */ unit_p->dma_time = HZ * DMA_TIME_DEF ; unit_p->fifo_time = HZ * FIFO_TIME_DEF ; } mutex_exit(&unit_p->mutex); /* end MUTEX */ return (retval); } /* ihcp_close is called after the last process that has the device open calls close(2) since we enforce exclusive open, ihcp_close will always be called when the user process calls close(2) */ static int ihcp_close(dev_t dev, int openflags, int otyp, cred_t *credp) { register struct ihcp_unit *unit_p; unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head, getminor(dev)); /* Verify instance structure - if this fails, we are really in trouble! */ if (unit_p == NULL) { cmn_err(CE_CONT,"ihcp_close: null unit_pointer!\n"); return (ENXIO); } /* lock unit structure (and flags and registers) */ mutex_enter(&unit_p->mutex); /* start MUTEX */ /* let the next person open the device */ unit_p->unit_open = FALSE; /* we don't reset the board and device here - that might kill a job that was in the device but not yet complete - reset in detach */ mutex_exit(&unit_p->mutex); /* end MUTEX */ return (0); } /* end of source module 0 */ /* start of source module 1 */ static int ihcp_write(dev_t dev, struct uio *uiop, cred_t *credp) { register struct ihcp_unit *unit_p ; int retval; int instance ; retval = 0; instance = getminor(dev) ; unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head,instance) ; if (unit_p == NULL) { cmn_err(CE_CONT,"ihcp_write: instance %d: unit_pointer NULL!\n",instance) ; return(ENXIO) ; } /* we are exclusive open - we will leave the mutex for strategy so no mutex_enter(&unit_p->mutex) */ /* clear the timeout and sig rx'd flags -- also clears "waiting for" flags strategy will set the "waiting for dma" flag. */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS ; /* call physio (and strategy) to start the write. NULL means use bur from pool */ retval = physio(ihcp_strategy,(struct buf *)NULL, dev, B_WRITE, minphys, uiop) ; /* check flags for timeout error or signal received */ if(unit_p->unit_flags & IHCP_DMA_TIMEOUT) { retval = EIO ; cmn_err(CE_CONT,"ihcp_write: instance %d: DMA timeout! \n",instance) ; } if(unit_p->unit_flags & IHCP_SIG_RECEIVED) { retval = EINTR ; cmn_err(CE_CONT,"ihcp_write: instance %d: signal received! \n",instance) ; } /* we did not aquire a mutex within write so no mutex_exit(&unit_p->mutex) ; */ return (retval); } /* note that the ihcp_ioctl routine treats arg as a pointer!!!!!!! */ static int ihcp_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp, int *rvalp) { register struct ihcp_unit *unit_p ; /* points at our unit struct */ volatile register struct dmaio_regs *dregs_p; /* points at dma registers */ volatile register struct ihcp_regs *iregs_p ; /* points at ikon registers */ u_long bits ; /* will contain incoming or outgoing argument value */ u_char ct ; /* temp char */ u_short st ; /* temp short */ u_long lt ; /* temp long */ int count, i, t ; int retval ; /*-- return value (errno) for system call*/ int instance ; /* our instance */ int command ; int waitval ; /* value returned by rdy or half wait */ u_int return_array[6] ; /* will contain copy of registers to return to caller */ u_char data_array[256] ; /* used to contain p-i/o data out values after copyin */ retval = 0; instance = getminor(dev) ; unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head,instance) ; if (unit_p == NULL) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: unit_pointer NULL!\n",instance) ; return(ENXIO) ; } /* lock our unit structure */ mutex_enter(&unit_p->mutex) ; iregs_p = unit_p->ihcp_regs_p ; /* get ikon register pointer */ dregs_p = unit_p->dma_regs_p; /* and dma register pointer */ command = cmd & IHCPIO_CMD_MASK ; /* get command */ count = (cmd & IHCPIO_COUNT_MASK) >> 16 ; /*arg size for data out */ /* get first word of argument (even if we are eventually doing a copyout operation) - this saves a copyin in each case */ copyin((caddr_t)arg, (caddr_t)&bits, sizeof(bits)) ; /* note our arg is a pointer! */ #define MASK IHCPIO_CMD_MASK /* use to strip the count from cases */ /* PLEASE NOTE THAT THERE SHOULD BE ANOTHER LEVEL OF INDENTATION IN THE FOLLOWING CODE - IT WAS CONVERTED FROM AN EARLIER DRIVER AND IT WAS JUST TOO MUCH WORK TO FIDDLE THE INDENTATION! */ switch(command) { /* go do the deed */ case MASK & IHCPIO_DEV_RESET: /* send reset to attached device */ iregs_p->Ihcp_lf |= IHCP_LONG_RESET ; /* set long reset bit */ for(t=0; t<100; t++) t=t ; /* give it some length */ iregs_p->Ihcp_lf &= ~IHCP_LONG_RESET ; /* clear it */ break ; case MASK & IHCPIO_SET_CONFIG: /* set speed and if to use busy */ bits &= (IHCP_IGNORE_BUSY | IHCP_SPEED3) ; /* pertinent bits only*/ if((bits & IHCPIO_IGNORE_BUSY) != 0) { /* if ig busy requested */ if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at SET_CONFIG - attempt to set",instance) ; cmn_err(CE_CONT," ignore busy mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } } iregs_p->Ihcp_lf = (u_char)bits ; /* legal bits - set speed and */ /* ignore busy mode if requested*/ break ; case MASK & IHCPIO_SET_DMATIME: /* set dma timeout parameter */ if(bits < DMA_TIME_MIN ) bits = DMA_TIME_MIN ; /* not less than min*/ if(bits > DMA_TIME_MAX ) bits = DMA_TIME_MAX ; /* or more than max */ unit_p->dma_time = (u_int)(bits * HZ) ; /* set ticks */ break ; case MASK & IHCPIO_SET_FIFOTIME: /* set timeout for fifo <1/2 full */ /* or fifo empty & dev rdy wait */ if(bits < FIFO_TIME_MIN) bits = FIFO_TIME_MIN ; /* not less than */ if(bits > FIFO_TIME_MAX) bits = FIFO_TIME_MAX ; /* or more than..*/ unit_p->fifo_time = (u_int)(bits * HZ) ; /* ticks*/ break ; case MASK & IHCPIO_GET_REGS: /* get all the board's registers*/ return_array[0] = dregs_p->Dmacsr ; /* get dma csr */ return_array[1] = dregs_p->Dmaar ; /* get add register */ return_array[2] = dregs_p->Dmabcr ; /* get count register */ return_array[3] = (u_long)iregs_p->Ihcp_lf ; /* get latched functions*/ return_array[4] = (u_long)iregs_p->Ihcp_is_do ; /* interface status */ return_array[5] = (u_long)iregs_p->Ihcp_ds_co ; /* device status */ copyout((caddr_t)return_array, (caddr_t)arg, sizeof(return_array)) ; break ; case MASK & IHCPIO_GET_STATUS: /* get formatted version of status*/ ct = iregs_p->Ihcp_ds_co & 0x1F ; /* get device status bits */ /* now complement where necessary to make pretty status rpt */ bits = (u_long)( ct ^ (IHCPIO_DEV_BUSY | IHCPIO_DEV_POUT | IHCPIO_DEV_SEL)) ; copyout((caddr_t)&bits, (caddr_t)arg, sizeof(bits)) ; break ; case MASK & IHCPIO_GET_BOARD: /* get board type */ bits = (u_long)(unit_p->unit_flags & IHCP_BOARD_MASK); copyout((caddr_t)&bits, (caddr_t)arg, sizeof(bits)) ; break ; case MASK & IHCPIO_SET_VMODE:/* set versatec mode - leave rdy ff on */ /* make sure the board is a versatec type - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at SET_VMODE - attempt to do versatec",instance) ; cmn_err(CE_CONT," mode set on centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((iregs_p->Ihcp_is_do & IHCP_FIFO_NOT_FULL) == 0) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at SET_VMODE - no room in fifo for",instance) ; cmn_err(CE_CONT," versatec mode set! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* bottom two bits contain print/plot/spp control */ iregs_p->Ihcp_ds_co = IHCP_MODESET_RYON | ((u_char)bits & 0x03); /* save mode in unit structure */ unit_p->mode = (u_char)bits & 0x03 ; break ; case MASK & IHCPIO_SET_VMODEX:/* set versatec mode - leave rdy ff off */ /* make sure the board is a versatec type - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at SET_VMODEX - attempt to do versatec",instance) ; cmn_err(CE_CONT," mode set on centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((iregs_p->Ihcp_is_do & IHCP_FIFO_NOT_FULL) == 0) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at SET_VMODEX - no room in fifo for",instance) ; cmn_err(CE_CONT," versatec mode set!\n") ; retval = EIO ; goto IOCTLEXIT ; } /* bottom two bits contain print/plot/spp control */ iregs_p->Ihcp_ds_co =IHCP_MODESET_RYOFF | ((u_char)bits & 0x03); /* save mode in unit structure */ unit_p->mode = (u_char)bits & 0x03 ; break ; case MASK & IHCPIO_V_CMD: /* issue versatec pulse command */ /* make sure the board is a versatec type - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at V_CMD - attempt to issue versatec",instance) ; cmn_err(CE_CONT," command to centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((iregs_p->Ihcp_is_do & IHCP_FIFO_NOT_FULL) == 0) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at V_CMD - no room in fifo for versatec",instance) ; cmn_err(CE_CONT," command!\n") ; retval = EIO ; goto IOCTLEXIT ; } /* bottom two bits contain command */ iregs_p->Ihcp_ds_co = ((u_char)bits & 0x03); break ; case MASK & IHCPIO_DATA_OUT: /* output chars from arg */ /* # of chars in bottom of cmd */ /* make sure # of chars (in bits) is <=255 - we know it is positive since we masked it. it must be <=255 since that is that is the most that can be stuffed into the fifo when it is <= half full. we will check to make sure that the fifo is not full before sending each byte. after dma, half full may be true - (fifo will not have more than 1/2 + 1 byte in it) - so we can't check 1/2 full flag and then just barf data at it!!!. */ if(count > 255) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at DATA_OUT - too many bytes!\n",instance) ; retval = EINVAL ; goto IOCTLEXIT ; } copyin((caddr_t)arg,(caddr_t)data_array,count) ; for(i=0; iIhcp_is_do & IHCP_FIFO_NOT_FULL)) { retval = EIO ; cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at DATA_OUT - fifo full while",instance) ; cmn_err(CE_CONT," transferring data bytes!\n") ; cmn_err(CE_CONT,"0x%x bytes requested, 0x%x bytes transferred\n", count, i) ; break ; } iregs_p->Ihcp_is_do = data_array[i] ; } break ; case MASK & IHCPIO_RDY_WAIT: /* wait for rdy & fifo empty - or timeout */ waitval = ihcp_rdy_wait(unit_p) ; if(waitval < 0) { /* timeout may not be error */ /* so no error messsage */ /* caller can test for timeout */ retval = EIO ; goto IOCTLEXIT ; } if(waitval > 0) { /* signal is not error but send to */ /* console */ /* log. return EINTR so caller */ /* can decide what to do!! */ cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at RDY_WAIT - signal while waiting",instance) ; cmn_err(CE_CONT," for ready & fifo empty!\n") ; retval = EINTR ; goto IOCTLEXIT ; } break ; case MASK & IHCPIO_HALF_WAIT:/* wait for fifo 0) { /* signal is not error but send to*/ /* console */ /* log. return EINTR so caller*/ /* can decide what to do!! */ cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at HALF_WAIT - signal while waiting",instance) ; cmn_err(CE_CONT," for fifo unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at STREAM_ON - attempt to set streaming",instance) ; cmn_err(CE_CONT," mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } iregs_p->Ihcp_lf |= IHCP_DATA_STREAM ; /* or in bit */ break ; case MASK & IHCPIO_STREAM_OFF: /* turn streaming off (if 10106) */ if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at STREAM_OFF - attempt to clear",instance) ; cmn_err(CE_CONT," streaming mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } iregs_p->Ihcp_lf &= ~IHCP_DATA_STREAM ; /* clear it */ break ; case MASK & IHCPIO_GET_FLAGS: /* get unit flag longword */ copyout((caddr_t)&unit_p->unit_flags,(caddr_t)arg,sizeof(u_int)) ; break ; case MASK & IHCPIO_GET_FIFO: /* get formatted fifo flags */ ct = iregs_p->Ihcp_is_do & 0x1C ; /* get fifo bits */ lt = (u_long)(ct ^ (IHCP_FIFO_NOT_FULL | IHCP_FIFO_NOT_HALF | IHCP_FIFO_NOT_EMPTY)) ; /*complement bits*/ copyout((caddr_t)<,(caddr_t)arg,sizeof(u_int)) ; break ; case MASK & IHCPIO_MASTER_CLEAR: /* master clear board - not for use by wimps! */ iregs_p->Ihcp_mc = 0 ; /* issue mclr to board */ break ; case MASK & IHCPIO_SOFT_ACK: /* software ack - looks like an ack from the dev*/ iregs_p->Ihcp_sa = 0 ; break ; case MASK & LPSETVERSATEC: /* compatibility - set versatec port */ /* make sure the board is a versatec type - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPSETVERSATEC - attempt to select",instance) ; cmn_err(CE_CONT," versatec port on centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } break ; case MASK & LPSETCENTRONICS: /* compatibility - set centr port */ if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPSETCENTRONICS - attempt to select",instance) ; cmn_err(CE_CONT," centronics port on versatec-type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } break ; case MASK & LPCOMMAND: /* compatibility - old style cmds */ /* as much as possible, allow multiple command bits in the argument. this is probably not the way it was intended, but we want to protect ourselves against non-reasonable uses of the old calls */ /* assert long reset signal */ if(bits & LPC_LRST) iregs_p->Ihcp_lf |= IHCP_LONG_RESET ; /* de-assert long reset */ if(bits & LPC_DRST) iregs_p->Ihcp_lf &= ~IHCP_LONG_RESET ; /* test for option port request - better be a 106 board !! */ if(bits & LPC_SOPT) if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to select",instance) ; cmn_err(CE_CONT," centronics port on versatec-type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* test for versatec port selection */ if(bits & LPC_SVPT) if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to select",instance) ; cmn_err(CE_CONT," versatec port on centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* enable data streaming - if this is a 106 board ! */ if(bits & LPC_DSTR) { if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to set",instance) ; cmn_err(CE_CONT," streaming mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } iregs_p->Ihcp_lf |= IHCP_DATA_STREAM ; /* or in bit */ } /* reset data streaming bit */ if(bits & LPC_DDST) { if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to clear",instance) ; cmn_err(CE_CONT," streaming mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } iregs_p->Ihcp_lf &= ~IHCP_DATA_STREAM ; /* clear it */ } /* test for versatec print/plot mode changes - make sure we are a versatec board !! */ if(bits & LPC_PRINTMASK) { /* make sure the board is a versatec type - can't do w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to do",instance) ; cmn_err(CE_CONT," versatec mode set on centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this cmd talks to the fifo - make sure there is room !! */ if((iregs_p->Ihcp_is_do & IHCP_FIFO_NOT_FULL) == 0) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - no room in fifo",instance) ; cmn_err(CE_CONT," for versatec mode set! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* get current mode setting */ ct = unit_p->mode ; /* now look at individual command bits and twiddle mode */ if(bits & LPC_VSPP) ct |= IHCP_SPP_MODE ; /* set spp mode */ if(bits & LPC_DSPP) ct &= ~IHCP_SPP_MODE ; /* clear spp */ if(bits & LPC_VPLT) ct |= IHCP_PLOT_MODE ; /* set plot mode*/ if(bits & LPC_DVPT) ct &= ~IHCP_PLOT_MODE ; /* clear plot */ if(bits & LPC_PRNT) ct = 0 ; /* 0 = normal print mode */ /* write the final mode selection to the fifo */ iregs_p->Ihcp_ds_co = IHCP_MODESET_RYON | ct ; /* save result in unit structure */ unit_p->mode = ct ; } /* end of mode set if */ /* check for software ack pulse request */ if(bits & LPC_SACK) iregs_p->Ihcp_sa = 0 ; /* any value will do*/ /* check for master clear request */ if(bits & LPC_MCLR) { /* this gets more complicated!!! */ iregs_p->Ihcp_mc = 0 ; /* issue mclr to board */ iregs_p->Ihcp_lf |= IHCP_LONG_RESET ; /* reset to device */ for(t=0;t<10;t++) t=t ; /* stretch it */ iregs_p->Ihcp_lf &= ~IHCP_LONG_RESET ; /* clear it */ /* set to normal print mode - the board doesn't do this automatically, and we want to look as much like the existing VME driver as possible THIS IS A NO_OP ON A 10106 */ iregs_p->Ihcp_ds_co = IHCP_MODESET_RYON ; /* above will send 0 to mode reg and leave ready on -- we know fifo has room since we just did a master clear */ /* set mode byte in unit structure to 0 (normal print mode) */ unit_p->mode = 0 ; } /* end of mclr if */ /* look for pulsed command request that has to go through fifo */ if(bits & (LPC_VCLR | LPC_VTFF | LPC_VEOT | LPC_VLTR)) { /* make sure the board is a versatec type - can't do w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - attempt to do",instance) ; cmn_err(CE_CONT," versatec pulsed command to centronics type unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this cmd talks to the fifo - make sure there is room !! */ /* WE WILL ASSUME THAT THERE WILL ONLY BE ONE PULSE PER CALL */ if((iregs_p->Ihcp_is_do & IHCP_FIFO_NOT_FULL) == 0) { cmn_err(CE_CONT,"ihcp_ioctl: instance %d: at LPCOMMAND - no room in fifo",instance) ; cmn_err(CE_CONT," for versatec pulsed command! \n") ; retval = EIO ; goto IOCTLEXIT ; } if(bits & LPC_VCLR) iregs_p->Ihcp_ds_co = IHCP_VCLR ; if(bits & LPC_VTFF) iregs_p->Ihcp_ds_co = IHCP_VFED ; if(bits & LPC_VEOT) iregs_p->Ihcp_ds_co = IHCP_VEOT ; if(bits & LPC_VLTR) iregs_p->Ihcp_ds_co = IHCP_VLTR ; } /* end of pulsed command if */ break ; case MASK & LPGETREGS: /* old style (VMEbus) get regs command */ st = 0 ; /* init temp u_short-used to collect bits */ /* start work on interface status register */ ct = iregs_p->Ihcp_is_do ; /* get interface status reg */ if(ct & IHCP_FIFO_DEV_RDY) st |= OLD_DIRY ; /* int ready */ if(ct & IHCP_DEV_RDY) st |= OLD_DVRY ; /* dev ready */ ct = iregs_p->Ihcp_lf ; /* get latched functions */ if(ct & IHCP_DATA_STREAM) st |= OLD_DSTR ; /* data stream */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) st |= OLD_SOPT ; /* centronics */ lt = (u_long)(st << 16) ; /* stuff result in top of long temp */ /* do the same ugly things with the device status register -- it has two halves: versatec and centronics -- only work on the appropriate half */ st = 0 ; if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* work on centronics part. get the device status reg */ ct = iregs_p->Ihcp_ds_co ; if(ct & IHCP_VRDY_CACK) st |= OLD_OACK ; /* ack on */ if(ct & IHCP_C_NOT_BUSY) st |= OLD_ONBY ; /* not busy */ if(ct & IHCP_PAPER_OK) st |= OLD_OPPR ; /* paper pres */ if(ct & IHCP_OFFLINE) st |= OLD_ONSL ; /* offline */ if(ct & IHCP_C_FAULT) st |= OLD_OFLT ; /* fault */ ct = iregs_p->Ihcp_lf ; /* get latched reg */ if(ct & IHCP_LONG_RESET) st |= OLD_OLRS ; /* long reset */ } else { /* work on versatec part */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_VERS_TTL) st |= OLD_VTTL ; /* ttl mode*/ ct = iregs_p->Ihcp_ds_co ; /* get dev status reg */ if(ct & IHCP_VRDY_CACK) st |= OLD_VRDY ; /* vers ready*/ if(ct & IHCP_PAPER_OK) st |= OLD_VPPR ; /* paper ok */ if(!(ct & IHCP_OFFLINE)) st |= OLD_VONL ; /* vers onlin*/ /* get current mode from unit struct - don't use the board's mode bits, since they are on the other side of the fifo and may not be the same as the input side */ ct = unit_p->mode ; if(ct & IHCP_SPP_MODE) st |= OLD_VSPP ; /* spp mode */ if(ct & IHCP_PLOT_MODE) st |= OLD_VPLT ; /* plot mode */ } lt |= st ; /* save in bottom of long temp */ copyout((caddr_t)<,(caddr_t)arg,sizeof(u_int)) ; break; case MASK & LPSETTIMVAL: /* set timeout value - old style - for dma and fifo empty at same time */ /* old style timeout call used fiftieths of a second as argument*/ if(bits < DMA_TIME_MIN * 50) bits = DMA_TIME_MIN * 50 ; if(bits > DMA_TIME_MAX * 50) bits = DMA_TIME_MAX * 50 ; /* old style used fiftieths of a second - probably will not be the same on Sbus machines(sun4c HZ = 100) */ unit_p->fifo_time = (bits * HZ)/50 ; unit_p->dma_time = (bits * HZ)/50 ; break; case MASK & LPGETTIMVAL: /* get current timeout (in ticks ?) */ /* return # of fiftieths of a second */ lt = (unit_p->dma_time * 50)/HZ ; copyout((caddr_t)<,(caddr_t)arg,sizeof(u_int)) ; break; default: /* we don't print an error message here if we are involved in a pipe, the pipe logic may send us a generic ioctl to determine if we are a terminal we have to return an error or we will give a false terminal indication. */ retval = EINVAL; break; } IOCTLEXIT: mutex_exit(&unit_p->mutex) ; return (retval); } /* start of source module 2 */ static u_int ihcp_intr(caddr_t instance) { /* the poor indentation is left over from an earlier driver! */ register struct ihcp_unit *unit_p ; /* points at our unit structure */ volatile register struct dmaio_regs *dregs_p ; /* points at dma registers */ volatile register struct ihcp_regs *iregs_p ; /* points at ikon registes */ register struct buf *bp ; /* points at system buffer in use */ u_int int_serviced = DDI_INTR_UNCLAIMED ; u_long csr ; /* control and status register */ u_int flags ; /* unit array flags for this unit */ /* get unit pointer */ unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head,(u_int)instance) ; /* lock unit array and associated hardware */ mutex_enter(&unit_p->mutex) ; /* get register and buffer pointers */ dregs_p = unit_p->dma_regs_p ; iregs_p = unit_p->ihcp_regs_p ; bp = unit_p->buf_p ; /* get dma controller status and see if our unit is interrupting IF THE CV_WAIT_SIG() CALL IN STRATEGY OR IOCTL WAS AWAKENED BY A SIGNAL OR TIMEOUT JUST AS THE DONE OR READY (OR WHATEVER) INTERRUPT ARRIVED, IT IS POSSIBLE THAT THIS CODE WAS ENTERED, AND BLOCKED ON THE MUTEX WHILE STRATEGY OR IOCTL TURNED OFF INTERRUPTS AND FLUSHED OUT THE CSR. WHEN THE INTERRUPT MUTEX UNBLOCKED, THE DMAIO_INT_PENDING BIT WOULD NO LONGER BE SET. THIS IS ACTUALLY WHAT WE WANT, SINCE WE HAVE FLAGGED A TIMEOUT, OR SIGNAL, AND DO NOT WANT TO PROCESS AN INTERRUPT. IT ALSO HAS THE EFFECT OF CAUSING THE FOLLOWING CODE TO BE SKIPPED (ALSO DESIRABLE) BUT that CAUSES US TO RETURN DDI_INTR_UNCLAIMED WHICH MAY CAUSE THE KERNEL TO DISPLAY AN "sbus interrupt not serviced" WARNING. AS LONG AS THE KERNEL JUST DISPLAYS A WARNING, AND DOES NOT PANIC - THIS IS A HARMLESS EVENT. IT IS VERY DIFFICULT - PERHAPS IMPOSSIBLE - TO COMPLETELY AVOID THIS RACE CONDITION. IT IS ALSO EXTREMELY UNLIKELY THAT IT WILL OCCUR IN THE FIRST PLACE, SINCE A PROPERLY CHOSEN TIMEOUT SHOULD NOT TERMINATE DMA THAT IS SUCCESFULLY PROCEEDING, AND SIGNALS WILL MOST LIKELY ONLY BE SENT TO HUNG PROCESSES. */ csr = dregs_p->Dmacsr ; if(csr & (DMAIO_INT_PENDING | DMAIO_ERR_PENDING)) { int_serviced = DDI_INTR_CLAIMED ; /* get unit flags */ flags = unit_p->unit_flags & (IHCP_DMA_WAIT | IHCP_RDY_WAIT | IHCP_HALF_WAIT) ; switch(flags) { case IHCP_DMA_WAIT: /* we are waiting for dma t/c interrupt */ /*--- check for error pending ---*/ if(csr & DMAIO_ERR_PENDING) { cmn_err(CE_CONT,"ihcp_intr: instance %d: error pending - flush_bfr issued! \n",instance) ; bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; /* what else to do ??? */ } /*--- better be t/c bit set ---*/ if(!(csr & DMAIO_TC)) { cmn_err(CE_CONT,"ihcp_intr: instance %d: waiting-for-dma w/ tc not set! \n",instance) ; bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; /* hardware error!!! */ } /*--- turn off dma enb bit (should already be off) ---*/ dregs_p->Dmacsr &=~DMAIO_DMA_ENABLE ; /*--- turn off Dmaio interrutp enable bit ---*/ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; /* do a flush buffer to clear interrupt pending bit - if we leave it until strategy, and someone else is on this level, and we are 1st in the polling chain, we may get polled and falsely enter this routine, but since we don't flush we will loop forever!!!!! */ /* use ddi_poke to write a flush command to the dmaio chip -- somewhere it says that systems using buffered writes can't guarantee when a write will arrive at the io bus. supposedly poke is guaranteed to wait until the write is complete before returning. this is necessary because we dare not re-enable ints while the int line is slowly floating high */ /*set csr to flush-bfr to turn off tc, err pend, and int pend & enb's */ ddi_pokel(unit_p->dip,(long *)&dregs_p->Dmacsr,DMAIO_FLUSH_BFR) ; drv_usecwait(1) ; /* allow time for the int line to rise (slowly!) */ cv_signal(&unit_p->cv) ; /* wake up strategy */ break ; case IHCP_HALF_WAIT: /* waiting for fifo less than 1/2 full */ case IHCP_RDY_WAIT: /* waiting for fifo empty & dev rdy int */ /* make sure that the int flag is set - GROSS ERROR if not */ if(!(iregs_p->Ihcp_is_do & IHCP_INTFLAG)) { cmn_err(CE_CONT,"ihcp_intr: instance %d: waiting-for-rdy interrupt w/ no",instance) ; cmn_err(CE_CONT," interrupt flag set \n") ; /* no way to pass this up */ /* to higher level !!! */ } /*--- turn off Dmaio interrupt enable bit ---*/ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; /* use ddi_poke to write a flush command to the dmaio chip -- somewhere it says that systems using buffered writes can't guarantee when a write will arrive at the io bus. supposedly poke is guaranteed to wait until the write is complete before returning. this is necessary because we dare not re-enable ints while the int line is slowly floating high */ /*set csr to flush-bfr to turn off tc, err pend, and int pend & enb's */ ddi_pokel(unit_p->dip,(long *)&dregs_p->Dmacsr,DMAIO_FLUSH_BFR) ; drv_usecwait(1) ; /* allow for slow int lines */ /*--- wake up the code that is waiting for fifo/rdy interrupt ---*/ cv_signal(&unit_p->cv) ; break ; default: /**** we got an actual interrupt when not waiting for anything!!! since no bp assigned - we don't have any way to flag it - so just print a message, and try to turn off the offending interrupt. ****/ cmn_err(CE_CONT,"ihcp_intr: instance %d: interrupt when not waiting! \n",instance); /*--- turn off ihcp int enable bits ---*/ iregs_p->Ihcp_lf &= ~(IHCP_RINT_ENB | IHCP_HALF_WAIT) ; /*--- issue reset int flag - after clearing enables ---*/ iregs_p->Ihcp_ri = 0 ; /*--- turn off int flag ---*/ iregs_p->Ihcp_ri = 0 ; /*set csr to flush-bfr to turn off tc, err pend, and int pend & enb's */ /* use ddi_poke to write a flush command to the dmaio chip -- somewhere it says that systems using buffered writes can't guarantee when a write will arrive at the io bus. supposedly poke is guaranteed to wait until the write is complete before returning. this is necessary because we dare not re-enable ints while the int line is slowly floating high */ ddi_pokel(unit_p->dip,(long *)&dregs_p->Dmacsr,DMAIO_FLUSH_BFR) ; drv_usecwait(1) ; /* allow for slow int lines */ /* we don't wake up anything - since there isn't anything sleeping!!! */ break ; } } /* end of if interrupting */ mutex_exit(&unit_p->mutex) ; /* release unit structure */ return(int_serviced) ; } /* end of ihcp_intr */ static int ihcp_strategy(struct buf *bp) { volatile register struct dmaio_regs *dregs_p; /* points to dma registers */ register struct ihcp_unit *unit_p ; /* points to unit structure */ int instance ; int timeout_id ; ddi_dma_handle_t dma_handle ; ddi_dma_cookie_t dma_cookie ; int sleepval ; /* saves value returned by cv_wait */ static ddi_dma_lim_t dma_lim = { /* initialize limit structure */ DMA_LIM_LOWER_ADD, DMA_LIM_UPPER_ADD, DMA_LIM_COUNTER, DMA_LIM_BURST_SIZE, DMA_LIM_MIN_XFER, DMA_LIM_SPEED, } ; /* get instance number from buffer - and various useful pointers */ instance = getminor(bp->b_edev) ; unit_p = (struct ihcp_unit *)ddi_get_soft_state(state_head,instance) ; dregs_p = unit_p->dma_regs_p ; /* lock unit structure and pointers */ mutex_enter(&unit_p->mutex) ; /* save buffer pointer in unit structure for error returns from int and possibly timeout */ unit_p->buf_p = bp ; /* check byte count size */ if(bp->b_bcount > DMA_MAXBLOCK) { cmn_err(CE_CONT,"ihcp_strategy: instance %d: DVMA byte count too large! \n",instance) ; bp->b_error |= EINVAL ; bp->b_flags |= B_ERROR ; goto EXITSTRATEGY ; } /* set up dma - using our own dma limit structure values (BETA used defaults) */ if(ddi_dma_buf_setup((dev_info_t *)unit_p->dip, bp, DDI_DMA_WRITE, NULL, 0, &dma_lim, (ddi_dma_handle_t *)&dma_handle) != DDI_DMA_MAPPED) { bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; cmn_err(CE_CONT,"ihcp_strategy: instance %d: ddi_dma_buf_setup failure!\n",instance) ; goto EXITSTRATEGY ; } /* get dma cookie for dma hardware */ if(ddi_dma_htoc(dma_handle, (off_t)0, (ddi_dma_cookie_t *)&dma_cookie) != DDI_SUCCESS) { bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; ddi_dma_free(dma_handle) ; cmn_err(CE_CONT,"ihcp_strategy: instance %d: ddi_dma_htoc failure!\n",instance) ; goto EXITSTRATEGY ; } /* make sure that the mapped buffer doesn't cross a 16Mbyte boundary. only the lower 24 bits of our add counter can increment. ddi_dma_buf_setup is supposed to look at our dma_lim structure, and avoid such mappings, but the book says that it is NOT GUARANTEED! we will just post an error and return - since we have no graceful way to carve the buffer into pieces in this routine */ if(((dma_cookie.dmac_address & DMA_LIM_COUNTER) + bp->b_bcount) > (DMA_LIM_COUNTER + 1)) { bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; ddi_dma_free(dma_handle) ; cmn_err(CE_CONT,"ihcp_strategy: instance %d: dma mapping crosses 16Mbyte boundary!\n",instance) ; goto EXITSTRATEGY ; } /* Write the DVMA start address and byte count to the hardware. */ dregs_p->Dmaar = dma_cookie.dmac_address ; dregs_p->Dmabcr = bp->b_bcount ; /* set waiting for dma in unit_flags. error flags not cleared here so they will last between multiple calls to strategy by physio. this way if physio breaks up a buffer into multiple buffers, a timeout will still show up in flags when we get back to the _write routine. */ unit_p->unit_flags |= IHCP_DMA_WAIT ; /* start the timeout counter - it will call ihcp_timeout(instance) */ timeout_id = timeout(ihcp_timeout,(void *)unit_p,unit_p->dma_time) ; /* Enable interrupts so we will know when the DVMA is done. Start the DVMA in memory read mode */ dregs_p->Dmacsr = (DMAIO_INT_ENABLE | DMAIO_DMA_ENABLE | DMAIO_COUNT_ENABLE ) ; /* sleep until done interrupt, signal, or timeout */ sleepval = cv_wait_sig(&unit_p->cv, &unit_p->mutex) ; dregs_p->Dmacsr &= ~DMAIO_DMA_ENABLE ; /* do 1st in case signal arrives*/ /* while dma is running!!!! */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* clears enables & cleans up */ /* flush must also be done in interrupt and timeout code to avoid endless loops if int pending left on! */ untimeout(timeout_id) ; /* turn off timer */ bp->b_resid = dregs_p->Dmabcr ; /* set remaining byte count */ ddi_dma_free(dma_handle) ; /* free DVMA resource */ /* check for signal termination or timeout and flag accordingly */ if(sleepval == 0) { unit_p->unit_flags |= IHCP_SIG_RECEIVED ; bp->b_error |= EINTR ; bp->b_flags |= B_ERROR ; } if(unit_p->unit_flags & IHCP_DMA_TIMEOUT) { bp->b_error |= EIO ; bp->b_flags |= B_ERROR ; } EXITSTRATEGY: biodone(bp) ; /* tell physio that we are done */ /* unlock unit structure */ mutex_exit(&unit_p->mutex) ; return(0) ; } /* end of stratety */ /* function ihcp_timeout() called by the real time clock interrupt handler if timeout is reached. will issue wakeup. also sets timeout flag in unit_array much simplified from earlier version. leaves most bookkeeping to higher level code. */ static void ihcp_timeout(register struct ihcp_unit *unit_p) { register volatile struct dmaio_regs *dregs_p ; u_int flags ; mutex_enter(&unit_p->mutex) ; /* Set pointer to DVMA registers. */ dregs_p = unit_p->dma_regs_p ; /* turn off interrupts so we don't get rudely interrupted!! */ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; flags = unit_p->unit_flags & (IHCP_DMA_WAIT | IHCP_RDY_WAIT | IHCP_HALF_WAIT) ; switch(flags) { case IHCP_DMA_WAIT: /* we are waiting for dma t/c interrupt */ /*--- set timeout waiting for dma flag bit ---*/ unit_p->unit_flags |= IHCP_DMA_TIMEOUT ; /* turn off dma and int enable bits */ dregs_p->Dmacsr &= ~(DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE) ; /* do flush buffer to clear int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; cv_signal(&unit_p->cv) ; /* wake up strategy */ break ; case IHCP_HALF_WAIT: /* waiting for fifounit_flags |= IHCP_RDY_TIMEOUT ; if(flags == IHCP_HALF_WAIT) unit_p->unit_flags |= IHCP_HALF_TIMEOUT ; /*--- wake up the code that is waiting for fifo/rdy interrupt ---*/ /*--- we can't flag an error in buf - since we aren't doing i/o---*/ /* turn off dma and int enable bits */ dregs_p->Dmacsr &= ~(DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE) ; /* do flush buffer to clear int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; cv_signal(&unit_p->cv) ; break ; default: /* if we get here things are really cooked !!! */ /* turn off dma and int enable bits */ dregs_p->Dmacsr &= ~(DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE) ; /* do flush buffer to clear int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; break ; } mutex_exit(&unit_p->mutex) ; } /* end of timeout */ static int ihcp_rdy_wait(register struct ihcp_unit *unit_p) /* wait for fifo empty & device ready */ /* returns 0 if condition true, */ /* -1 if timed out, +1 if signal int'd*/ { volatile register struct dmaio_regs *dregs_p; volatile register struct ihcp_regs *iregs_p; int retval ; int timeout_id ; /* we are already within the ioctl routine's mutex lock - so no mutex or start critical here */ retval = 0 ; /*--- make register pointers ---*/ dregs_p = unit_p->dma_regs_p ; iregs_p = unit_p->ihcp_regs_p ; /*--- clear waiting-for and timeout flags in unit array ---*/ unit_p->unit_flags &= IHCP_CLEAR_FLAGS ; /*--- see if already ready & fifo empty - if so just exit w/retval=0 ---*/ if(iregs_p->Ihcp_is_do & IHCP_FIFO_DEV_RDY) return(retval) ; /*--- if not, set waiting-for flag and fire up interrupts ---*/ unit_p->unit_flags |= IHCP_RDY_WAIT ; /*--- start the timeout counter - it will call ihcp_timeout() ---*/ timeout_id = timeout(ihcp_timeout,(void *)unit_p,unit_p->fifo_time) ; /*--- enable fifo empty and device ready interrupt ---*/ iregs_p->Ihcp_lf |= IHCP_RINT_ENB ; /*--- enable external interrupts in DMAIO chip ---*/ dregs_p->Dmacsr = (DMAIO_INT_ENABLE) ; /* sleep on cv until rdy, timeout, or signal */ if(cv_wait_sig(&unit_p->cv, &unit_p->mutex) == 0) retval = 1; /* flags sig received */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* turns off everything! */ untimeout(timeout_id) ; /* stop watchdog timer */ iregs_p->Ihcp_lf &= ~(IHCP_RINT_ENB | IHCP_HINT_ENB) ; /* ikon enb's off*/ iregs_p->Ihcp_ri = 0 ; /* clear flag AFTER enb's turned off !!!!!!!!!!!!*/ /*--- check timeout flags ---*/ if(unit_p->unit_flags & IHCP_RDY_TIMEOUT) retval = -1 ; return(retval); } /* end of ihcp_rdy_wait */ static int ihcp_half_wait(register struct ihcp_unit *unit_p) /* wait for fifo dma_regs_p ; iregs_p = unit_p->ihcp_regs_p ; /*--- clear waiting-for and timeout flags in unit array ---*/ unit_p->unit_flags &= IHCP_CLEAR_FLAGS ; /*--- see if already fifo Ihcp_is_do & IHCP_FIFO_NOT_HALF) return(retval) ; /*--- if not, set waiting-for flag and fire up interrupts ---*/ unit_p->unit_flags |= IHCP_HALF_WAIT ; /*--- start the timeout counter - it will call ihcp_timeout() ---*/ timeout_id = timeout(ihcp_timeout,(void*)unit_p,unit_p->fifo_time) ; /*--- enable fifo half full interrupt ---*/ iregs_p->Ihcp_lf |= IHCP_HINT_ENB ; /*--- enable external interrupts in DMAIO chip ---*/ dregs_p->Dmacsr = (DMAIO_INT_ENABLE) ; /* sleep on bp until fifo <1/2 full, timeout, or signal */ if(cv_wait_sig(&unit_p->cv, &unit_p->mutex) == 0) retval = 1; /* flags sig received */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* turns off everything! */ untimeout(timeout_id) ; /* stop watchdog timer */ iregs_p->Ihcp_lf &= ~(IHCP_RINT_ENB | IHCP_HINT_ENB) ; /* ikon enb's off*/ iregs_p->Ihcp_ri = 0 ; /* clear flag AFTER enb's turned off !!!!!!!!!!!!*/ /*--- check timeout flags ---*/ if(unit_p->unit_flags & IHCP_HALF_TIMEOUT) retval = -1 ; return(retval); } /* end of ihcp_half_wait() */