/* ik310.c modified for 10.0 converged i/o May, 1996 IKON Corporation 2617 Western Avenue Seattle, Washington 98110 phone: (206) 728-6465 fax: (206) 728-1633 */ /* code module for hp-ux driver for IKON ISA/EISA hardcopy boards. Includes code for compatiblilty with IKON's other unix hardcopy drivers, and for the driver for IKON's VME board (supplied by SUN and Versatec). THIS CODE IS OFFERED TO IKON'S CUSTOMERS AT NO CHARGE, WITH THE INTENT THAT IT BE USED WITH IKON'S BOARD LEVEL PRODUCTS. WHILE IKON INTENDS TO KEEP THE DRIVER CURRENT WITH HP'S HARDWARE AND OPERATING SYSTEMS, AND TO KEEP THE CODE AS BUG FREE AS POSSIBLE, THIS IS NOT GUARANTEED. THIS DRIVER IS DESIGNED TO SUPPORT IKON'S MODEL 10111 ISA/EISA HARDCOPY BOARD. IT CAN TAKE ADVANTAGE OF THE 10111'S ODD BYTE COUNT CAPABILITY, AND MAY IN THE FUTURE BE ENHANCED TO INCLUDE THE 10111'S OTHER ADVANCED FEATURES. THE 10111 IS CAPABLE OF MUCH HIGHER PERFORMANCE THAN IKON'S OLDER ISA HARDCOPY BOARDS, AND IS THE RECOMMENDED SOLUTION FOR HIGH PERFORMANCE PLOTTING IN ISA AND EISA SLOTS. THIS DRIVER WILL CURRENTLY SUPPORT THE OLDER 10092 AND 10097 IS BOARDS FROM IKON. NEITHER OF THESE BOARDS CAN SUPPORT AN ODD BYTE COUNT WHEN RUNNING ON A 16 BIT DMA CHANNEL (requiring even byte counts). IN THE FUTURE THIS DRIVER MAY BE MODIFIED IN WAYS THAT NO LONGER ALLOW SUPPORT OF THE 10092 AND 10097. THE DATA PRODUCTS PORTION OF THE 10097 IS NOT EXPLICITLY SUPPORTED! */ /* The approach taken here is that multiple boards will use multiple drivers. Each driver uses a pre-defined board address to probe for the 10111 board. Since these boards appear as ISA slaves on the bus, they share (with all ISA boards on the bus) a common ID of 0. There is no way for a driver's attach code to identify a board by ID. The board's registers must be probed at a specific address to determine whether the board being probed exists, and is in fact the correct board for the driver. The use of a single driver and board address per installed board, rather than a single, multiple board driver, that probes multiple addresses, should help reduce the possibility of attaching a foreign board. The availablity of multiple drivers and addresses makes it easier for the user to select a device address that avoids conflict with other boards that may be installed on the bus. NOTE THAT ALTHOUGH THE 10111 IS AN ISA SLAVE, IT DOES SUPPORT EISA ENHANCED DMA MODES FOR IMPROVED PERFORMANCE. The various copies of the driver all use the same .h files: ikreg.h & ikio.h. Defines in the code modules control which address is recognized by each driver. Any externally visible variable or function is distinctly named for each driver. Variables and functions that are strictly internal share the same name between drivers. */ /* 14 July, 1992 Initial release 12 August, 1992 added missing () after two spl6 calls 20 October, 1992 "commented out" the error message in the default case in the ioctl code. If a pipe was established to the driver, it was apparently being probed with a generic ioctl to determine if it was a terminal. We return EINVAL, which is appropriate, and causes no problems, but the printed error message was causing concern over what is not really an error. 7 June, 1994 adopted changes suggested by Susan Danz to correct bug in strategy. Heavily loaded machine was running out of Kernel memory. The sys_memall call was returning a null pointer, which was causing serious problems. Declared the dma_parms structure locally. Added code to fix the case where two or more 10111s were installed in the same machine. This bug case causes both drivers to attach both boards. Fixed by hard coding the board's slot in the source code. This is quite crude, but is made necessary by the way HP-UX probes ISA cards. A better way might be to put the board's address in the configuration file, and look for that, but that requires a more elaborate config file, and considerably more changes to the driver. (Availability of a 7xx machine for testing is a problem!) fixed a minor bug in strategy that set an error code directly into b_error, rather than oring it in. May, 1996 Modified for hp-ux converged I/O rules, for HP-UX 10.10 and beyond. Added special header and Install() routine as required. Added isc_claim in attach function. File was edited for readability and printing. */ #include "../h/file.h" #include "../h/types.h" #include "../h/errno.h" #include "../h/buf.h" #include "../machine/eisa.h" #include "../machine/eeprom.h" #include "../h/hpibio.h" #include "../h/param.h" #include "./ikreg.h" /* internal definitions */ #include "./ikio.h" /* external definitions */ /* added for converged driver at 10.0 */ #include #include #include #include #include #include #include /* the address defined below will change with each copy of the driver */ #define IK_ADDR 0x310 /* address changes per driver */ /* the following are defines of the external entry point names - they will change per driver to match the address and the slot in which we are installed. Recompile for other adds and/or slots */ #define IK_INSTALL ik310_install #define IK_LINK ik310_link #define IK_ATTACH ik310_attach #define IK_OPEN ik310_open #define IK_CLOSE ik310_close #define IK_WRITE ik310_write #define IK_IOCTL ik310_ioctl #define IK_SLOT 1 /* 5/96-defines for new converged header */ #define IK_OPS ik310_ops #define IK_INFO ik310_info #define IK_DATA ik310_data #define IK_WSIO_INFO ik310_wsio_info /* the defines below may change depending on the device attached to the card - they are placed here, rather than in ikreg.h, where they would affect all drivers */ #define IK_MODE_DEF VERS_NORM_PRINT /* board opens in this mode */ #define IK_RDY_TIME_DEF 1800 /* timeout defaults are 30 minutes! */ #define IK_DMA_TIME_DEF 1800 /* defines that probably will not change */ #define IK_ID 0 /* all ISA cards have ID = 0 */ #define IK_DMA_TIMING DMA_TYPEB /* all our cards can run type b timing */ #define IK_DMA_MODE DMA_DEMAND /* 92 & 97 will single cycle anyway - but 10111 will block x8 words */ /* Forward declarations */ int IK_INSTALL(); int IK_LINK() ; int IK_ATTACH() ; int IK_OPEN() ; int IK_CLOSE() ; int IK_WRITE() ; int IK_IOCTL() ; /* these fields are filled in within the driver. */ /* Device Driver - Access via bdevsw table */ static drv_ops_t IK_OPS = { IK_OPEN, IK_CLOSE, NULL, NULL, NULL, NULL, NULL, IK_WRITE, IK_IOCTL, NULL, NULL, NULL, NULL, NULL, NULL, C_MAP_BUFFER_TO_KERNEL, }; /* Driver-specific for all cdio drivers; set up for dynamic major and minor assignment */ static drv_info_t IK_INFO = { "ik310", "ext_bus", DRV_CHAR | DRV_SAVE_CONF, -1, -1, NULL, NULL, NULL, }; /* Driver-Specific fields */ static wsio_drv_data_t IK_DATA = { "scsi", T_DEVICE, DRV_CONVERGED, NULL, NULL, }; /* Pointers to previous structures */ static wsio_drv_info_t IK_WSIO_INFO = { &IK_INFO, &IK_OPS, &IK_DATA, }; extern int (*eisa_attach)(); extern caddr_t map_isa_address(); static int (*ik_saved_attach)(); static int ik_strategy(); static int ik_wakeup(); static int ik_isr(); static int ik_timeout() ; static int ik_rdy_wait() ; /* most of this stuff should probably be stuffed into our isc structure, particularly if we implement multiple devices within one driver */ static int ik_irq; static int ik_dma_size; static int ik_channel; static int ik_attached = 0; static int ik_opened = 0; static int ik_saved_resid = 0 ; /* fake range counter */ static int ik_dma_time = IK_DMA_TIME_DEF * HZ; /* timeout counts, in cycles */ static int ik_rdy_time = IK_RDY_TIME_DEF * HZ ; static u_long ik_flags = 0 ; /* contains waiting-for flags, timeout flags, interface mode done this way to try for some compatibility with our sbus driver */ static int ik_sleephook ; /* we will use this for sleeping within strategy & ioctl to avoid using a system buffer pointer - and hopefully avoiding any possibility of deadlock. */ static int ik_sleeping = 0 ; /* used to flag return from sleep - will still be set if we are awakened by a signal instead of wakeup() */ /* Here is the new Install Routine */ IK_LINK() { ik_saved_attach = eisa_attach; eisa_attach = IK_ATTACH; } /* end of link */ IK_INSTALL() { IK_LINK(); /* call the link function */ return (wsio_install_driver (&IK_WSIO_INFO)); /* then register driver with WSIO and return error, if any */ } IK_ATTACH(id, isc) int id; struct isc_table_type *isc; { struct ik_reg *ik_reg; /* make sure that we are called with the isc structure for our assigned slot. this keeps us from attaching multiple 10111s more than once each. it also keeps us from attaching a single 10111 more than once if there are other ISA boards in the machine. We get called for each ISA board installed, regardless of whether it is a 10111 or not. we may get called multiple times with other board's isc structures. This is a terribly inelegant approach, but effective. */ if ((id == IK_ID)&&(((struct eisa_if_info *)(isc->if_info))->slot\ == IK_SLOT)) { /* since this is an ISA card, its id will be 0. Map in its 8 bytes worth of registers using map_isa_address */ /* if we implement multiple boards, we could check this isc's INITIALIZED flag - to see if we have already initialized attached this board*/ /* WAIT UNTIL PROBE SUCCESS TO SET THE REG POINTER IN ISC! */ ik_reg = (struct ik_reg *)map_isa_address(isc, IK_ADDR); /* Did map_isa_address succeed? If so, is there a a card at our address? If so, assume it's ours. Latched fcn register powers up to 0, and the switch reg should always be non-zero - use these for sanity checks */ if ((ik_reg)&&(ik_reg->latch_func == 0)&&(ik_reg->switches)) { /* board & isc are ours - so NOW WE CAN SAVE REG POINTER! */ isc->if_reg_ptr = ik_reg ; /* get a few values from card */ ik_channel = (ik_reg->switches & DMA_CH_MASK) >> 4; ik_irq = ik_reg->switches & INT_LEVEL_MASK; if (ik_reg->card_status & DMA_BYTE) ik_dma_size = DMA_8BYTE; else ik_dma_size = DMA_16BYTE; /* if this card does any interrupts, call eisa_isrlink for each IRQ line it uses */ eisa_isrlink(isc, ik_isr, ik_irq, 0); /* indent changed here for ease in reading */ /* set initialized flag & clear error flag in isc */ ((struct eisa_if_info *)(isc->if_info))->flags |= INITIALIZED; ((struct eisa_if_info *)(isc->if_info))->flags &= ~INIT_ERROR; /* then claim the isc from the wsio services */ (struct eisa_if_info *) isc_claim(isc, &IK_WSIO_INFO); ik_attached = 1; /* prevent open success if not attached */ printf("\nIKON 10111 driver - revision 1 - 7 June, 1994\n"); printf("\nik%x_attach: IKON 10111 Hardcopy++ Board attached\n", IK_ADDR) ; printf(" board address = 0x%x\n",IK_ADDR) ; printf(" interrupt level = 0x%x\n",ik_irq) ; printf(" DMA channel = 0x%x\n",ik_channel) ; if(ik_dma_size == DMA_8BYTE) printf(" DMA mode = BYTE\n") ; else printf(" DMA mode = WORD\n") ; ik_flags = ik_reg->dev_status & BOARD_MASK ; /* save a copy */ if(ik_flags == VERS_TTL) printf(" interface mode = Versatec TTL\n"); if(ik_flags == VERS_DIFF) printf(" interface mode = Versatec DIFF\n"); if(ik_flags == CENT) printf(" interface mode = Centronics\n"); if(ik_flags == DP_DIFF) printf(" interface mode = Data Products DIFF\n"); if(ik_flags == DP_TTL) printf(" interface mode = Data Products TTL\n"); } else { /* if null mapping, or latches != 0 or switches == 0 */ printf("ik%x_attach: no card found\n",IK_ADDR); ((struct eisa_if_info *)(isc->if_info))->flags \ |= INIT_ERROR; } } return((*ik_saved_attach)(id, isc)); } /* end of attach */ IK_OPEN(dev, flags) dev_t dev; int flags; { struct isc_table_type *isc; struct ik_reg *ik_reg; if (!ik_attached) { printf ("ik%x_open: ikon driver was not attached!\n",IK_ADDR); return (ENOSYS); } if (ik_opened) /* exclusive open device */ return(EBUSY); /* MUST REMAIN EXCLUSIVE OPEN */ /* IN FUTURE - SINCE WE DO */ /* THINGS LIKE SLEEPING WITHIN */ /* STRATEGY - ON AN ADDRESS */ /* INTERNAL TO THE DRIVER. WE */ /* MUST AVOID ANY POSSIBILITY */ /* OF DEADLOCK!! */ if(!(flags & FWRITE)) { /* write only device!!!!!! */ printf("ik%x_attach: write only device!\n",IK_ADDR) ; } /* get isc pointer for this device changed for converged io (OPEN) */ wsio_get_isc(dev, &isc, &IK_WSIO_INFO); if(!isc) { /* test for null isc - access via null could cause a system panic since ik_reg undefined! */ printf("ik%x_open: NULL isc pointer!\n",IK_ADDR) ; return(EIO) ; } ik_reg = (struct ik_reg *)isc->if_reg_ptr; ik_reg->pulse_func = MASTER_CLEAR; /* default mode = print */ /* unlike Sbus cards, these boards don't return from i/o until the fifo is empty and the device is ready - so issuing an MCLR (& potential mode change because of it) should be ok. */ /* a close of the previous job followed by an open & MCLR is safe. */ /* set initial print-plot mode - some plotters may always want plot mode, and this way, the user won't have to call ioctl to set plot */ ik_reg->latch_func = (ik_reg->latch_func & VERS_MODE_MASK) | IK_MODE_DEF; /* set mode reg to 0. this version of the driver does not implement dma and interrupt tri-state controls or level interrupt mode. Data strobe inhibit is available via ioctl, and odd byte counts are taken care of in strategy. NOTE that none of this is available on a 10092, and only the tri-state stuff is available on a 10097. The 10111 is the only board that implements strobe gating and odd byte capability in word dma mode */ ik_reg->modes = 0 ; ik_dma_time = IK_DMA_TIME_DEF * HZ ; /* timeout counts in cycles/ ik_rdy_time = IK_RDY_TIME_DEF * HZ ; /* we won't issue a device clear here either - could change */ /* interrupts are NOT enabled here: only as needed, unlike the sample*/ ik_flags &= IHCP_BOARD_MASK ; /* clear flags but not type */ ik_opened = 1; return(0); } /* end of open */ IK_CLOSE(dev) dev_t dev; { struct isc_table_type *isc; struct ik_reg *ik_reg; wsio_get_isc(dev, &isc, &IK_WSIO_INFO) ; /* changed for converged io (CLOSE)*/ ik_reg = (struct ik_reg *)isc->if_reg_ptr ; ik_reg->pulse_func = MASTER_CLEAR ; /* make sure ints & dma OFF!!*/ /* might get here via signal!*/ /* we should catch any signal*/ /* at a lower level - but we*/ /* won't take the chance!*/ /* Normally, we won't get here /* unless device is ready*/ ik_opened = 0; return(0); } /* end of close */ IK_WRITE(dev, uio) dev_t dev; struct uio *uio; { int retval ; /* The NULL in the physio call means that we are using a system buffer pointer rather than a locally declared buffer pointer. I am informed that future revs of hp-ux may have restrictions on the type of memory space that a buffer pointer can occupy - so we will leave it to the system to worry about that. However - we will use an internal address for other sleep-wakeup pairs within the driver - we will not use a system buffer pointer. */ /* Clear the compatibility flags. Error and use flags will stay set until the next write or ioctl call. Strategy will set the waiting for dma flag. */ ik_flags &= IHCP_CLEAR_FLAGS ; retval = (physio(ik_strategy, NULL, dev, B_WRITE, minphys, uio)); /* check for flags from strategy - print here instead of strategy to avoid a massive stream of printfs at a low level. we will let the error# set in strategy return to the caller */ if(ik_flags & IHCP_DMA_TIMEOUT) printf("ik%x_write: DMA timeout!\n",IK_ADDR) ; if(ik_flags & IHCP_SIG_RECEIVED) printf("ik%x_write: signal received!\n",IK_ADDR) ; return(retval) ; } /* end of write */ static int ik_strategy(bp) struct buf *bp; { struct dma_parms dma_parms; struct isc_table_type *isc; struct ik_reg *ik_reg; int pri, ret, resid, oddflag ; wsio_get_isc(bp->b_dev, &isc, &IK_WSIO_INFO); /* Changed for converged io (STRATEGY) */ ik_reg = (struct ik_reg*)isc->if_reg_ptr ; /* Leaving out the sample's test for device ready, since the dma logic will happily wait until the board is ready. */ bp->b_s2 = &dma_parms; bzero(&dma_parms, sizeof(struct dma_parms)) ; isc->owner = bp; dma_parms.dma_options = DMA_WRITE | IK_DMA_TIMING | IK_DMA_MODE | ik_dma_size ; dma_parms.channel = ik_channel; dma_parms.flags = 0; dma_parms.drv_routine = ik_wakeup; dma_parms.drv_arg = (int)isc; dma_parms.addr = bp->b_un.b_addr; dma_parms.count = bp->b_bcount; /* Check for odd byte count when in word mode. If odd & WORD, bump the range count by one to fool dma_setup into thinking that we are doing an even byte count. It will return an error if we ask for an odd byte count in word dma mode. Also set the odd byte control bit on the board - ONLY THE 10111 HAS THIS CAPABILITY. */ if((ik_dma_size == DMA_16BYTE) && (bp->b_bcount & 1)) { dma_parms.count++ ; ik_reg->modes |= ODD_COUNT ; oddflag = 1 ; } else { ik_reg->modes &= ~ODD_COUNT ; oddflag = 0 ; } pri = spl6() ; while (ret = eisa_dma_setup(isc, &dma_parms)) { if (ret == RESOURCE_UNAVAILABLE) { sleep(bp->b_s2, PZERO+2); } else if (ret < 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; iodone(bp); isc->owner = NULL ; printf("ik%x_strategy: eisa_dma_setup returned error %d\n", IK_ADDR, ret); splx(pri) ; return(-1) ; } else break; } splx(pri) ; /* WE WILL LOWER PRI FOR A WHILE _ it seems selfish to do things like call timeout, which may be expensive while at a high priority! HOWEVER - WE WILL BRACKET OUR SLEEP WITH A HIGHER PRIORITY, so we don't miss an interrupt after a short block. */ /* We will sleep on i/o completion rather than let physio do the iowait, since we need to be able to catch signals, so that we can do proper dma cleanup. The interrupt code will do a wakeup instead of iodone. We use an internal address for sleep. */ ik_reg->pulse_func = CLR_INT_FLAG ; /* make sure we start clean - no left over intflags */ ik_flags |= IHCP_DMA_WAIT ; /* For compatibility w/ other drivers flags are cleared in write or ioctl code. We don't clear here so timeout flag will persist through multiple strategy calls by physio */ ik_sleeping = 1 ; /* So we can tell if wakeup or signal */ timeout(ik_timeout,(caddr_t)&ik_sleephook,ik_dma_time,NULL) ; /* Start timer */ pri = spl6() ; /* Bracket dma start & sleep w/high priority*/ ik_reg->latch_func |= (DMA_START | INT_ENABLE) ; /* fire up dma & enable ints */ sleep((caddr_t)&ik_sleephook,(PCATCH | IK_PRI)) ; /* wait for interrupt */ /* Make sure that things are quiet - since we may have gotten here via timeout or sig! */ ik_reg->latch_func &= ~(DMA_START | INT_ENABLE) ; splx(pri) ; ik_reg->pulse_func = CLR_INT_FLAG ; /* Tidy up a bit */ untimeout(ik_timeout,(caddr_t)&ik_sleephook) ; /* Turn off timer */ if(ik_sleeping) { /* Must have gotten signal*/ ik_flags |= IHCP_SIG_RECEIVED ; isc->owner->b_flags |= B_ERROR ; isc->owner->b_error |= EINTR ; } if(ik_flags & IHCP_DMA_TIMEOUT) { /* Flag timeout as error */ isc->owner->b_flags |= B_ERROR ; isc->owner->b_error |= EIO ; } ik_sleeping = 0 ; resid = eisa_dma_cleanup(isc,isc->owner->b_s2) ; /* Tidy up & get residual count */ if((resid > 0 ) && oddflag) resid-- ; /* Fix residual if we fudged count*/ /* Use oddflag, not ODD_COUNT since '92 may return all ones in modes reg */ ik_saved_resid = resid ; /* Save for fake range counter */ bp->b_resid = resid ; /* Return value to caller */ if(resid<0) { /* Test for abnormal termination */ isc->owner->b_flags |= B_ERROR ; isc->owner->b_error |= EIO ; printf("ik%x_strategy: abnormal DMA termination!\n",IK_ADDR) ; } iodone(isc->owner) ; /* Tell physio that we are done */ } /* end of strategy */ IK_IOCTL(dev, function, arg, flag) dev_t dev; u_int function; u_int *arg; u_int flag; { struct isc_table_type *isc; struct ik_reg *ik_reg ; u_int command, count ; /* Decoded ioctl command and arg size */ int i, t, pri ; u_int temp ; char *c_arg ; /* Will be char pointer at arg */ u_short *s_arg ; /* Short pointer to arg */ c_arg = (char *)arg ; /* Make c_arg point at arg */ s_arg = (u_short *)arg ; /* and short pointer */ /* Changed for converged io (IOCTL) */ wsio_get_isc(dev, &isc, &IK_WSIO_INFO); ik_reg = (struct ik_reg*)isc->if_reg_ptr ; /* Get pointer at board */ /* Strip off the size portion of function, so that data out (which has variable arg size) will still recognize its case. */ #define MASK ~IOCSIZE_MASK /* from ioctl.h */ command = function & MASK ; /* Mask defined in ioctl.h */ count = (function & IOCSIZE_MASK) >> 16 ; /* ">>16" must match the macros in ioctl. Will */ /* need to change if unix changes */ switch(command) { /* Go do the deed! */ /*** The following ioctl commands attempt to preserve compatibility with IKON's Sbus board(s) and driver. ****/ case MASK & IHCPIO_DEV_RESET: /* long (latched) reset to device */ ik_reg->latch_func |= DEV_RESET ; for(t=0;t<10;t++) t=t ; /* Give it some length. */ ik_reg->latch_func &= ~DEV_RESET ; break ; case MASK & IHCPIO_SET_CONFIG: /* Does nothing to this board & driver */ /* We don't flag an error since we want */ /* to let existing apps run if possible */ break ; case MASK & IHCPIO_SET_DMATIME: /* Set dma timeout value */ ik_dma_time = *arg * HZ ; break ; case MASK & IHCPIO_SET_FIFOTIME: /* Set ready wait timeout value */ ik_rdy_time = *arg * HZ ; break ; case MASK & IHCPIO_GET_REGS: /* Return board regs to caller */ *arg = 0 ; /* was CSR on Sbus board */ *(arg + 1) = 0 ; /* address reg on Sbus */ *(arg + 2) = ik_saved_resid ; /* fake range count */ *(arg + 3) = ik_reg->latch_func ; *(arg + 4) = ik_reg->card_status ; *(arg + 5) = ik_reg->diag_data ; *(arg + 6) = ik_reg->dev_status ; *(arg + 7) = ik_reg->switches ; *(arg + 8) = 0 ; /* pad */ *(arg + 9) = ik_reg->modes ; *(arg + 10) = 0 ; /* pad */ break ; case MASK & IHCPIO_GET_STATUS: /* return formatted status - matches Sbus driver */ t = ik_reg->dev_status & ~BOARD_MASK ; /* now swizzle the bits */ temp = 0 ; if(t & VERS_READY) temp |= IHCPIO_DEV_RDY ; if(t & CENT_BUSY) temp |= IHCPIO_DEV_BUSY ; if(t & PAPER_OUT) temp |= IHCPIO_DEV_POUT ; if(t & ON_LINE) temp |= IHCPIO_DEV_SEL ; if(t & CENT_FAULT) temp |= IHCPIO_DEV_FAULT ; *arg = temp ; break ; case MASK & IHCPIO_GET_BOARD: /* return board's interface strapping */ *arg = ik_flags & IHCP_BOARD_MASK ; break ; case MASK & IHCPIO_SET_VMODE: /* set versatec print/plot mode */ case MASK & IHCPIO_SET_VMODEX: /* same, except wait for ready */ /* w/this board neither waits */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* error if done to centronics board */ printf("ik%x_ioctl: attempt to set Versatec modes \ on Centronics type device!\n",IK_ADDR) ; return(ENOTTY) ; } temp = ik_reg->latch_func & ~VERS_MODE_MASK ; /* get latched bits minus mode */ ik_reg->latch_func = temp |= (*arg & VERS_MODE_MASK) ; /* stuff in new mode bits */ break ; case MASK & IHCPIO_V_CMD: /* issue versatec pulsed command */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* error if done to centronics board */ printf("ik%x_ioctl: attempt to issue Versatec\ command to Centronics type device!\n",IK_ADDR); return(ENOTTY) ; } /* set flags to waiting for ready - and test for ready before issuing the command - this is probably overkill */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; ik_rdy_wait(ik_reg) ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for ready \ before issuing pulse command!\n",IK_ADDR) ; return(ETIME) ; } if(ik_flags & IHCP_SIG_RECEIVED) { printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; return(EINTR) ; } switch(*arg) { /* convert Sbus style pulse request into this board's format */ case IHCPIO_VLTR: /* remote line terminate */ temp = VERS_LTR ; break ; case IHCPIO_VEOT: /* e-o-t */ temp = VERS_EOT ; break ; case IHCPIO_VFED: /* form feed */ temp = VERS_FF ; break ; case IHCPIO_VCLR: /* buffer clear */ temp = VERS_CLR ; break ; default: printf("ik%x_ioctl: unrecognized pulse \ command request!\n",IK_ADDR) ; return(EINVAL) ; } /* we have to wait for an interrupt after a pulsed command rather than just wait for ready, since a very long cable might delay the not ready response to a command sufficiently that the device would still look ready! this is different from data out: data out sets not ready locally as the byte is sent, but for historical reasons, local ready is left true when a pulse is issued. */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; ik_sleeping = 1 ; timeout(ik_timeout,(caddr_t)&ik_sleephook,ik_rdy_time,NULL) ; pri = spl6() ; ik_reg->pulse_func = CLR_INT_FLAG ; ik_reg->latch_func |= INT_ENABLE ; ik_reg->pulse_func = temp ; /* issue pulse command */ sleep((caddr_t)&ik_sleephook,(PCATCH | IK_PRI)) ; /* wait for interrupt */ ik_reg->latch_func &= ~INT_ENABLE ; /* settle things down */ splx(pri) ; /* restore priority */ untimeout(ik_timeout,(caddr_t)&ik_sleephook) ; /* Turn off Timer */ if(ik_sleeping) { /* must have gotten signal */ ik_flags |= IHCP_SIG_RECEIVED ; printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; ik_sleeping = 0 ; return(EINTR) ; } ik_sleeping = 0 ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for ready after \ issuing pulse command!\n",IK_ADDR) ; return(ETIME) ; } break ; case MASK & IHCPIO_DATA_OUT: /* send as many as 255 bytes to device */ if((count>255) | (count<=0)) { printf("ik%x_ioctl: byte count out of range in \ data out request!\n",IK_ADDR) ; return(EINVAL) ; } /* we don't need to test for ready before p-i/o output - the hardware will wait for ready before cranking out the byte - but we should wait afterward! */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; for(i=0;idata_out = *(c_arg + i) ; ik_saved_resid = (count - i) -1 ; /* leave for posterity!! */ /* we can use ready wait after a byte output instead of explicitly setting up and waiting for an interrupt since the hardware will set not ready as the byte is sent. Long cable delays will not cause a false ready indication, which could happen if pulsed commands were done this way. Pulsed commands do not set not ready locally */ ik_rdy_wait(ik_reg) ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for \ ready after sending byte to device!\n",IK_ADDR); return(ETIME) ; } if(ik_flags & IHCP_SIG_RECEIVED) { printf("ik%x_ioctl: signal received!\n",\ IK_ADDR) ; return(EINTR) ; } } break ; case MASK & IHCPIO_RDY_WAIT: /* wait for device and interface ready */ /* set flags to waiting for ready */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; ik_rdy_wait(ik_reg) ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for ready!\n",\ IK_ADDR) ; return(ETIME) ; /* borrow streams error # */ } if(ik_flags & IHCP_SIG_RECEIVED) { printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; return(EINTR) ; } break ; case MASK & IHCPIO_HALF_WAIT: /* for compatibility - in this driver - just does rdy wait */ /* set flags to waiting for ready */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_HALF_WAIT ; ik_rdy_wait(ik_reg) ; if(ik_flags & IHCP_HALF_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for ready!\n",\ IK_ADDR) ; return(ETIME) ; } if(ik_flags & IHCP_SIG_RECEIVED) { printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; return(EINTR) ; } break ; case MASK & IHCPIO_STREAM_ON: /* turn on data streaming - if strapped for centronics */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to enable data streaming \ to Versatec type device!\n",IK_ADDR) ; return(ENOTTY) ; } ik_reg->latch_func |= DATA_STREAM ; break ; case MASK & IHCPIO_STREAM_OFF: /* turn off data streaming */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to disable data \ streaming to Versatec type device!\n",IK_ADDR) ; return(ENOTTY) ; } ik_reg->latch_func &= ~DATA_STREAM ; break ; case MASK & IHCPIO_GET_FLAGS: /* return flag word */ *arg = ik_flags ; break ; case MASK & IHCPIO_GET_FIFO: /* compatibility - always says fifo empty */ *arg = IHCPIO_FIFO_EMPTY ; break ; /*** strobe inhibit and enable were not included in the Sbus driver. the same function was available on the 10104 and 10105 boards using a "private" mode switch command. It could disable the data strobe for one byte after it was invoked. there is no reasonable way to duplicate that in this driver, so we are using a separate ioctl. THIS IS ONLY AVAILABLE ON THE 10111 ***/ case MASK & IHCPIO_STB_INH: /* disables the data strobe to the device - special apps only! Allows toggling the data out lines without causing a data strobe - leaves the board ready after data out */ ik_reg->modes |= STROBE_INH ; break ; case MASK & IHCPIO_STB_ENB: ik_reg->modes &= ~STROBE_INH ; break ; /**** the following ioctl stuff is to preserve compatibility with various drivers for IKON's VME board. ****/ case MASK & LPSETVERSATEC: /* compatibility - set versatec port on VME card */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ik%x_ioctl: attempt to select Versatec \ port on Centronics interface!\n",IK_ADDR) ; return(ENOTTY) ; } break ; case MASK & LPSETCENTRONICS: /* more compatibility */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to select Centronics \ port on Versatec interface!\n",IK_ADDR) ; return(ENOTTY) ; } break ; case MASK & LPCOMMAND: /* interpret various VME-sytle commands. As much as possible allow multiple command bits per call. it was probably not intended to work this way - but we will try to deal with any possible use of the old IOCTLs */ temp = *arg ; if(temp & LPC_LRST) ik_reg->latch_func |= DEV_RESET ; /* set latched reset */ if(temp & LPC_DRST) ik_reg->latch_func &= ~DEV_RESET ; /* clear it */ if(temp & LPC_SOPT) { /* it is asking to select Centronics port - check strapping */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to select \ Centronics port on Versatec interface!\n", IK_ADDR) ; return(ENOTTY) ; } } if(temp & LPC_SVPT) { /* trying to select versatec port - check strapping */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ik%x_ioctl: attempt to select Versatec \ port on Centronics interface!\n",IK_ADDR) ; return(ENOTTY) ; } } if(temp & LPC_DSTR) { /* make sure data streaming applies to this board strapping */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to enable data \ streaming to Versatec type device!\n", IK_ADDR) ; return(ENOTTY) ; } ik_reg->latch_func |= DATA_STREAM ; } if(temp & LPC_DDST) { /* test for correct interface type for data streaming */ if((ik_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ik%x_ioctl: attempt to disable data \ streaming to Versatec type device!\n", IK_ADDR) ; return(ENOTTY) ; } ik_reg->latch_func &= ~DATA_STREAM ; } if(temp & LPC_PRINTMASK) { /* caller wants to change print/plot modes */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* error if Centronics board */ printf("ik%x_ioctl: attempt to set Versatec \ modes on Centronics type device!\n", IK_ADDR) ; return(ENOTTY) ; } if(temp & LPC_VSPP) ik_reg->latch_func |= VERS_SPP ; /* set spp mode */ if(temp & LPC_DSPP) ik_reg->latch_func &= ~VERS_SPP; /* reset spp mode */ if(temp & LPC_VPLT) ik_reg->latch_func |= VERS_PLOT; /* set plot mode */ if(temp & LPC_DVPT) ik_reg->latch_func &= ~VERS_PLOT; /* reset plot mode */ if(temp & LPC_PRNT) ik_reg->latch_func &= \ ~VERS_MODE_MASK ; /* clear sdpp & plot */ } if(temp & LPC_SACK) ik_reg->pulse_func = SW_ACK ; /* software ack */ if(temp & LPC_MCLR) { /* master clear - on VME board this reset board AND DEVICE */ ik_reg->pulse_func = MASTER_CLEAR ; /* clear board */ ik_reg->latch_func |= DEV_RESET ; /* clear device w/ fat pulse */ for(t=0;t<10;t++) t=t ; ik_reg->latch_func &= ~DEV_RESET ; } if(temp &= (LPC_VCLR | LPC_VTFF | LPC_VEOT | LPC_VLTR)) { /* mask bits & test for pulsed commands */ /* MUST BE THE LAST USE OF temp!!!!! */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* error if done to Centronics board */ printf("ik%x_ioctl: attempt to issue Versatec \ command to Centronics type device!\n",IK_ADDR); return(ENOTTY) ; } /* set flags to waiting for ready - and test for ready before issuing the command */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; ik_rdy_wait(ik_reg) ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: Timeout waiting for ready before \ issuing pulse command!\n",IK_ADDR) ; return(ETIME) ; } if(ik_flags & IHCP_SIG_RECEIVED) { printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; return(EINTR) ; } switch(*arg) { /* convert VME style pulse request into this board's format */ /* ASSUME ONLY ONE PULSE COMMAND REQUEST PER CALL!!!!! */ /* we can use temp for something else now */ case LPC_VLTR: /* remote line terminate */ temp = VERS_LTR ; break ; case LPC_VEOT: /* e-o-t */ temp = VERS_EOT ; break ; case LPC_VTFF: /* form feed */ temp = VERS_FF ; break ; case LPC_VCLR: /* buffer clear */ temp = VERS_CLR ; break ; default: printf("ik%x_ioctl: unrecognized pulse \ command request!\n",IK_ADDR) ; return(EINVAL) ; } /* we have to wait for an interrupt after a pulsed command rather than just wait for ready, since a very long cable might delay the not ready response to a command sufficiently that the device would still look ready! This is different from data out: data out sets not ready locally as the byte is sent, but for historical reasons, local ready is left true when a pulse is issued. */ ik_flags &= IHCP_CLEAR_FLAGS ; ik_flags |= IHCP_RDY_WAIT ; ik_sleeping = 1 ; timeout(ik_timeout,(caddr_t)&ik_sleephook,ik_rdy_time,NULL) ; pri = spl6() ; ik_reg->pulse_func = CLR_INT_FLAG ; ik_reg->latch_func |= INT_ENABLE ; ik_reg->pulse_func = temp ; /* issue pulse command */ sleep((caddr_t)&ik_sleephook,(PCATCH | IK_PRI)) ; /* wait for interrupt */ ik_reg->latch_func &= ~INT_ENABLE ; /* settle things down */ splx(pri) ; /* restore priority */ untimeout(ik_timeout,(caddr_t)&ik_sleephook) ; /* turn off timer */ if(ik_sleeping) { /* must have gotten signal */ ik_flags |= IHCP_SIG_RECEIVED ; printf("ik%x_ioctl: signal received!\n",IK_ADDR) ; ik_sleeping = 0 ; return(EINTR) ; } ik_sleeping = 0 ; if(ik_flags & IHCP_RDY_TIMEOUT) { printf("ik%x_ioctl: timeout waiting for ready after \ issuing pulse command!\n",IK_ADDR) ; return(ETIME) ; } } /* this whole mess wasn't indented!!!! */ break ; case MASK & LPGETREGS: /* caller wants register read-back that looks like old VME card - we will try! */ /* start work on interface status register - use it for board reg & temp for old reg */ temp = 0 ; t = ik_reg->card_status ; if(t & MASTER_READY) temp |= OLD_DIRY ; /* interface ready */ if(t & DEV_READY) temp |= OLD_DVRY ; /* device ready */ t = ik_reg->latch_func ; if(t & DATA_STREAM) temp |= OLD_DSTR ; /* data streaming */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) temp |= \ OLD_SOPT ; /* centronics */ *s_arg = temp ; /* stuff result in first short of arg */ /* go through similar gyrations to make the device status reg. it has two parts - Versatec & Centronics - only work on the appropriate part */ temp = 0 ; if((ik_flags & IHCP_BOARD_MASK) ==IHCPIO_CENT) { /* centronics part */ t = ik_reg->dev_status ; if(t & VERS_READY) temp |= OLD_OACK ; /* ack on */ if(!(t & CENT_BUSY)) temp |= OLD_ONBY ; /* not busy */ if(!(t & PAPER_OUT)) temp |= OLD_OPPR ; /* paper present*/ if(!(t & ON_LINE)) temp |= OLD_ONSL ; /* off line*/ if(t & CENT_FAULT) temp |= OLD_OFLT ; /* fault */ t = ik_reg->latch_func ; if(t & DEV_RESET) temp |= OLD_OLRS ; /* latched reset*/ } else { /* versatec part */ if((ik_flags & IHCP_BOARD_MASK) == IHCPIO_VERS_TTL) \ temp |= OLD_VTTL ; /* ttl mode */ t = ik_reg->dev_status ; if(t & VERS_READY) temp |= OLD_VRDY ; /* Versatec ready */ if(!(t & PAPER_OUT)) temp |= OLD_VPPR ; /* paper present*/ if(!(t & ON_LINE)) temp |= OLD_VONL ; /* online */ t = ik_reg->latch_func ; if(t & VERS_SPP) temp |= OLD_VSPP ; /* spp mode */ if(t & VERS_PLOT) temp |= OLD_VPLT ; /* plot mode */ } *(s_arg + 1) = temp ; /* store in 2nd short of arg */ break ; case MASK & LPSETTIMVAL: /* set timeout - old style - in 50ths of a sec */ ik_rdy_time = (*arg * HZ) / 50 ; /* set ready wait timeout */ ik_dma_time = ik_rdy_time ; /* same for both in old style */ break ; case MASK & LPGETTIMVAL: /* get timeout value in 50ths of a second */ *arg = (ik_dma_time * 50) / HZ ; break ; default: /* printf commented out since we may be probed by a generic ioctl to see if we are a terminal - we should return an error, but not print an error message. printf("ik%x_ioctl: unrecognized ioctl command!\n",IK_ADDR) ; */ return(EINVAL) ; } /* end of ioctl switch */ return(0); } /* end of ioctl */ static int ik_wakeup(isc) struct isc_table_type *isc; { wakeup(isc->owner->b_s2); } /* end of wakeup */ static int ik_isr(isc, arg) /* we are using a direct int handler without going through a software trigger first. This code should the extra penalty of a software trigger call. Also the sample driver may have had a bug in the way that 'interrupt serviced' was reported to the kernel. */ struct isc_table_type *isc; int arg; /* execute quite quickly - so there is no reason to pay */ { struct ik_reg *ik_reg; ik_reg = (struct ik_reg *)isc->if_reg_ptr; if (!(ik_reg->card_status & INTERRUPT_FLAG)) /* my card not interrupting */ return(0); /* force DMA and interrupts off, and clear the int flag */ ik_reg->latch_func &= ~(INT_ENABLE | DMA_START) ; ik_reg->pulse_func = CLR_INT_FLAG ; ik_sleeping = 0; wakeup((caddr_t)&ik_sleephook) ; /* wake up strategy or ioctl - wakeup may get called from both isr and timeout - i think that it is ok to call wakeup twice for one sleep - and that a wakeup when not sleeping is also ok */ /* we DO NOT re-enable interrupts until we intend to use them!! */ return 1; } /* end of isr */ static int ik_timeout(t_arg) /* timeout catcher */ caddr_t t_arg ; { /* set appropriate flag - done for compatibility w/ other drivers */ if(ik_flags & IHCP_DMA_WAIT) ik_flags |= IHCP_DMA_TIMEOUT ; if(ik_flags & IHCP_RDY_WAIT) ik_flags |= IHCP_RDY_TIMEOUT ; if(ik_flags & IHCP_HALF_WAIT) ik_flags |= IHCP_HALF_TIMEOUT ; ik_sleeping = 0 ; wakeup(t_arg) ; /* we may call wakeup from both isr and timeout i think that is ok???????? */ return ; } /* end of ik_timeout */ static int ik_rdy_wait(reg_ptr) /* waits for device and interface ready */ struct ik_reg *reg_ptr ; /* we don't really need this - it could be global */ { int pri, cardstat ; /* 'waiting-for' flags will be set appropriately by the ioctl that calls this routine the calling routines will check the flags to determine if the return was due to ready, timeout, or signal received. */ /* test for ready here to see if we can avoid all the code below. In most cases we will already be ready when this routine is called */ if(reg_ptr->card_status & MASTER_READY) return ; pri = spl6() ; ik_sleeping = 1 ; timeout(ik_timeout,(caddr_t)&ik_sleephook,ik_rdy_time,NULL) ; reg_ptr->pulse_func = CLR_INT_FLAG ; /* make sure flag isn't left over from ?? */ reg_ptr->latch_func |= INT_ENABLE ; /* we will sleep if master_ready is not already set. if it is already set it may not have caused the int flag to set since the board's hardware only sets int flag on a ready transition that happens while the int enable bit is true. (this is left over from earlier hardware, (ours and IBM's edge interrupts) and would not be the best way to do it these days! We will also sleep if the int flag is already set, since we don't know how the hardware reacts to having an interrupt asserted while the CPU is at a priority higher than the interrupt, and having the interrupt removed before the priority is lowered. a properly designed CPU shouldn't care - but there are a few that do! */ cardstat = reg_ptr->card_status ; if(!(cardstat & MASTER_READY) || (cardstat & INTERRUPT_FLAG)) sleep((caddr_t)&ik_sleephook,(PCATCH | IK_PRI)) ; reg_ptr->latch_func &= ~INT_ENABLE ; reg_ptr->pulse_func = CLR_INT_FLAG ; splx(pri) ; untimeout(ik_timeout,(caddr_t)&ik_sleephook) ; if(ik_sleeping) /* must have gotten signal - not int or timeout */ ik_flags |= IHCP_SIG_RECEIVED ; ik_sleeping = 0 ; return ; }