/* ihcp_driver.c hp-ux driver for pci hardcopy board - model 10117 code module Ikon Corporation 2617 Western Avenue Seattle, WA USA 98121 phone: 206.728.6465 fax: 206.728.1633 www: http://www.launchsite.com/ikon */ /* 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. 10 November, 1997 initial coding strategy uses a combination of p-i/o for leading & trailing bytes, and dma for long word blocks dma chaining is supported */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ihcp_var.h" #include "ihcp_reg.h" #include "ihcp_io.h" #include /* for some reason ... this one must be last */ /* prototype our external functions */ int ihcp_open(); int ihcp_close(); int ihcp_write(); int ihcp_read(); int ihcp_ioctl(); /* prototype our internal functions */ static int ihcp_attach(); static int ihcp_strategy(); static int ihcp_wakeup(); static int ihcp_isr(); static int ihcp_timeout(); static u_int ihcp_minphys(); static u_long ihcp_getl(); static void ihcp_putl(); /* prototype internal functions that handle byte swizzling between the cpu and the pci bus */ static u_long getl(); static void putl(); /* declare dummy variables used to the left of = when we read a board register to force the write cache to flush a register write to the bus */ static volatile u_long read_dump_0; static volatile u_long read_dump_1; /* structure that tells the kernel what our externally accessible routines are */ static drv_ops_t ihcp_ops = { ihcp_open, ihcp_close, ihcp_strategy, NULL, NULL, NULL, NULL, ihcp_write, ihcp_ioctl, NULL, NULL, NULL, NULL, NULL, NULL, C_MAP_BUFFER_TO_KERNEL | C_ALLCLOSES /* need C_MAP_... since physio doesn't lock pages anymore! */ }; /* specify the driver name and class using entries suggested by G.R. at HP (this stuff is still a mystery) */ static drv_info_t ihcp_info = { "ihcp", "ihcp117", DRV_CHAR | DRV_SAVE_CONF, -1, -1, NULL, NULL, NULL }; /* more info for the wsio cdio */ static wsio_drv_data_t ihcp_data = { "ihcp117_infc", T_INTERFACE, DRV_CONVERGED, NULL, NULL }; /* pointers to previous structures */ static wsio_drv_info_t ihcp_wsio_info = { &ihcp_info, &ihcp_ops, &ihcp_data, }; extern int (*pci_attach)(); static int (*ihcp_saved_attach)(); /* install the driver */ ihcp_install() { int retval; DPRINT(("\nihcp_install: entering install\n")); /* add our attach routine to the pci attach chain the documents don't specificy the name of the pci attach chain - but it shows up in pci.h -- so we hope this is right! */ ihcp_saved_attach = pci_attach; pci_attach = ihcp_attach; /* tell the kernel that we are here for some reason, wsio_install_driver returns 1 for success */ retval = wsio_install_driver (&ihcp_wsio_info); if(retval == 0) printf("ihcp_install: wsio_install_error!\n"); else DPRINT(("ihcp_install: driver installed\n")); DPRINT(("ihcp_install: leaving install\n")); return(retval); } /* see if the passed isc structure points to our board if so, do some housekeeping */ ihcp_attach(id, isc) PCI_ID id; struct isc_table_type *isc; { int slot; /* slot # */ ihcp_unit_t *unit_p; /* points at our soft state */ u_long *plx_p; /* points at mapped plx registers */ u_long *ihcp_p; /* points at mapped ihcp registers */ u_long plx_phys_base; /* physical base address of plx registers */ u_long ihcp_phys_base; /* physical base address of ihcp registers */ u_long l_temp; u_short s_temp; u_char c_temp; int sg_list_size; /* # of bytes required for dma chain list */ caddr_t sg_buff_base; /* base of sg list space MALLOC'd */ caddr_t sg_buff_page; /* 1st page break after sg_buff_page */ struct iovec iopb_iov; /* iopb area iovec (dma list) */ struct iovec dma_iov; /* pci iovec for dma list */ io_map_t iopb_map_cb; /* for iopb mapping functions */ DPRINT(("\nihcp_attach: entering attach, vendor_id = 0x%x, device_id = 0x%x, isc = 0x%x\n", id.vendor_id, id.device_id, isc)); DPRINT(("ihcp_attach: isc->if_info->flags = 0x%x\n", ((struct wsio_if_info *)(isc->if_info))->flags)); /* We get called for each Pci board installed, regardless of whether it is a 10117 or not. We may get called multiple times with other board's isc structures. make sure that this isc points to a 10117 if not, print debug error, and call next attach on chain */ if(!((id.vendor_id == IHCP_VENDOR_ID) && (id.device_id == IHCP_DEVICE_ID))) { DPRINT(("ihcp_attach: not our device and vendor ID\n")); return ihcp_saved_attach(id,isc); } DPRINT(("ihcp_attach: IDs match, claiming board, initializing driver & board\n")); /* make sure this board isn't already initialized */ if(((struct wsio_if_info *)(isc->if_info))->flags & INITIALIZED) { DPRINT(("ihcp_attach: board already initialized\n")); return ihcp_saved_attach(id, isc); } DPRINT(("ihcp_attach: board not already initialized, proceding with attach\n")); /* get the slot number for use in debug prints -- we don't seem to have a minor number available at this point save in soft state later it is possible that the board is present, but downstream of a pci bridge - in which case the slot number may not be valid. we will flag the slot with a strange number, and try to go on */ if(pci_get_fru_info_isc(isc, &slot) != PCI_GET_FRU_INFO_EXPANSION_DEVICE) { DPRINT(("ihcp_attach: slot number not available!\n")); slot = 999; } DPRINT(("ihcp_attach: slot # = %d\n", slot)); /* allocate kernel memory for this board's soft state, and save pointer in isc, zero the allocated memory */ MALLOC(unit_p, ihcp_unit_t *, sizeof(ihcp_unit_t), M_IOSYS, M_WAITOK); isc->if_drv_data = (caddr_t)unit_p; if(unit_p == NULL) { printf("ihcp_attach: slot %d: soft state MALLOC failure!\n", slot); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id,isc); } bzero(unit_p, sizeof(ihcp_unit_t)); DPRINT(("ihcp_attach: slot %d: soft state allocated, unit_p = 0x%x\n", slot, unit_p)); /* attempt to allocate kernel memory for the io parameter blocks used by the dma chaining mechanism one iopb is used for each page or partial page of user buffer mapped for dma in strategy the iopb area must fit within a single page, and must be aligned on an IOPB_SIZE (currently 16 byte) boundary the number of iopbs is set by SG_LIST_LENGTH in ihcp_io.h the space required is IOPB_SIZE * SG_LIST_LENGTH if the requested space is larger than a single page (NBPG), we will return an error and abort the attach since we can't request specific alignments from MALLOC, we will ask for twice the necessary memory, and locate any page boundary within the requested memory. if the page boundary falls within the 1st half of the space, we will use the page boundary as the base of the iopb area. if is not within the 1st half of the area, (or there is no page boundary within the requested area) we will use the start of the requested memory as the iopb base in the latter case, we will also have to force alignment on an IOPB_SIZE boundary. */ /* make sure iopb space fits in one page */ sg_list_size = SG_LIST_LENGTH * IOPB_SIZE; DPRINT(("ihcp_attach: slot %d: SG_LIST_LENTGH = 0x%x\n", slot, SG_LIST_LENGTH)); DPRINT(("ihcp_attach: slot %d: sg_list_size = 0x%x\n", slot, sg_list_size)); if(sg_list_size > NBPG) { printf("ihcp_attach: slot %d: iopb space too big!\n", slot); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } /* ask for enough kernel memory to guarantee an iopb space with no page boundary (2*sg_list_size) */ MALLOC(sg_buff_base, caddr_t, sg_list_size * 2, M_DMA, M_WAITOK); if(sg_buff_base == NULL) { printf("ihcp_attach: slot %d: iopb MALLOC failure!\n", slot); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id,isc); } DPRINT(("ihcp_attach: slot %d: iopb space allocated, sg_buff_base = 0x%x\n", slot, sg_buff_base)); /* calculate the location of the first page break at or after the base of the iopb buffer area we just got from MALLOC */ sg_buff_page = (caddr_t)(((u_int)sg_buff_base + NBPG -1) & ~(NBPG -1)); /* if the page boundary less than base + sg_list_size, use the page boundary as the start of the list, otherwise, use the original base (the page boundary may BE the original base) */ if(sg_buff_page < (sg_buff_base + sg_list_size)) { DPRINT(("ihcp_attach: slot %d: page break in low iopb space, using page break as base\n",slot)); unit_p->iopb_base = sg_buff_page; } else { DPRINT(("ihcp_attach: slot %d: using original buffer base\n", slot)); unit_p->iopb_base = sg_buff_base; } DPRINT(("ihcp_attach: slot %d: iopb base address = 0x%x\n", slot, unit_p->iopb_base)); /* force alignment on IOPB_SIZE boundary */ unit_p->iopb_base = (caddr_t)(((u_int)unit_p->iopb_base + IOPB_SIZE -1) & ~(IOPB_SIZE -1)); DPRINT(("ihcp_attach: slot %d: iopb_base after alignment = 0x%x\n", slot, unit_p->iopb_base)); /* before calling wsio_map, you must call init_map_context which initializes the mapping context structure */ init_map_context(&iopb_map_cb); /* map iopb area into pci address space for later use by dma chain logic if wsio_map returns <0, there was an error, if >0, there was a page break in the iopb area */ iopb_iov.iov_base = unit_p->iopb_base; iopb_iov.iov_len = sg_list_size; if(l_temp = wsio_map(isc, &iopb_map_cb, IO_CONTIGUOUS, KERNELSPACE, &iopb_iov, &dma_iov)) { printf("ihcp_attach: slot %d: wsio_map (iopb) returns error!(0x%x)\n", slot, l_temp); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } /* save virtual i/o base address for use in strategy */ unit_p->iopb_phys_base_addr = dma_iov.iov_base; DPRINT(("ihcp_attach: slot %d: iopb space mapped, io address = 0x%x\n", slot, dma_iov.iov_base)); /* save slot number in soft state */ unit_p->slot = slot; /* save isc pointer for (possible) future reference */ unit_p->isc = isc; /* get the physical addresses of the plx and ihcp register spaces from the board's configuration space make sure they were assigned by system, and map into kernel space */ pci_read_cfg_uint32_isc(isc, PLX_CONFIG_BASE_ADDR, &plx_phys_base); if(plx_phys_base == 0) { printf("ihcp_attach: slot %d: plx_phys_base not assigned!\n", slot); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } unit_p->plx_mapped_base = map_mem_to_host(isc, plx_phys_base, PLX_SIZE); if(unit_p->plx_mapped_base == NULL) { printf("ihcp_attach: slot %d: can't map plx registers!\n", slot); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } DPRINT(("ihcp_attach: slot %d: plx registers mapped, phys_addr = 0x%x, mapped_addr = 0x%x\n", slot, plx_phys_base, unit_p->plx_mapped_base)); pci_read_cfg_uint32_isc(isc, IHCP_CONFIG_BASE_ADDR, &ihcp_phys_base); if(ihcp_phys_base == 0) { printf("ihcp_attach: slot %d: ihcp_phys_base not assigned!\n", slot); unmap_mem_from_host(isc, unit_p->plx_mapped_base, PLX_SIZE); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } unit_p->ihcp_mapped_base = map_mem_to_host(isc, ihcp_phys_base, IHCP_SIZE); if(unit_p->ihcp_mapped_base == NULL) { printf("ihcp_attach: slot %d: can't map ihcp registers!\n", slot); unmap_mem_from_host(isc, unit_p->plx_mapped_base, PLX_SIZE); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } DPRINT(("ihcp_attach: slot %d: ihcp registers mapped, phys_addr = 0x%x, mapped_addr = 0x%x\n", slot, ihcp_phys_base, unit_p->ihcp_mapped_base)); /* plug mapped addresses into local pointers - to use the same format that will be used in all other routines THE REGISTER OFFSETS IN ihcp_reg.h ARE BYTE OFFSETS, THEY NEED TO BE CONVERTED TO WORD OFFSETS FOR THE POINTER MATH TO WORK CORRECTLY! we use longword pointers, since all registers can be accessed as 32 bit values, and some MUST be access registers thusly: putl(reg_base_ptr, REG_OFFSET, value) getl(reg_base_ptr, REG_OFFSET) to preserve source compatibility - as much as possible - with the solaris drivers, we will use the macros: IHCP_PUTL, IHCP_GETL, IPLX_PUTL, IPLX_GETL, PLX_PUTL, PLX_GETL as defined in ihcp_reg.h to access the above functions */ plx_p = (u_long *)unit_p->plx_mapped_base; ihcp_p = (u_long *)unit_p->ihcp_mapped_base; /* ask the kernel to allocate a spinlock we would do this ourselves, but there are some alignment requirementes that we will leave to the kernel -- returns a pointer to spinlock structure THE DOCUMENTATION IS VERY WEAK re SPINLOCK ROUTINE PARAMETERS --- HOPEFULLY THIS WILL WORK */ unit_p->spinlock = alloc_spinlock(IHCP_ORDER, "ihcp_spinlock"); if((unit_p->spinlock == NULL) || (unit_p->spinlock == -1)) { printf("ihcp_attach: slot %d: couldn't allocate spinlock!\n", slot); unmap_mem_from_host(isc, unit_p->ihcp_mapped_base, IHCP_SIZE); unmap_mem_from_host(isc, unit_p->plx_mapped_base, PLX_SIZE); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } DPRINT(("ihcp_attach: slot %d: spinlock allocated\n", slot)); /* enable memory access and bus mastering */ pci_read_cfg_uint16_isc(isc, PCI_CS_COMMAND, &s_temp); pci_write_cfg_uint16_isc(isc, PCI_CS_COMMAND, s_temp | PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER); DPRINT(("ihcp_attach: slot %d: mem access and bus mastering enabled\n", slot)); /* register our interrupt handler the kernel will read the int line from the config regs isc will be passed to the isr when invoked */ if(isrlink(isc, ihcp_isr, -1, isc, 0) != 0) { printf("ihcp_attach: slot %d: isrlink failure!\n", slot); dealloc_spinlock(unit_p->spinlock); unmap_mem_from_host(isc, unit_p->ihcp_mapped_base, IHCP_SIZE); unmap_mem_from_host(isc, unit_p->plx_mapped_base, PLX_SIZE); wsio_unmap(isc, &dma_iov); FREE(sg_buff_base, M_DMA); FREE(unit_p, M_IOSYS); PCI_ATTACH_DEV_INIT_ERROR(isc); return ihcp_saved_attach(id, isc); } DPRINT(("ihcp_attach: slot %d: isr linked\n", slot)); /* everything seems OK - claim the card isc_claim returns an undefined value! */ isc_claim(isc, &ihcp_wsio_info); /* flag unit as attached */ unit_p->unit_attached = 1; DPRINT(("ihcp_attach: slot %d: device claimed, attached flag set\n", slot)); /* get and save a few values from card */ /* vendor ids and revision level */ pci_read_cfg_uint32_isc(isc, PCI_CS_VENDOR_ID, &unit_p->dev_and_vendor_id); pci_read_cfg_uint8_isc(isc, PCI_CS_REV_ID, &unit_p->revision_id); DPRINT(("ihcp_attach: slot %d: device & vendor ID = 0x%x, revision ID = 0x%x\n", slot, unit_p->dev_and_vendor_id, unit_p->revision_id)); /* print assigned interrupt level - for diag purposes */ pci_read_cfg_uint8_isc(isc, PCI_CS_INTERRUPT_LINE, &c_temp); DPRINT(("ihcp_attach: slot %d: assigned interrupt line = 0x%x\n", slot, c_temp)); /* get the board strapping and save it zeros rest of flag register at same time */ unit_p->unit_flags = INTERFACE_STRAP_MASK & IHCP_GETL(IHCP_DEVICE_STATUS); DPRINT(("ihcp_attach: slot %d: interface strapping = 0x%x\n", slot, IHCP_BOARD_MASK & unit_p->unit_flags)); /* set the data byte ordering default mode the native ordering for the board is low byte first if high byte first mode is selected, the single byte data out and command out bytes must be sent to the fifos in the HIGH byte of the longword! ordering is set here, and stored in the unit structure, since it will affect all later accesses to the 8 bit data and command registes we only take action if high_byte_first is selected, since the preceeding reset should have set us to low_byte_first mode setting high byte first mode requires turning on the byte_swizzle bit in the ihcp logic, and TURNING OFF the user output bit in the plx logic */ if(ORDER_DEF == HIGH_BYTE_FIRST) { DPRINT(("ihcp_attach: slot %d: byte order set to high byte first\n", slot)); IHCP_PUTL(IHCP_MODE, IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE); PLX_PUTL(PLX_EEPROM_USER, PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT); } unit_p->byte_order = ORDER_DEF; /* get default properties and save in soft state this code looks somewhat like the solaris code that reads the defaults from the ihcp.conf file and translates them to the appropriate bit patterns this code collects the actual default bit patterns from ihcp_io.h where they may be modified by the user */ /* save versatec speed default */ unit_p->vers_speed_def = VERS_SPEED_DEF; DPRINT(("ihcp_attach: slot %d: vers_speed_def = %d\n", slot, VERS_SPEED_DEF)); /* save centronics speed default */ unit_p->cent_speed_def = CENT_SPEED_DEF; DPRINT(("ihcp_attach: slot %d: cent_speed_def = %d\n", slot, CENT_SPEED_DEF)); /* save mode default */ unit_p->mode_def = MODE_DEF; DPRINT(("ihcp_attach: slot %d: mode_def = 0x%x\n", slot, MODE_DEF)); unit_p->dma_time_def = DMA_TIME_DEF; DPRINT(("ihcp_attach: slot %d: dma_time_def = 0x%x\n", slot, DMA_TIME_DEF)); unit_p->rdy_time_def = RDY_TIME_DEF; DPRINT(("ihcp_attach: slot %d: rdy_time_def = 0x%x\n", slot, RDY_TIME_DEF)); /* set the default handshake speeds for versatec and centronics boards (as determined by strapping), respectively, and the default print/plot mode for versatec boards the print/plot mode is also saved in the unit structure, since it may be referred to later - and isn't visible in the board's registers NOTE THAT THE SAME DEFAULTS ARE USED FOR ALL BOARDS OF THE SAME TYPE!!!!!! */ if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* versatec - set speed and print/plot mode and save a local copy in soft state */ DPRINT(("ihcp_attach: slot %d: setting default versatec speed to 0x%x\n", slot, unit_p->vers_speed_def)); DPRINT(("ihcp_attach: slot %d: setting default versatec print/plot mode to 0x%x\n", slot, unit_p->mode_def)); IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def); l_temp = SET_VERSATEC_MODE | unit_p->mode_def; if(unit_p->byte_order == HIGH_BYTE_FIRST) l_temp = l_temp << 24; IHCP_PUTL(IHCP_COMMAND_OUT, l_temp); unit_p->mode = unit_p->mode_def ; } else { /* centronics - set speed only */ DPRINT(("ihcp_attach: slot %d: setting default centronics speed to 0x%x\n", slot, unit_p->cent_speed_def)); IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def); } /* set saved data streaming mode - for old vme getregs call compatibility, since we can't read data streaming status from the logic on the other side of the fifo in the pci card */ unit_p->data_stream_mode = DATA_STREAMING_OFF; return ihcp_saved_attach(id, isc); } /* end of attach */ /* ihcp_open is called in response to the open(2) system call */ static int ihcp_open(dev, flags) dev_t dev; int flags; { int slot ; struct isc_table_type *isc; ihcp_unit_t *unit_p; DPRINT(("ihcp_open: entering open (no slot yet), dev = 0x%x, flag = 0x%x\n", dev, flags)); /* get isc pointer for this device - returns 1 for success, 0 for failure */ if(!(wsio_get_isc(dev, &isc, &ihcp_wsio_info))) { printf("ihcp_open: (no slot yet), wsio_get_isc failure!\n"); return(EIO); } /* test for null isc */ if(isc == NULL) { printf("ihcp_open: NULL isc pointer!\n") ; return(EIO) ; } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_open: NULL soft state pointer!\n"); return(EIO); } /* get slot - for ease of typing dprints */ slot = unit_p->slot; /* make sure board has been attached */ if(unit_p->unit_attached == 0) { printf("ihcp open: slot %d: open attempted before attach complete!\n", slot); return(ENXIO); } /* we are a write only device! */ if(!(flags & FWRITE)) { printf("ihcp_attach: slot %d: write only device!\n",slot); return(ENOTTY); } /* Only allow a single open */ if (unit_p->unit_open != 0) { DPRINT(("ihcp_open: slot %d: open called when already open!\n", slot)); return(EBUSY); } else { /* mark device as open */ unit_p->unit_open = 1; /* clear unit flags - but not board type */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS ; /* set default timeouts here timeout parameters are stored as ticks */ unit_p->dma_time = HZ * unit_p->dma_time_def; unit_p->fifo_time = HZ * unit_p->rdy_time_def; DPRINT(("ihcp_open: slot %d: open complete\n", slot)); } return (0); } ihcp_close(dev) dev_t dev; { int slot ; struct isc_table_type *isc; ihcp_unit_t *unit_p; /* get isc pointer for this device - returns 1 for success, 0 for failure */ if(!(wsio_get_isc(dev, &isc, &ihcp_wsio_info))) { printf("ihcp_open: (no slot yet), wsio_get_isc failure!\n"); return(EIO); } /* test for null isc */ if(isc == NULL) { printf("ihcp_close: NULL isc pointer!\n") ; return(EIO) ; } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_close: NULL soft state pointer!\n"); return(EIO); } /* get slot - for ease of typing dprints */ slot = unit_p->slot; /* clear open flag */ unit_p->unit_open = 0; DPRINT(("ihcp_close: slot %d: unit closed\n", slot)); return(0); } ihcp_ioctl(dev, cmd, arg, flags) dev_t dev; int cmd; u_long *arg; int flags; { ihcp_unit_t *unit_p; /* points at our soft state */ struct isc_table_type *isc; /* points at structure for our slot */ char *c_arg; /* Will be char pointer at arg */ u_short *s_arg; /* Short pointer to arg */ int slot; /* our slot number */ u_int command; /* command bits stripped from cmd */ u_long bits; /* local copy of *arg */ int count; /* arg size bits stripped from cmd */ u_long *plx_p; /* points at mapped plx registers */ u_long *ihcp_p; /* points at mapped ihcp registers */ int sleepval; /* return value from sleep */ int retval; /* value returned by ioctl */ u_char ct; /* temp char */ u_short st; /* temp short */ u_long lt; /* temp long */ int i; /* useful counter */ lock_t *sleeplock; /* pointer for use in sleep locking */ /* make char and shourt pointers to arg */ c_arg = (char *)arg ; s_arg = (u_short *)arg ; /* clear return value */ retval = 0; /* get isc pointer for this device - returns 1 for success, 0 for failure */ if(!(wsio_get_isc(dev, &isc, &ihcp_wsio_info))) { printf("ihcp_ioctl: (no slot yet), wsio_get_isc failure!\n"); return(EIO); } /* test for null isc */ if(isc == NULL) { printf("ihcp_ioctl: NULL isc pointer!\n") ; return(EIO) ; } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_ioctl: NULL soft state pointer!\n"); return(EIO); } /* get slot - for ease of typing dprints */ slot = unit_p->slot; DPRINT(("ihcp_ioctl: slot %d: entering ioctl, cmd = 0x%x, arg = 0x%x, *arg = 0x%x\n", slot, cmd, arg, *arg)); /* get command and argument size encoded in arg */ command = cmd & IHCPIO_CMD_MASK; count = (cmd & IHCPIO_COUNT_MASK) >> 16; /* get a local copy of *arg for local manipulation */ bits = *arg; /* get base pointers at ihcp and plx registers */ ihcp_p = unit_p->ihcp_mapped_base; plx_p = unit_p->plx_mapped_base; /* define mask used to strip the count and upper control bits from cases */ #define MASK IHCPIO_CMD_MASK switch(command) { case MASK & IHCPIO_DEV_RESET: /* send reset pulse to attached device */ DPRINT(("ihcp_ioctl: slot %d: at DEV_RESET\n", slot)); /* set and reset device clear bit, with a delay so the pulse is long enough for even slow devices we will use reads to time the pulse, and force (hopefully) the write to flush to the bus */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); read_dump_0 = IHCP_GETL(IHCP_MODE); read_dump_1 = IHCP_GETL(IHCP_REVERSE_DATA); IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE); break ; case MASK & IHCPIO_SET_CONFIG: /* set speed and if to use busy */ DPRINT(("ihcp_ioctl: slot %d: at SET_CONFIG\n", slot)); /* mask to pertinent bits only the calling program will use the old sbus speed definitions we will have to convert them to the pci card bits here */ bits &= ( IHCPIO_V_BURST | IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY | IHCPIO_SPEED3) ; /* if request is a centronics function and board is strapped for versatec - error! */ if(((bits & (IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY)) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT)) { printf("ihcp_ioctl: slot %d: at SET_CONFIG - attempt to set",slot) ; printf(" centronics mode on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* if request is a versatec only function and board is strapped for centronics - error! */ if(((bits & IHCPIO_V_BURST) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT)) { printf("ihcp_ioctl: slot %d: at SET_CONFIG - attempt to set",slot) ; printf(" versatec mode on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* shuffle bits to match new board */ ct = SPEED_MASK & (3 - ((bits & IHCPIO_SPEED3) >> 2)); if(bits & IHCPIO_BUSY_HANDSHAKE) ct |= BUSY_NOT_ACK; if(bits & IHCPIO_4_EDGE) ct |= FOUR_EDGE_HANDSHAKE; if(bits & IHCPIO_IGNORE_BUSY) ct |= IGNORE_BUSY; if(bits & IHCPIO_V_BURST) ct |= V_BURST; /* plug into mode register - being careful not to disturb other bits */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & (BYTE_SWIZZLE | REVERSE_DATA_ENB)) | ct); break ; case MASK & IHCPIO_SET_DMATIME: /* set dma timeout parameter */ DPRINT(("ihcp_ioctl: slot %d: at SET_DMATIME\n", slot)); 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_long)(bits * HZ); /* set ticks */ break ; case MASK & IHCPIO_SET_FIFOTIME: /* set timeout for fifo <1/2 full */ /* or fifo empty & dev rdy wait */ DPRINT(("ihcp_ioctl: slot %d: at SET_FIFOTIME\n", slot)); 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_long)(bits * HZ); /* ticks*/ break ; case MASK & IHCPIO_GET_REGS: /* get all the board's registers*/ DPRINT(("ihcp_ioctl: slot %d: at GET_REGS\n", slot)); *arg = unit_p->dev_and_vendor_id; /* device and vendor id */ *(arg + 1) = unit_p->revision_id; /* revision id */ *(arg + 2) = PLX_GETL(PLX_INT_CSTAT); /* interrupt control-status */ *(arg + 3) = PLX_GETL(PLX_EEPROM_USER); /* eeprom control and user bits */ *(arg + 4) = IPLX_GETL(IPLX_DMA_MODE_0); /* dma mode reg 0 */ *(arg + 5) = IPLX_GETL(IPLX_DMA_PCI_ADD_0); /* dma pci add 0 */ *(arg + 6) = IPLX_GETL(IPLX_DMA_LOC_ADD_0); /* dma local add 0 */ *(arg + 7) = IPLX_GETL(IPLX_DMA_COUNT_0); /* dma transfer count 0 */ *(arg + 8) = IPLX_GETL(IPLX_DMA_DESC_PTR_0); /* dma descriptor pointer 0 */ *(arg + 9) = IPLX_GETL(IPLX_DMA_CMD_STAT); /* dma command/status - both channels */ *(arg + 10) = IHCP_GETL(IHCP_INTERRUPT_MASK); /* ihcp interrupt mask reg */ *(arg + 11) = IHCP_GETL(IHCP_MODE); /* ihcp mode reg */ *(arg + 12) = IHCP_GETL(IHCP_DEVICE_CONTROL); /* device control reg */ *(arg + 13) = IHCP_GETL(IHCP_INTERFACE_CONTROL); /* interface control */ *(arg + 14) = IHCP_GETL(IHCP_INTERFACE_STATUS); /* interface status */ *(arg + 15) = IHCP_GETL(IHCP_DEVICE_STATUS); /* device status */ *(arg + 16) = IHCP_GETL(IHCP_REVERSE_DATA); /* reverse data in reg */ *(arg + 17) = IHCP_GETL(IHCP_AUTO_LTR_LOW); /* auto ltr count low byte */ *(arg + 18) = IHCP_GETL(IHCP_AUTO_LTR_HIGH); /* auto ltr count high byte */ break ; case MASK & IHCPIO_GET_STATUS: /* get formatted version of status*/ DPRINT(("ihcp_ioctl: slot %d: at GET_STATUS\n", slot)); /* get device status bits */ ct = IHCP_GETL(IHCP_DEVICE_STATUS) & (FAULT | ONLINE | PAPER_OUT | CENTRONICS_BUSY | VERSATEC_READY); /* shuffle status bits to match sbus format */ bits = 0; if(ct & FAULT) bits |= IHCPIO_DEV_FAULT; if(ct & ONLINE) bits |= IHCPIO_DEV_SEL; if(ct & PAPER_OUT) bits |= IHCPIO_DEV_POUT; if(ct & CENTRONICS_BUSY) bits |= IHCPIO_DEV_BUSY; if(ct & VERSATEC_READY) bits |= IHCPIO_DEV_RDY; *arg = bits; break ; case MASK & IHCPIO_GET_BOARD: /* get board type */ DPRINT(("ihcp_ioctl: slot %d: at GET_BOARD\n", slot)); *arg = IHCP_GETL(IHCP_DEVICE_STATUS) & INTERFACE_STRAP_MASK; break ; case MASK & IHCPIO_SET_VMODE: /* set versatec mode - both leave rdy ff on on pci card */ case MASK & IHCPIO_SET_VMODEX: DPRINT(("ihcp_ioctl: slot %d: at SET_VMODE\n", slot)); /* make sure the board is strapped for versatec */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at SET_VMODE - attempt to do versatec",slot) ; printf(" mode set on board strapped for centronics!\n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room we could wait for room with an interrupt, but the way the board is to be used, there should always be room */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at SET_VMODE - no room in fifo for",slot) ; printf(" versatec mode set! \n") ; retval = EIO ; goto IOCTLEXIT; } /* bottom two bits contain print/plot/spp control - same for sbus and pci command byte will be in low byte unless byte swizzling selected */ lt = SET_VERSATEC_MODE | (bits & 0x03); if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* save mode in unit structure */ unit_p->mode = bits & 0x03; break ; case MASK & IHCPIO_V_CMD: /* issue versatec pulse command */ DPRINT(("ihcp_ioctl: slot %d: at V_CMD\n", slot)); /* make sure the board is strapped for versatec - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at V_CMD - attempt to issue versatec",slot) ; printf(" command to board strapped for centronics!\n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at V_CMD - no room in fifo for",slot) ; printf(" versatec command! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* bottom two bits contain command - we have to shuffle them for compatibility with sbus driver */ if(bits == IHCPIO_VLTR) lt = VERSATEC_LINE_TERM; if(bits == IHCPIO_VEOT) lt = VERSATEC_EOT; if(bits == IHCPIO_VFED) lt = VERSATEC_FORM_FEED; if(bits == IHCPIO_VCLR) lt = VERSATEC_CLEAR; DPRINT(("ihcp_ioctl: slot %d: at V_CMD, command sent = 0x%x\n", slot, lt)); if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break ; case MASK & IHCPIO_DATA_OUT: /* output chars from arg - # of chars in bottom of cmd */ DPRINT(("ihcp_ioctl: slot %d: at DATA_OUT, byte count = 0x%x\n", slot, count)); /* make sure # of characterss is <=255 that is the max # of characters in arg array - for historical reasons we will check for room in fifo before each byte but will not wait on an interrupt if the fifo is full - we will flag an error and return each byte will be sent by writing to the 8 bit data out register this is much less effecient than writing quad bytes to the 32 bit data out register, or to the burst out range it is not the intent of this driver to use this ioctl for large data blocks, or frequent data transfers - those funtions should be served via the write call this ioctl should be used for the output of a few bytes - perhaps as a header to a write call, and should be used between write calls, or waits for fifo less than half full if this ioctl is used repeatedly, without interspersed dma transfers, which leave the fifo approximately half full, or without wait for half full ioctls the fifo may eventually overflow it would be possible to incorporate a wait for less than half full interrupt in the data out loop, but that would be overkill, considering the intended use of this ioctl */ if(count > 255) { printf("ihcp_ioctl: slot %d: at DATA_OUT - too many bytes!\n",slot) ; retval = EINVAL ; goto IOCTLEXIT ; } for(i=0; ibyte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, lt); } break ; case MASK & IHCPIO_RDY_WAIT: /* wait for rdy & fifo empty - or timeout */ DPRINT(("ihcp_ioctl: slot %d: at RDY_WAIT, wait time = %d (ticks)\n", slot, unit_p->fifo_time)); /* if already ready and fifo empty, just return first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if(IHCP_GETL(IHCP_INTERFACE_STATUS) & DEVICE_AND_INT_READY) goto IOCTLEXIT; /* set flags to indicate for what we wait */ unit_p->unit_flags |= IHCP_RDY_WAIT; /* start the software timer */ timeout(ihcp_timeout, isc, unit_p->fifo_time, NULL); /* we need a spinlock so that we don't get interrupted before we finish critical section HOWEVER - we can't hold a spinlock when we call sleep so we will also need a sleeplock sleep unlocks sleeplock */ unit_p->sleepevent = 0; spinlock(unit_p->spinlock); sleeplock = get_sleep_lock(&unit_p->sleepevent); spinunlock(unit_p->spinlock); /* make sure no left over interrupt flag, then enable plx and ihcp interrupts for device and interface ready we don't or into ihcp or plx mask reg since we know exactly which ints to enb */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, DEV_AND_INT_RDY_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE); /* snooze until ready interrupt, timeout, or signal */ DPRINT(("ihcp_ioctl: slot %d: at RDY_WAIT: calling sleep\n", slot)); sleepval = sleep(&unit_p->sleepevent, PCATCH | PSLEP); DPRINT(("ihcp_ioctl: slot %d: at RDY_WAIT: sleep returns 0x%x\n", slot, sleepval)); /* turn off the software timer */ untimeout(ihcp_timeout, isc); /* make sure interrupt enables are off and clear interrupt flag in case we got here via timeout or signal */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* find out why we woke up */ if(unit_p->unit_flags & IHCP_RDY_TIMEOUT) { /* timeout may not be error so no error message - caller can test flags for timeout we will set flag bit to stay compatible with sbus driver that had its own timeout routine */ DPRINT(("ihcp_ioctl: slot %d: at RDY_WAIT, timeout while waiting!\n", slot)); retval = EIO ; goto IOCTLEXIT ; } if(sleepval != 0) { /* signal is not error but send to console log return EINTR so caller can decide what to do */ printf("ihcp_ioctl: slot %d: at RDY_WAIT - signal while waiting for ready & fifo empty\n",slot) ; unit_p->unit_flags |= IHCP_SIG_RECEIVED; retval = EINTR ; goto IOCTLEXIT; } break ; case MASK & IHCPIO_HALF_WAIT: /* wait for fifo fifo_time)); /* if already less than half full, just return first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if(IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) goto IOCTLEXIT; /* set flags to indicate for what we wait */ unit_p->unit_flags |= IHCP_HALF_WAIT; /* start the software timer */ timeout(ihcp_timeout, isc, unit_p->fifo_time, NULL); /* we need a spinlock so that we don't get interrupted before we finish critical section HOWEVER - we can't hold a spinlock when we call sleep so we will also need a sleeplock sleep unlocks sleeplock */ unit_p->sleepevent = 0; spinlock(unit_p->spinlock); sleeplock = get_sleep_lock(&unit_p->sleepevent); spinunlock(unit_p->spinlock); /* clear any left over interrupt flag, then enable plx and ihcp interrupt for fifo less than half full we don't or into ihcp or plx mask reg since we know exactly which ints to enb */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE); /* snooze until ready interrupt, timeout, or signal */ DPRINT(("ihcp_ioctl: slot %d: at HALF_WAIT: calling sleep\n", slot)); sleepval = sleep(&unit_p->sleepevent, PCATCH | PSLEP); DPRINT(("ihcp_ioctl: slot %d: at HALF_WAIT: sleep returns 0x%x\n", slot, sleepval)); /* turn off the software timer */ untimeout(ihcp_timeout, isc); /* make sure interrupt enables are off and clear flag in case we got here via timeout or signal */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* find out why we woke up */ if(unit_p->unit_flags & IHCP_HALF_TIMEOUT) { /* timeout may not be error so no error message - caller can test flags for timeout we will set flag bit to stay compatible with sbus driver that had its own timeout routine */ DPRINT(("ihcp_ioctl: slot %d: at HALF_WAIT, timeout while waiting!\n", slot)); retval = EIO ; goto IOCTLEXIT ; } if(sleepval != 0) { /* signal is not error but send to console log return EINTR so caller can decide what to do */ printf("ihcp_ioctl: slot %d: at HALF_WAIT - signal while waiting",slot) ; printf(" for fifo not half full!\n") ; unit_p->unit_flags |= IHCP_SIG_RECEIVED; retval = EINTR ; goto IOCTLEXIT; } break ; case MASK & IHCPIO_STREAM_ON: /* set streaming mode if 10106 */ DPRINT(("ihcp_ioctl: slot %d: at STREAM_ON\n", slot)); if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at STREAM_ON - attempt to set streaming",slot) ; printf(" mode on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at STREAM_ON - no room in fifo for",slot) ; printf(" stream on command! \n") ; retval = EIO ; goto IOCTLEXIT ; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_ON; break ; case MASK & IHCPIO_STREAM_OFF: /* turn streaming off (if 10106) */ DPRINT(("ihcp_ioctl: slot %d: at STREAM_OFF\n", slot)); if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at STREAM_OFF - attempt to clear",slot) ; printf(" streaming mode on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at STREAM_OFF - no room in fifo for",slot) ; printf(" stream off command! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* set_io_mode w/ nothing else or'd in turns off streaming and auto ltr - we won't try to do streaming and auto ltr at same time in this driver */ lt = SET_IO_MODE ; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_OFF; break ; case MASK & IHCPIO_GET_FLAGS: /* get unit flag longword */ DPRINT(("ihcp_ioctl: slot %d: at GET_FLAGS\n", slot)); *arg = unit_p->unit_flags; break ; case MASK & IHCPIO_GET_FIFO: /* get formatted fifo flags */ DPRINT(("ihcp_ioctl: slot %d: at GET_FIFO\n", slot)); /* get fifo status bits and swizzle and complement to match sbus driver format */ lt = IHCP_GETL(IHCP_INTERFACE_STATUS); bits = 0; if(!(lt & FIFO_NOT_FULL)) bits |= IHCPIO_FIFO_FULL; if(!(lt & FIFO_NOT_HALF_FULL)) bits |= IHCPIO_FIFO_HALF; if(lt & FIFO_EMPTY) bits |= IHCPIO_FIFO_EMPTY; *arg = bits; break ; case MASK & IHCPIO_MASTER_CLEAR: /* master clear board - not for use by wimps! */ /* clear ihcp logic and restore all defaults */ DPRINT(("ihcp_ioctl: slot %d: at MASTER_CLEAR\n", slot)); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* make absolutely sure that dma and interrupts are off in plx chip the IPLX reference is a way to get to plx registers via ihcp space it isn't necessary any more, but is left over from earlier drivers */ PLX_PUTL(PLX_INT_CSTAT, 0); IPLX_PUTL(IPLX_DMA_CMD_STAT, 0); /* set and clear mclr bit - other bits in this register are also treated like pulses, and do not persist, so we don't need to or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* set and clear the device reset bit with two read accesses to bus to force some time between set and reset the mode reads between puts should also force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); read_dump_0 = IHCP_GETL(IHCP_MODE); read_dump_1 = IHCP_GETL(IHCP_REVERSE_DATA); IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE); /* set to default print mode, default handshake speed, and default byte ordering we don't have to check for fifo space, since we just cleared it print mode is set only if the board is strapped for versatec */ if(ORDER_DEF == HIGH_BYTE_FIRST) { IHCP_PUTL(IHCP_MODE, IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE); PLX_PUTL(PLX_EEPROM_USER, PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT); unit_p->byte_order = ORDER_DEF; } if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def); lt = SET_VERSATEC_MODE | unit_p->mode_def; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def ; } else { /* centronics - set speed only */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def); /* clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } break ; case MASK & IHCPIO_SOFT_ACK: /* software ack - looks like an ack from the dev*/ DPRINT(("ihcp_ioctl: slot %d: at SOFT_ACK\n", slot)); /* set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); break ; case MASK & IHCPIO_AUTO_LTR_COUNT: /* set byte count for auto ltr function */ DPRINT(("ihcp_ioctl: slot %d: at AUTO_LTR_COUNT, byte count = %d\n", slot, bits)); if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at AUTO_LTR_COUNT - attempt to set",slot) ; printf(" auto ltr count on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* auto ltr count is 16 bit register, accessed as two separate bytes the count register takes count minus 1 */ IHCP_PUTL(IHCP_AUTO_LTR_LOW, (bits - 1) & 0xFF); IHCP_PUTL(IHCP_AUTO_LTR_HIGH, ((bits -1) >> 8) & 0xFF); break; case MASK & IHCPIO_AUTO_LTR_ON: /* enable auto ltr operation */ DPRINT(("ihcp_ioctl: slot %d: at AUTO_LTR_ON\n", slot)); if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at AUTO_LTR_ON - attempt to set",slot) ; printf(" auto ltr mode on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at AUTO_LTR_ON - no room in fifo for",slot) ; printf(" auto ltr on command! \n") ; retval = EIO ; goto IOCTLEXIT ; } lt = SET_IO_MODE | AUTO_LTR_MODE; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break ; case MASK & IHCPIO_AUTO_LTR_OFF: /* disable auto ltr operation */ DPRINT(("ihcp_ioctl: slot %d: at AUTO_LTR_OFF\n", slot)); if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at AUTO_LTR_OFF - attempt to clear",slot) ; printf(" auto ltr mode on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at AUTO_LTR_OFF - no room in fifo for",slot) ; printf(" auto ltr off command! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* set io mode with no other bits clears auto ltr and data streaming */ lt = SET_IO_MODE; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break ; case MASK & IHCPIO_DEV_AND_VEND_ID: /* return vendor id in low 16 bits, device id in high 16 bits */ DPRINT(("ihcp_ioctl: slot %d: at DEV_AND_VEND_ID\n", slot)); *arg = unit_p->dev_and_vendor_id; break; case MASK & IHCPIO_REVISION_ID: /* return board revision id */ DPRINT(("ihcp_ioctl: slot %d: at REVISION_ID\n", slot)); *arg = unit_p->revision_id; break; case MASK & IHCPIO_LITTLE_ENDIAN: /* data will go to plotter low byte first */ DPRINT(("ihcp_ioctl: slot %d: at LITTLE_ENDIAN\n", slot)); /* low first byte ordering (the hardware default) is set by turning off the byte swizzle bit in the ihcp logic, and TURNING ON the user output bit in the plx logic] */ IHCP_PUTL(IHCP_MODE, IHCP_GETL(IHCP_MODE) & ~BYTE_SWIZZLE); PLX_PUTL(PLX_EEPROM_USER, PLX_GETL(PLX_EEPROM_USER) | USER_OUTPUT); unit_p->byte_order = LOW_BYTE_FIRST; break; case MASK & IHCPIO_BIG_ENDIAN: /* data will go to plotter high byte first */ DPRINT(("ihcp_ioctl: slot %d: at BIG_ENDIAN\n", slot)); /* turn on the ihcp byte swizzle bit and TURN OFF the plx user output bit */ IHCP_PUTL(IHCP_MODE, IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE); PLX_PUTL(PLX_EEPROM_USER, PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT); unit_p->byte_order = HIGH_BYTE_FIRST; break; case MASK & IHCPIO_DIRECT_MODE: /* direct user write of mode register - use carefully! */ DPRINT(("ihcp_ioctl: slot %d: at DIRECT_MODE\n", slot)); IHCP_PUTL(IHCP_MODE, bits); break; case MASK & IHCPIO_DEVICE_CONTROL: /* direct write to device control register */ DPRINT(("ihcp_ioctl: slot %d: at DEVICE_CONTROL\n", slot)); IHCP_PUTL(IHCP_DEVICE_CONTROL, bits); break; case MASK & IHCPIO_INTERFACE_STATUS: /* direct read of interface status register */ DPRINT(("ihcp_ioctl: slot %d: at INTERFACE_STATUS\n", slot)); *arg = IHCP_GETL(IHCP_INTERFACE_STATUS); break; case MASK & IHCPIO_DEVICE_STATUS: /* direct read of device status register */ DPRINT(("ihcp_ioctl: slot %d: at DEVICE_STATUS\n", slot)); *arg = IHCP_GETL(IHCP_DEVICE_STATUS); copyout((caddr_t)&bits, (caddr_t)arg, sizeof(u_long)); break; case MASK & IHCPIO_REVERSE_DATA: /* direct read of reverse data register - requires */ /* fiddling of tri-state bit, and others to actually */ /* do anything useful with this */ DPRINT(("ihcp_ioctl: istance %d: at REVERSE_DATA\n", slot)); *arg = IHCP_GETL(IHCP_REVERSE_DATA); break; /* ioctls for compatibility with versatec and sun drivers for VME and Sbus boards */ case MASK & LPSETVERSATEC: /* compatibility - set versatec port */ DPRINT(("ihcp_ioctl: slot %d: at LPSETVERSATEC\n", slot)); /* make sure the board is a versatec type - can't do this w/centr */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPSETVERSATEC - attempt to select",slot) ; printf(" versatec port on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } break ; case MASK & LPSETCENTRONICS: /* compatibility - set centr port */ DPRINT(("ihcp_ioctl: slot %d: at LPSETCENTRONICS\n", slot)); if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPSETCENTRONICS - attempt to select",slot) ; printf(" centronics port on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } break ; case MASK & LPCOMMAND: /* compatibility - old style cmds */ DPRINT(("ihcp_ioctl: slot %d: at LPCOMMAND\n", slot)); /* 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) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); /* de-assert long reset */ if(bits & LPC_DRST) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE); /* test for option port request - better be strapped for centronics!! */ if(bits & LPC_SOPT) if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to select",slot) ; printf(" centronics port on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* test for versatec port selection */ if(bits & LPC_SVPT) if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to select",slot) ; printf(" versatec port board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* enable data streaming - if board is strapped for centronics! */ if(bits & LPC_DSTR) { if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to set",slot) ; printf(" streaming mode on board strapped for versatec! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this command talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - no room in fifo for",slot) ; printf(" stream on command! \n") ; retval = EIO ; goto IOCTLEXIT ; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* save for posterity */ unit_p->data_stream_mode = DATA_STREAMING_ON; } /* reset data streaming bit */ if(bits & LPC_DDST) { if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to clear",slot) ; printf(" streaming mode on non-centronics unit! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - no room in fifo for",slot) ; printf(" stream off command! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* set_io_mode w/ nothing else or'd in turns off streaming and auto ltr - we won't try to do streaming and auto ltr at same time in this driver */ lt = SET_IO_MODE ; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* save streaming mode for later getregs call */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* test for versatec print/plot mode changes - make sure we are a versatec board !! */ if(bits & LPC_PRINTMASK) { if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to do",slot) ; printf(" versatec mode set on board strapped for centronics! \n") ; retval = ENOTTY ; goto IOCTLEXIT ; } /* this cmd talks to the fifo - make sure there is room !! */ if((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - no room in fifo",slot) ; printf(" for versatec mode set! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* get current mode setting */ lt = unit_p->mode ; /* now look at individual command bits and twiddle mode */ if(bits & LPC_VSPP) lt |= SPP_MODE ; /* set spp mode */ if(bits & LPC_DSPP) lt &= ~SPP_MODE ; /* clear spp */ if(bits & LPC_VPLT) lt |= PLOT_MODE ; /* set plot mode*/ if(bits & LPC_DVPT) lt &= ~PLOT_MODE ; /* clear plot */ if(bits & LPC_PRNT) lt = NORM_PRINT_MODE ; /* 0 = normal print mode */ /* save final mode - without mode set command bit - in unit structure */ unit_p->mode = lt; /* write final mode to fifo - remember to write to high byte if big-endian data mode selected */ lt |= SET_VERSATEC_MODE; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } /* check for software ack pulse request */ if(bits & LPC_SACK) { /* set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); } /* check for master clear request - the old vme board also issued a reset to the device when mclr was pulsed, so we will do that also we will also restore the default print/plot mode (if versatec), the device handshake speed, and the byte ordering */ if(bits & LPC_MCLR) { IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* make absolutely sure that dma and interrupts are off in plx chip */ PLX_PUTL(PLX_INT_CSTAT, 0); IPLX_PUTL(IPLX_DMA_CMD_STAT, 0); /* set and clear mclr bit - other bits in this register are also treated like pulses, and do not persist, so we don't need to or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* set and clear the device reset bit, with reads between set and reset to give some length to pulse the mode read between puts should force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); read_dump_0 = IHCP_GETL(IHCP_MODE); read_dump_1 = IHCP_GETL(IHCP_REVERSE_DATA); IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE); /* set to default print mode, default handshake speed, and default byte ordering we don't have to check for fifo space, since we just cleared it print mode is set only if the board is strapped for versatec */ if(ORDER_DEF == HIGH_BYTE_FIRST) { IHCP_PUTL(IHCP_MODE, IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE); PLX_PUTL(PLX_EEPROM_USER, PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT); unit_p->byte_order = ORDER_DEF; } if((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def); lt = SET_VERSATEC_MODE | unit_p->mode_def; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def ; } else { /* centronics - set speed only */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def); } /* clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* 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) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - attempt to do",slot) ; printf(" versatec pulsed command to board strapped for centronics! \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((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { printf("ihcp_ioctl: slot %d: at LPCOMMAND - no room in fifo",slot) ; printf(" for versatec pulsed command! \n") ; retval = EIO ; goto IOCTLEXIT ; } /* shuffle vme style commands to match pci board */ if(bits & LPC_VLTR) lt = VERSATEC_LINE_TERM; if(bits & LPC_VEOT) lt = VERSATEC_EOT; if(bits & LPC_VTFF) lt = VERSATEC_FORM_FEED; if(bits & LPC_VCLR) lt = VERSATEC_CLEAR; if(unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } break ; case MASK & LPGETREGS: /* old style (VMEbus) get regs command */ DPRINT(("ihcp_ioctl: slot %d: at LPGETREGS\n", slot)); /* start work on interface status register */ st = 0; /* get interface status register and fiddle interface and device ready bits */ ct = (u_char)IHCP_GETL(IHCP_INTERFACE_STATUS); if(ct & DEVICE_AND_INT_READY) st |= OLD_DIRY; if(ct & DEVICE_READY) st |= OLD_DVRY; /* get remembered data streaming mode */ if(unit_p->data_stream_mode == DATA_STREAMING_ON) st |= OLD_DSTR; /* get board type from unit struct if centronics, flag as old style option port */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) st |= OLD_SOPT; /* put result in top of long temp */ lt = (u_long)(st << 16) ; /* 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 register and shuffle bit positions and polarities */ ct = (u_char)IHCP_GETL(IHCP_DEVICE_STATUS); if(ct & VERSATEC_READY) st |= OLD_OACK; if(!(ct & CENTRONICS_BUSY)) st |= OLD_ONBY; if(!(ct & PAPER_OUT)) st |= OLD_OPPR; if(!(ct & ONLINE)) st |= OLD_ONSL; if(ct & FAULT) st |= OLD_OFLT; /* look for device reset latch */ if(IHCP_GETL(IHCP_INTERFACE_CONTROL) & RESET_DEVICE) st |= OLD_OLRS ; /* long reset */ } else { /* work on versatec part */ if((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_VERS_TTL) st |= OLD_VTTL; /* get device status and twiddle bits */ ct = (u_char)IHCP_GETL(IHCP_DEVICE_STATUS); if(ct & VERSATEC_READY) st |= OLD_VRDY ; /* vers ready*/ if(ct & PAPER_OUT) st |= OLD_VPPR ; /* paper ok */ if(ct & ONLINE) st |= OLD_VONL ; /* vers onlin*/ /* get current mode from unit structure */ ct = unit_p->mode ; if(ct & SPP_MODE) st |= OLD_VSPP ; /* spp mode */ if(ct & PLOT_MODE) st |= OLD_VPLT ; /* plot mode */ } lt |= st ; /* save in bottom of long temp */ *arg = lt; break; case MASK & LPSETTIMVAL: /* set timeout value - old style - for dma and fifo empty */ DPRINT(("ihcp_ioctl: slot %d: at LPSETTIMVAL\n", slot)); /* 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 ; unit_p->fifo_time = (u_long)((bits * HZ)/50) ; unit_p->dma_time = (u_long)((bits * HZ)/50) ; break; case MASK & LPGETTIMVAL: /* get current timeout (in ticks ?) */ DPRINT(("ihcp_ioctl: slot %d: at LPGETTIMVAL\n", slot)); /* return # of fiftieths of a second */ *arg = (unit_p->dma_time/HZ) * 50 ; 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: /* we could have just done return(retval) locally, instead of coming here, but if we need a spinlock, or something else global to the ioctl code, it will be nice to have one place to deal with it */ return (retval); } ihcp_write(dev, uiop) dev_t dev; struct uio *uiop; { ihcp_unit_t *unit_p; /* points at our soft state */ struct isc_table_type *isc; /* points at structure for our slot */ int slot; /* our slot number */ int retval; /* write return value (from strategy) */ /* get isc pointer for this device - returns 1 for success, 0 for failure */ if(!(wsio_get_isc(dev, &isc, &ihcp_wsio_info))) { printf("ihcp_write: (no slot yet), wsio_get_isc failure!\n"); return(EIO); } /* test for null isc */ if(isc == NULL) { printf("ihcp_write: NULL isc pointer!\n") ; return(EIO) ; } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_write: NULL soft state pointer!\n"); return(EIO); } /* get slot - for ease of typing dprints */ slot = unit_p->slot; DPRINT(("ihcp_write: slot %d: entering write\n", slot)); /* 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 bufferr from pool use our local minphys routine to limit xfer size as restricted by the scatter-gather list length */ retval = physio(ihcp_strategy,(struct buf *)NULL, dev, B_WRITE, ihcp_minphys, uiop) ; /* check flags for timeout error or signal received */ if(unit_p->unit_flags & IHCP_DMA_TIMEOUT) { retval = EIO ; printf("ihcp_write: slot %d: DMA timeout! \n",slot); } if(unit_p->unit_flags & IHCP_SIG_RECEIVED) { retval = EINTR ; printf("ihcp_write: slot %d: signal received! \n",slot); } DPRINT(("ihcp_write: slot %d: leaving write routine\n", slot)); /* reset uio_offset so we don't "seek" past the end of the iov counter */ uiop->uio_offset = 0; return (retval); } ihcp_strategy(bp) struct buf *bp; { struct isc_table_type *isc; int slot; ihcp_unit_t *unit_p; /* points at our soft state */ u_long *plx_p; /* points at mapped plx registers */ u_long *ihcp_p; /* points at mapped ihcp registers */ u_long *iopb_p; /* points at iopb memory */ int rem_count; /* count of bytes left to transfer */ io_map_t map_cb; /* for mapping functions */ caddr_t user_addr; /* user buff start address */ int user_count; /* user buff size */ caddr_t dma_addr; /* local copy of buf start add */ int dma_count; /* local copy of buf size */ int count; /* useful counter */ int index; /* useful index */ int sleepval; /* value returned by sleep */ int iopb_number; /* number of pci iovecs used */ u_int temp; /* handy temporary variable */ lock_t *sleeplock; /* pointer for use in sleep locking */ struct iovec host_iov; /* user buffer iovec */ struct iovec pci_iov_array[SG_LIST_LENGTH]; /* array of pci iovecs */ DPRINT(("ihcp_strategy: (no slot yet): entering strategy,\n")); /* get isc pointer for this device - returns 1 for success, 0 for failure */ if(!(wsio_get_isc(bp->b_dev, &isc, &ihcp_wsio_info))) { printf("ihcp_strategy: (no slot yet), wsio_get_isc failure!\n"); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto EXIT_STRATEGY; } /* test for null isc */ if(isc == NULL) { printf("ihcp_strategy: NULL isc pointer!\n") ; bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto EXIT_STRATEGY; } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_strategy: NULL soft state pointer!\n"); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto EXIT_STRATEGY; } /* get slot - for ease of typing dprints */ slot = unit_p->slot; DPRINT(("ihcp_strategy: slot %d: isc = 0x%x, unit_p = 0x%x\n", slot, isc, unit_p)); /* now that we have the soft state poiner, we can grab our spinlock AFTER THIS POINT, WE MUST BE VERY CAREFUL WHERE WE GO IF AN ERROR WE MUST UNLOCK THE SPINLOCK */ DPRINT(("ihcp_strategy: slot %d: grabbing spinlock\n", slot)); unit_p->sleepevent = 0; spinlock(unit_p->spinlock); DPRINT(("ihcp_strategy: slot %d: bp = 0x%x, buff add = 0x%x, buff size = 0x%x\n", slot, bp, bp->b_un.b_addr, bp->b_bcount)); /* get pointers at ikon registers, plx registers, and iopb space these pointers are used by macros that swizzle bytes (required by the way HP handles big-little endian issues */ ihcp_p = unit_p->ihcp_mapped_base; plx_p = unit_p->plx_mapped_base; iopb_p = unit_p->iopb_base; /* save buffer pointer in unit structure for possible error returns from interrupt routine */ unit_p->buf_p = bp ; /* we will do a sleep within strategy, rather than returning directly to physio and waiting for iodone there this allows us to check status after each transfer, and determine whether it completed successfully, or terminated with a timeout or signal we will check for alignment and/or trailing bytes before beginning block transfers - this is necessary since once commited to wsio_map calls, we can't modify the host and pci iovs, so we have to start with an aligned, 0 mod 4 buffer! we must also be certain to check for fifo less than 1/2 full to protect against a series of write calls with small buffers that do not cause dma transfers eventually overflowing the fifo if no dma is required, we will do a test for fifo less than half full, and wait for an interrupt if necessary p-i/o transfers will be used to handle "odd" bytes -- the dma mechanism requires quad byte aligned buffers, of complete quad byte words */ /* make local copies of the buffer add and size */ user_addr = bp->b_un.b_addr; user_count = bp->b_bcount; /* set flags to indicate dma wait - even if we don't wait, just so flags will reflect that we were in strategy error flags are not cleared here so they will survive multiple calls to strategy by physio */ unit_p->unit_flags |= IHCP_DMA_WAIT; /* calculate number of any leading modify addr and count to show only full quad bytes in buffer to be mapped limit count to actual buffer size!!!! */ count = 0x03 & (4 - (0x03 & (u_int)user_addr)); if(count > dma_count) count = user_count; DPRINT(("ihcp_strategy: slot %d: number of initial unaligned bytes = 0x%x\n", slot, count)); /* output any leading bytes dma address will be incremented to indicate new start add for dma, and dma count will be decremented */ for(index=0; indexb_error |= EIO; bp->b_flags |= B_ERROR; printf("ihcp_strategy: slot %d: fifo full while transferring leading bytes!\n",slot) ; goto UNLOCK_STRATEGY ; } temp = *user_addr; if(unit_p->byte_order == HIGH_BYTE_FIRST) temp = temp << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp); } /* we should now have an aligned buffer, with 0 or more bytes in it if 4 or more bytes, do a dma xfer if less than 4, wait for 3) { /* since we have dealt with any leading bytes, we know we have to do at least one dma block - so we can proceed with mapping */ DPRINT(("ihcp_strategy: slot %d: dma required, dma buff address = 0x%x, total size = 0x%x\n", slot, user_addr, user_count)); /* sync the user buffer for dma */ DPRINT(("ihcp_strategy: slot %d: syncing user buffer for dma\n", slot)); dma_sync(bp->b_spaddr, host_iov.iov_base, host_iov.iov_len, IO_SYNC_FORDEV); /* seed host iov with modified buffer add and count -- these will be updated by wsio_map during each pass through the page output logic we will modify dma_addr and dma_count during each pass based on what we find in the mapped iov */ host_iov.iov_base = user_addr; host_iov.iov_len = user_count & 0xffffffc; /* before calling wsio_map, we must call init_map_context which initializes the mapping context structure */ init_map_context(&map_cb); /* continue to loop on buffer pages as long as data remains to be transferred - that is >3 aligned bytes count the number of pci iovecs used AND make sure we don't exceed the available number ov iovecs */ do { DPRINT(("ihcp_strategy: slot %d: processing iopb #0x%x\n", slot, iopb_number)); /* make sure the caller isn't asking to map more pages than we can handle if so - umap the ones we have already mapped */ if(iopb_number >= SG_LIST_LENGTH) { printf("ihcp_strategy: slot %d: too many io parameter blocks!\n", slot); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto UNMAP_STRATEGY; } /* wsio_map returns greater than zero if the mapping crosses a page boundary and there additional bytes to map */ rem_count = wsio_map(isc, &map_cb, IO_IGN_ALIGNMENT, bp->b_spaddr, &host_iov, &pci_iov_array[iopb_number]); if(rem_count < 0) { printf("ihcp_strategy: slot %d: wsio_map returns error!\n", slot); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto UNMAP_STRATEGY; } DPRINT(("ihcp_strategy: slot %d: iopb #0x%x mapped, pci address = 0x%x, size = 0x%x\n", slot, iopb_number, pci_iov_array[iopb_number].iov_base, pci_iov_array[iopb_number].iov_len)); /* save base address and size of this iopb in convenient local variables */ dma_addr = pci_iov_array[iopb_number].iov_base; dma_count = pci_iov_array[iopb_number].iov_len; DPRINT(("ihcp_strategy: slot %d: assembling chain link, pci add = 0x%x, count = 0x%x\n", slot, dma_addr, dma_count)); /* assemble the chain link */ /* WE NEED TO FIND OUT WHAT THE DMA BYTE ORDERING AND BYTE LANE SITUATION IS --- IT AFFECTS THE IOPBs, SINCE THEY ARE EXTRACTED WITH 32 BIT DMA READS if we use plx byte swapping, that affects the iopbs if we use ikon swizzling, it doesn't affect the iopbs USE IOPB_X MACROS EVEN IF THEY RESOLVE TO SIMPLE ACCESS FOR SIMILARITY WITH SOLARIS, AND TO EASE LATER MODIFICATION */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_PCI_ADDRESS, dma_addr); /* pci user buf segment address */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_TRANSFER_SIZE, dma_count & 0xFFFFFFFC); /* buf segment size */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_LOCAL_ADDRESS, IHCP_32_BIT_DATA_OUT); /* data destination */ /* the next descriptor pointer element of the iopb includes the location bit (pci or local memory), the end of chain bit, the interrupt after terminal count bit (not used), the direction of transfer bit (0 = output to device), and the physical location of the next iopb if the remaining byte count is less than 4, this is the last link flag it as the last in the list, and don't set a "next iopb" address */ if(rem_count > 3) { DPRINT(("ihcp_strategy: slot %d: not last iopb, writing pointer to next iopb\n", slot)); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | DMA_NEXT_ADDRESS_MASK & (unit_p->iopb_phys_base_addr + ((iopb_number + 1) * IOPB_SIZE))); } else { DPRINT(("ihcp_strategy: slot %d: last iopb, setting end of chain bit\n", slot)); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | DMA_END_OF_CHAIN); } /* reduce total buffer size by each bufflet count (masked) and bump virtual address by masked size */ user_addr += (dma_count & 0xFFFFFFFC); user_count -= (dma_count & 0xFFFFFFFC); DPRINT(("ihcp_strategy: slot %d: done with iopb number 0x%x, new user add = 0x%x, new user count = 0x%x\n", slot, iopb_number, user_addr, user_count)); iopb_number++; } while(rem_count > 3); /* we know that we have at least one iopb to sync for device sync only as much space as needed */ DPRINT(("ihcp_strategy: slot %d: syncing iopb memory, iopb_count = 0x%x, size sync'd = 0x%x\n", slot, iopb_number, iopb_number * IOPB_SIZE)); dma_sync(KERNELSPACE, unit_p->iopb_base, iopb_number * IOPB_SIZE, IO_SYNC_FORDEV); /* program plx dma logic on board - in chaining mode make sure that any left over dma done interrupt is cleared USE PLX_DMA_ENABLE, NOT DMA_ENABLE...THE LATTER COLLIDED WITH AN HP-UX DEFINITION USED IN A MACRO */ IPLX_PUTL(IPLX_DMA_CMD_STAT, DMA_CLEAR_INTERRUPT); IPLX_PUTL(IPLX_DMA_MODE_0, DMA_BUS_32_BIT | DMA_WAIT_1 | DMA_BURST_ENABLE | DMA_CHAIN_ENABLE | DMA_DONE_INTERRUPT_ENB | DMA_LOCAL_ADD_HOLD | DMA_DEMAND_MODE); /* mode register */ IPLX_PUTL(IPLX_DMA_DESC_PTR_0, DMA_CHAIN_IN_PCI_MEM | unit_p->iopb_phys_base_addr); /* output, list in pci mem, list pointer */ IPLX_PUTL(IPLX_DMA_CMD_STAT, PLX_DMA_ENABLE | DMA_START); /* actually fire up xfer*/ /* the 9060 drives the local interrupt out true when a dma interrupt happens we have to loop that output back to the local interrupt input external to the chip to get a dma interrupt to the pci bus */ PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE | LOCAL_INT_OUT_ENABLE | LOCAL_DMA_0_INT_ENABLE); } else { DPRINT(("ihcp_strategy: slot %d: no dma, waiting for <1/2 full\n", slot)); /* we need to wait for fifo less than half full it may already be less than half full - if so sneak around the wait code */ if(IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) goto NOWAIT_STRATEGY; DPRINT(("ihcp_strategy: slot %d: not <1/2 full, setting up for interrupt\n", slot)); /* clear any left over interrupt flag, then enable plx and ihcp interrupt for fifo less than half full we don't or into ihcp or plx mask reg since we know exactly which ints to enb */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); } /* we arrive here after setting up for a dma wait, or a less than half full wait */ /* start the software timer */ timeout(ihcp_timeout, isc, unit_p->dma_time, NULL); /* we already have a spinlock so that we won't get interrupted before we finish critical section HOWEVER - we can't hold a spinlock when we call sleep so we will also need a sleeplock sleep unlocks sleeplock */ sleeplock = get_sleep_lock(&unit_p->sleepevent); spinunlock(unit_p->spinlock); /* snooze until dma interrupt, <1/2 full interrupt, timeout, or signal */ DPRINT(("ihcp_strategy: slot %d: calling sleep\n", slot)); sleepval = sleep(&unit_p->sleepevent, PCATCH | PSLEP); DPRINT(("ihcp_strategy: slot %d: sleep returns 0x%x\n", slot, sleepval)); /* re-grab spinlock for later unlocking (we always unlock, even if we didn't do dma or wait for 1/2 full interrupt */ spinlock(unit_p->spinlock); /* turn off the software timer */ untimeout(ihcp_timeout, isc); /* make sure interrupt enables are off and pause dma (which will normally already be done) */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); /* if dma is not done (after sig or timeout), force an abort and issue a master clear to flush ihcp buffers abort may take some time to complete, so use delay and register read to give it some time */ if(!(IPLX_GETL(IPLX_DMA_CMD_STAT) & DMA_DONE)) { DPRINT(("ihcp_strategy: slot %d: dma not done after cv_timedwait_sig\n", slot)); IPLX_PUTL(IPLX_DMA_CMD_STAT, DMA_ABORT); /* get mode - so we can restore it after a mclr mclr clears the fifos, and also makes them assert dma rq but...mclr holds off dma rq while mclr is asserted, and since the 9060 likes a synchronous dma rq, we will wait a while before hitting mclr WE NEED A WAY TO DELAY A FEW MICROSECONDS HERE it is not clear whether the 9060 needs dma rq in order to finish an abort if it does, it may transfer a word or two after the mclr, so we will repeat the mclr later */ temp = IHCP_GETL(IHCP_MODE); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* force write cache to flush - and delay with reads */ read_dump_0 = IHCP_GETL(IHCP_INTERRUPT_MASK); read_dump_1 = IHCP_GETL(IHCP_DEVICE_CONTROL); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_MODE, temp); /* make sure dma actually says done */ if(!(IPLX_GETL(IPLX_DMA_CMD_STAT) & DMA_DONE)) { printf("ihcp_strategy: slot %d: dma not done after abort!\n", slot); bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto UNMAP_STRATEGY ; } } /* now we can safely make sure that the done interrupt is clear, and reset the ihcp interrupt flag (the above is probably not necessary after normal dma done) */ IPLX_PUTL(IPLX_DMA_CMD_STAT, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* find out why we woke up */ if(unit_p->unit_flags & IHCP_DMA_TIMEOUT) { /* timeout is always an error so print message and set error flag bits flags for timeout */ printf("ihcp_strategy: slot %d: timeout while waiting for interrupt!\n", slot); unit_p->unit_flags |= IHCP_DMA_TIMEOUT; bp->b_error |= EIO; bp->b_flags |= B_ERROR; goto UNMAP_STRATEGY ; } if(sleepval != 0) { /* flag signal received and print error message */ printf("ihcp_strategy: slot %d: signal received while waiting for interrupt\n",slot) ; unit_p->unit_flags |= IHCP_SIG_RECEIVED; bp->b_error |= EINTR; bp->b_flags |= B_ERROR; goto UNMAP_STRATEGY ; } UNMAP_STRATEGY: /* unmap the iovecs used in the dma transfer(s) IF ANY!!!!!!! */ DPRINT(("ihcp_strategy: slot %d: at UNMAP_STRATEGY: unmapping user buffer\n", slot)); for(index = 0; index < iopb_number; index++) wsio_unmap(isc, &pci_iov_array[index]); NOWAIT_STRATEGY: /* come here if no dma or wait for <1/2 full interrupt required, or in the normal flow of things */ /* now we need to test for trailing bytes that didn't fit into exact number of long words it is possible to get here with remaining byte count == 0, either after dma or if leading byte output logic exhausted the byte count user_count should show the remaining bytes - if any, and user_addr address should point to the next byte to output */ DPRINT(("ihcp_strategy: slot %d: at NOWAIT_STRATEGY\n", slot)); DPRINT(("ihcp_strategy: slot %d: number of trailing bytes = 0x%x, buffer address = 0x%x\n", slot, user_count, user_addr)); for(index=0; indexb_error |= EIO; bp->b_flags |= B_ERROR; printf("ihcp_strategy: slot %d: fifo full while transferring trailing bytes!\n", slot) ; goto UNLOCK_STRATEGY ; } temp = *user_addr; if(unit_p->byte_order == HIGH_BYTE_FIRST) temp = temp << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp); } UNLOCK_STRATEGY: /* come here if error after we have grabbed spinlock, or in the normal flow of events */ DPRINT(("ihcp_strategy: slot %d: at UNLOCK_STRATEGY: releasing spinlock\n", slot)); spinunlock(unit_p->spinlock); EXIT_STRATEGY: /* come here if error before any the mappings are done or in normal flow if error - be sure to do the appropriate unmapping(s) before arriving here */ /* we can't read back the range counter in all versions of the plx chip so we will just set resid to 0! since we have a fifo with an unknown quantity of data in it (after an error), we can't recover from a partial transfer anyway! */ DPRINT(("ihcp_strategy: slot %d: at EXIT_STRATEGY: setting resid to 0, calling iodone\n", slot)); bp->b_resid = 0; /* tell physio that we are done */ iodone(bp) ; DPRINT(("ihcp_strategy: slot %d: leaving strategy\n", slot)); } /* routine that catches an ioctl interrupt or dma complete interrupt */ int ihcp_isr(isc) struct isc_table_type *isc; /* points at structure for our slot */ { ihcp_unit_t *unit_p; /* points at our soft state */ u_long *plx_p; /* points at mapped plx registers */ u_long *ihcp_p; /* points at mapped ihcp registers */ u_long temp; int int_serviced = 0; /* 1 says we handled interrutp */ int slot; /* board slot */ lock_t *sleeplock; /* pointer for use in sleep locking */ DPRINT(("ihcp_isr: (no slot yet): entering isr\n")); /* test for null isc */ if(isc == NULL) { printf("ihcp_isr: NULL isc pointer!\n"); return(-1); } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_isr: NULL soft state pointer!\n"); return(-1); } /* get base pointers at ihcp and plx registers */ ihcp_p = unit_p->ihcp_mapped_base; plx_p = unit_p->plx_mapped_base; /* get slot - for ease of typing dprints */ slot = unit_p->slot; DPRINT(("ihcp_isr: slot %d: isc = 0x%x, unit_p = 0x%x\n", slot, isc, unit_p)); /* get spinlock */ spinlock(unit_p->spinlock); temp = IPLX_GETL(IPLX_INT_CSTAT); if((temp & PCI_INTERRUPT_ENABLE) && ((temp & LOCAL_INTERRUPT) || (temp & DMA_0_INTERRUPT))) { DPRINT(("ihcp_isr: slot %d: claiming interupt\n", slot)); /* tell the kernel that we accept the interrupt */ int_serviced = 1; /* force off dma enable, interrupt enables, and ihcp interrupt mask bits which shoule release the interrupt request then (after masks are off) toggle reset int flag bit on & off to clear interrupt flag (which is a latch) and do the same to the dma done interrupt */ IPLX_PUTL(IPLX_DMA_CMD_STAT, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); IPLX_PUTL(IPLX_DMA_CMD_STAT, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* do register reads to get the previous writes to flush to the pci bus so the writes that clear the interrupt will be complete before we exit - we will hope that the optimizer doesn't delete these! */ read_dump_0 = PLX_GETL(PLX_EEPROM_USER); read_dump_1 = IHCP_GETL(IHCP_MODE); /* in the sbus handler, we did a drv_usecwait here to give the interrupt line time to rise the _GETL will take some time to complete since the plx chip is fairly slow for single reads, so there should be plenty of time for the interrupt line to rise */ /* go through the spinlock gyrations to wake up strategy or the ioctl code waiting on this interrupt the documentation isn't too clear about locks within interrupt code */ unit_p->sleepevent = 1; sleeplock = get_sleep_lock(&unit_p->sleepevent); wakeup(&unit_p->sleepevent) ; spinunlock(sleeplock); } /* release the spinlock, and tell the kernel if we fielded the int */ DPRINT(("ihcp_isr: slot %d: leaving isr\n", slot)); spinunlock(unit_p->spinlock); return(int_serviced); } /* routine called if software timer expires during wait for dma completion, or wait for ioctl condition */ int ihcp_timeout(isc) struct isc_table_type *isc; /* points at structure for our slot */ { ihcp_unit_t *unit_p; /* points at our soft state */ u_long *plx_p; /* points at mapped plx registers */ u_long *ihcp_p; /* points at mapped ihcp registers */ int slot; /* our board's slot */ lock_t *sleeplock; /* pointer for use in sleep locking */ /* test for null isc */ if(isc == NULL) { printf("ihcp_timeout: NULL isc pointer!\n"); return(-1); } /* get pointer at soft state - make sure it isn't null */ unit_p = (ihcp_unit_t *)isc->if_drv_data; if(unit_p == NULL) { printf("ihcp_timeout: NULL soft state pointer!\n"); return(-1); } /* get base pointers at ihcp and plx registers */ ihcp_p = unit_p->ihcp_mapped_base; plx_p = unit_p->plx_mapped_base; /* get slot - for ease of typing dprints */ slot = unit_p->slot; DPRINT(("ihcp_timeout: slot %d: entering timeout,\n", slot)); /* force interrupts and dma off */ IPLX_PUTL(IPLX_DMA_CMD_STAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); /* get spinlock */ spinlock(unit_p->spinlock); /* set appropriate flag - done for compatibility w/ other drivers */ if(unit_p->unit_flags & IHCP_DMA_WAIT) unit_p->unit_flags |= IHCP_DMA_TIMEOUT ; if(unit_p->unit_flags & IHCP_RDY_WAIT) unit_p->unit_flags |= IHCP_RDY_TIMEOUT ; if(unit_p->unit_flags & IHCP_HALF_WAIT) unit_p->unit_flags |= IHCP_HALF_TIMEOUT ; /* wake up sleeping strategy or ioctl code using rather elaborate sleeplock stuff */ unit_p->sleepevent = 1; sleeplock = get_sleep_lock(&unit_p->sleepevent); wakeup(&unit_p->sleepevent) ; spinunlock(sleeplock); spinunlock(unit_p->spinlock); return(0); } /* internal function that limits the size of the transfer used in each call to strategy to the max that our scatter-gather list length allows */ static u_int ihcp_minphys(bp) struct buf *bp; { u_long max_xfer; max_xfer = (SG_LIST_LENGTH - 1) * NBPG; if(bp->b_bcount > max_xfer) { DPRINT(("ihcp_minphys: slot ?: limiting xfer size to 0x%x\n", max_xfer)); bp->b_bcount = max_xfer; } else { DPRINT(("ihcp_minphys: slot ?: using original xfer size\n")); } } /* internal function that gets a 32 bit value from one of our register sets on the pci bus, and swizzles the bytes to match the cpu's format */ static u_long ihcp_getl(pointer, offset) u_long *pointer; u_long offset; { u_long temp; u_long result; temp = *(pointer + (offset >> 2)); result = ((temp >> 24) & 0x000000ff) |((temp >> 8) & 0x0000ff00) |((temp << 8) & 0x00ff0000) |((temp << 24) & 0xff000000); return (result); } /* internal function that takes a value, swizzles the bytes, and writes it to one of our register sets */ static void ihcp_putl(pointer, offset, value) u_long *pointer; u_long offset; u_long value; { *(pointer + (offset >> 2)) = ((value >> 24) & 0x000000ff) |((value >> 8) & 0x0000ff00) |((value << 8) & 0x00ff0000) |((value << 24) & 0xff000000); }