/* * Copyright (c) 1990 by Sun Microsystems, Inc. * @(#)bpp.c 1.1 94/10/31 SMI * * Source code for the bidirectional parallel port * driver for the Zebra SBus card. * */ /* * These switches are needed to provide full source code compatibility * for both the modload (loadable) case and the config (linked in) case. * Also, some header files do not include some kernel information unless * KERNEL is defined. */ #ifndef KERNEL /* predefined in the config case*/ # define KERNEL #else KERNEL # define LINKED_IN /* prevent errors in make depend*/ #endif KERNEL /* #includes below */ /* #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bpp_io.h" #include "bpp_reg.h" #include "bpp_var.h" /* structure definitions below */ static struct bpp_transfer_parms bpp_default_transfer_parms = { BPP_ACK_BUSY_HS, /* read_handshake */ 1000, /* read_setup_time - 1 us */ 1000, /* read_strobe_width - 1 us */ 60, /* read_timeout - 1 minute */ BPP_ACK_HS, /* write_handshake */ 1000, /* write_setup_time - 1 us */ 1000, /* write_strobe_width - 1 us */ 60, /* write_timeout - 1 minute */ }; static struct bpp_pins bpp_default_pins = { 0, /* output pins */ 0, /* input pins */ }; static struct bpp_error_status bpp_default_error_stat = { 0, /* no timeout */ 0, /* no bus error */ 0, /* no pin status set */ }; #ifdef NO_BPP_HW /* buffer to simulate hardware */ static u_char bpp_hard_buf[FAKE_BUF_SIZE]; /* hardware mimicing registers */ static struct bpp_regs bpp_fake_regs[MAX_NBPP]; /* default register values */ static struct bpp_regs bpp_def_regs; #endif NO_BPP_HW /* static variable declarations below */ /* array of pointers to unit structs */ static struct bpp_unit *bpp_units[MAX_NBPP]; static u_char nbpp = 0; /* number of units in system */ static int sys_clock = 0; /* system clock freq in MHz */ static int clock_cycle = 0; /* system clock period in nsec */ static int sbus_clock = 0; /* sbus clock freq prop in MHz */ static int sbus_cycle = 0; /* sbus clock prop period in nsec */ static int sbus_clock_cycle = 0; /* actual sbus clock period in nsec */ extern int hz; /* clock ticks per second */ extern struct map *dvmamap; /* pointer to DVMA resource map */ /* Declarations for bpp_fakeout hardware simulation support */ static u_int sim_byte_count = 0; /* bytes desired to be transferred */ /* - this will be set by bpp_ioctl() */ /* for testing error conditions */ static int err_code = 0; /* error code for simulating */ /* hardware errors */ static enum trans_type { /* state variable for last transfer */ read_trans, write_trans } last_trans; /* * bpp_printlevel controls what level of messages seen * on the console. LOG_DEBUG is the most verbose. * Do not set bpp_printlevel below LOG_CRIT or you * will not even see error messages from the bpp driver. * bpp_print depends on these levels being legal values to pass * to the kernel log() function. * These are level defs from , and are included * here for reference. * * int bpp_printlevel = LOG_ERR; error conditions * int bpp_printlevel = LOG_WARNING; warning conditions * int bpp_printlevel = LOG_NOTICE; normal but significant condition * int bpp_printlevel = LOG_INFO; informational * int bpp_printlevel = LOG_DEBUG; debug-level messages */ #ifdef BPP_DEBUG int bpp_printlevel = LOG_DEBUG; /* debug-level messages */ #else BPP_DEBUG int bpp_printlevel = LOG_ERR; /* error conditions only */ #endif BPP_DEBUG /* private procedure declarations below */ /* Autoconfig Declarations */ static int bpp_identify(); static int bpp_attach(); #ifdef notdef int bpp_init(); /* bpp_init MUST be global or modload will fail */ #endif notdef /* Driver function Declarations */ /* * bpp_open, bpp_close, bpp_read, bpp_write and * bpp_ioctl MUST be global to build this driver into a kernel */ int bpp_open(); int bpp_close(); int bpp_read(); int bpp_write(); int bpp_ioctl(); static int bpp_poll(); static void bpp_intr(); static bpp_strategy(); static void bpp_minphys(); /* Utility Function Declarations */ static bpp_transfer_timeout(); static void see_devinfo(); static char * get_property(); static int get_num_property(); static int check_bpp_registers(); static void set_dss_dsw(); static u_short check_write_params(); static u_short check_read_params(); static u_short check_read_pins(); static u_short check_write_pins(); static void read_outpins(); #ifdef notdef static void bpp_decommission(); #endif notdef static void start_critical(); static void end_critical(); static int bpp_transfer_failed(); #ifdef NO_BPP_HW static void bpp_fakeout(); static void bpp_stuff_fregs(); #endif NO_BPP_HW static void bpp_print_regs(); static void bpp_print(); caddr_t kmem_zalloc(); /* * The bpp_ops struct enables the kernel to find the * bpp_identify and bpp_attach routines. Note * that the name of this structure is REQUIRED to be "_ops" * if the driver is to be configured into the kernel. */ struct dev_ops bpp_ops = { 1, /* revision number */ bpp_identify, /* confirm device ID */ bpp_attach, /* attach routine of driver */ }; #ifdef notdef /* * Character device entry points for this driver. * These are in cdevsw[] in sun/conf.c for preconfigured drivers. */ struct cdevsw bpp_cdevsw = { bpp_open, bpp_close, bpp_read, bpp_write, bpp_ioctl, nulldev, seltrue, 0, 0 }; /* * The bpp_drv structure provides the linkage between the vd driver * (for loadable drivers) and the dev_ops structure for this driver * (bpp_ops). */ struct vdldrv bpp_drv = { VDMAGIC_DRV, /* Drv_magic */ #ifndef BPP_DEBUG "bpp 1.1 @(#) 1.1 94/10/31", /* *Drv_name */ #else BPP_DEBUG "bpp 1.1 Debug @(#) 1.1 94/10/31", /* *Drv_name */ #endif BPP_DEBUG &bpp_ops, /* *Drv_dev_ops */ NULL, /* *Drv_bdevsw */ &bpp_cdevsw, /* *Drv_cdevsw */ 0, /* Drv_blockmajor */ 0 /* Drv_charmajor */ }; #endif notdef /* Autoconfig Support Functions */ /* * bpp_identify() * Determine whether this is the right driver to work with the named device. * Count the number of times a match occurs - this is the number of * devices actually in the system. */ static int bpp_identify(name) char *name; { bpp_print(LOG_INFO, "Entering bpp_identify, name: %s\n", name); if (strcmp(name, BPP_PROM_NAME) == 0) { /* * Sanity check, perhaps paranoid. Limit the number of * units that we accept. Any not accepted cannot be * attached, and therefore cannot be serviced. */ if (nbpp < MAX_NBPP) { nbpp++; bpp_print(LOG_INFO, "Leaving bpp_identify, ACCEPTED (#%d)\n", nbpp); return (1); /* Good match; device accepted. */ } else { bpp_print(LOG_ERR, "bpp_identify: too many bpps (%d).\n", nbpp); } } bpp_print(LOG_INFO, "Leaving bpp_identify, (REJECTED)\n"); return (0); /* No match or over limit; the device rejected. */ } /* * bpp_attach() * * Map the bpp device registers into kernel virtual memory. * Add the bpp driver to the level 2 interrupt chain. * Initialize the bpp portion of the zebra card. * Turn on the interrupts. * Allocate unit structures based on the number of units (nbpp). */ static int bpp_attach(devinfo_p) register struct dev_info *devinfo_p; { struct dev_reg *dev_reg_p; struct dev_intr *dev_intr_p; u_int counter; int myslot; u_int unit_no; /* attaching unit's number */ int cpu_frequency; /* system clock frequency (in cycles) */ int sbus_frequency; /* sbus clock frequency (in cycles) */ int interrupt_level; /* real interrupt level used */ int i; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ struct bpp_regs *bpp_regs_p; static int unit_count = 0; /* * The static variable unit_count * is the source of unit numbers to * assign to the devinfo struct. * It is the responsibility of the * attach routine to fill in the value * of devinfo_p->devi_unit. */ bpp_print(LOG_INFO, "Entering bpp_attach: devinfo_p: 0x%x\n", devinfo_p); /* Initialize bpp_units struct before allocating any unit structs */ bpp_print(LOG_INFO, "Initializing units array.\n"); if (unit_count == 0) { /* first time attach is called */ for (i = 0; i < MAX_NBPP; i++) bpp_units[i] = NULL; } if (devinfo_p == NULL) { /* * Make the attach fail. A physical device should * have been found by the prom, and therefore have a * devinfo pointer. The code below requires it. * This is a "can't happen". */ bpp_print(LOG_ERR, "bpp_attach: devinfo_p is NULL!\n"); goto attach_failed; } #ifdef notdef OLD METHOD /* * Allocate the bpp_units array based on the number of * units (nbpp) counted by bpp_identify. * The unit structs are allocated as zeroed memory. */ if (bpp_units == NULL) { /* not allocated yet - 1st time */ bpp_units = (struct bpp_unit *)kmem_zalloc(nbpp * sizeof (struct bpp_unit)); } #endif notdef if (bpp_units[unit_count] != NULL) { /* Massive error - unit_no is out of sync? */ bpp_print(LOG_ERR, "bpp_attch: unit %d struct pointer not NULL!\n", unit_count); goto attach_failed; } /* * Allocate a unit structure for this unit. * Each bpp_unit struct is allocated as zeroed memory. * Store away its address for future use. */ bpp_print(LOG_INFO, "Allocating unit struct for unit %d.\n", unit_count); bpp_units[unit_count] = (struct bpp_unit *)kmem_zalloc(sizeof (struct bpp_unit)); /* * Initialize the unit structures. The unit structure for * each unit is initialized when bpp_attach is called for that unit. */ /* * Assign a unit structure and fill in the value of * devinfo_p->devi_unit. */ unit_no = unit_count++; devinfo_p->devi_unit = unit_no; /* Make sure we're not in a slave-only slot */ if ((myslot = slaveslot(devinfo_p->devi_reg->reg_addr)) >= 0) { bpp_print(LOG_ERR, "bpp unit %d: NOT used - SBus slot %d is slave only.\n", unit_no, myslot); goto attach_failed; } /* assign a pointer to this unit's state struct */ bpp_p = bpp_units[unit_no]; /* * Store the devinfo pointer in the unit structure. We will need * it for decommissioning. Initialize the saved spl in the * unit structure for assertion checking for critical sections. * Initialize the pointer to the transfer parameters structure * for this unit. */ bpp_p->devinfo_p = devinfo_p; bpp_p->saved_spl = (-1); /* * Initialize the transfer parameters structure for this unit. */ bpp_p->transfer_parms = bpp_default_transfer_parms; /* * Initialize the control pins structure for this unit. */ bpp_p->pins = bpp_default_pins; /* * Initialize the error status structure for this unit. * Initialize the timeout status byte for this unit. */ bpp_p->error_stat = bpp_default_error_stat; bpp_p->timeouts = NO_TIMEOUTS; bpp_print(LOG_INFO, "Timeout block is 0x%x.\n", bpp_p->timeouts); /* clear the buf struct before using it */ bzero ((char*)&(bpp_p->buf), sizeof (struct buf)); /* * Look at the properties in the devinfo struct. Demonstrate * how to get other properties by getting the value of the * "name" property the hard way. */ #ifdef DEBUG (void) see_devinfo(devinfo_p); #endif DEBUG (void) get_property(devinfo_p, "name", "NO NAME PROPERTY?"); /* * Check that the clock frequency in the boot prom is in * a sensible range. If it isn't, the math used when setting * the transfer parameters strobe and width times will * fail. Flag the future problem here rather than in the ioctl. */ /* * Sparcstation and its clones only have a clock-frequency * property associated with the top_devinfo node. This * means that the system clock and sbus clock are the * same frequency. Calvin runs the sbus at a slower * speed than the cpu. Try to find a clock-frequency * property in my parent (sbus) node. If none there, * then try the cpu node (top_devinfo). */ sbus_frequency = get_num_property(devinfo_p->devi_parent, "clock-frequency", 1000000); sbus_clock = sbus_frequency/1000000; if (sbus_clock >= 10 && sbus_clock <= 25) { bpp_print(LOG_INFO, "Sbus clock is %d MHz.\n", sbus_clock); /* calculate clock period (in nsec) */ sbus_cycle = (1000 / sbus_clock); sbus_clock_cycle = sbus_cycle; } else { bpp_print(LOG_WARNING, "Sbus frequency out of range.\n"); /* try for cpu clock frequency instead */ cpu_frequency = get_num_property(top_devinfo, "clock-frequency", 1000000); sys_clock = cpu_frequency/1000000; if (sys_clock >= 10 && sys_clock <= 25) { bpp_print(LOG_WARNING, "Using CPU clock property for SBus.\n"); bpp_print(LOG_INFO, "Sbus clock is %d MHz.\n", sys_clock); /* calculate clock period (in nsec) */ clock_cycle = (1000 / sys_clock); sbus_clock_cycle = clock_cycle; } else { bpp_print(LOG_ERR, "Warning! System Clock freq out of range!\n"); goto attach_failed; } } bpp_print(LOG_INFO, "Sbus Clock period is %d nsec.\n", sbus_clock_cycle); /* * Map in any device registers. The zebra parallel section * has only one register area. I'm assuming devinfo_p->nreg = 1. */ dev_reg_p = devinfo_p->devi_reg; /* * dev_reg_p may be NULL, but only if devi_nreg is zero. */ if (dev_reg_p == NULL) { /* * A "can't happen". */ bpp_print(LOG_ERR, "bpp_attach: dev_reg_p is NULL!\n"); goto attach_failed; /*NOTREACHED*/ } #ifndef NO_BPP_HW /* The "real" hardware case */ /* * Map the structure into kernel virtual space. */ bpp_p->bpp_regs_p = (struct bpp_regs *) map_regs(dev_reg_p->reg_addr, sizeof (struct bpp_regs), dev_reg_p->reg_bustype); bpp_regs_p = bpp_p->bpp_regs_p; bpp_print(LOG_INFO, "After mapping registers, int_cntl = 0x%x\n", bpp_regs_p->int_cntl); /* * Check that the size of the register set is the same as the * size of the structure that overlays it. */ if (sizeof (struct bpp_regs) != dev_reg_p->reg_size) { bpp_print(LOG_ERR, "bpp_attach, unit %d, structure size mismatch!\n", unit_no); bpp_print(LOG_ERR, "Struct (%d bytes) != reg (%d bytes)!\n", sizeof (struct bpp_regs), dev_reg_p->reg_size); goto attach_failed; } #else NO_BPP_HW /* use allocated memory instead of reg_addr if no real hardware */ /* The "faked" hardware case */ /* bustype SPO_VIRTUAL */ /* * Map the (fake) structure into kernel virtual space. */ bpp_p->bpp_regs_p = (struct bpp_regs *) map_regs(&bpp_fake_regs[unit_no], sizeof (struct bpp_regs), SPO_VIRTUAL); bpp_print(LOG_INFO, "Mapped fake registers: %x mapped to %x\n", &bpp_fake_regs[unit_no], bpp_p->bpp_regs_p); /* stuff initial vals into fake registers */ bpp_stuff_fregs(unit_no); #endif NO_BPP_HW if (bpp_p->bpp_regs_p == NULL) { bpp_print(LOG_ERR, "bpp_attach unit %d: map_regs failed!\n", unit_no); goto attach_failed; } if (check_bpp_registers(unit_no)) { /* registers don't seem right */ bpp_print(LOG_ERR, "bpp_attach unit %d: register check failed!\n", unit_no); goto attach_failed; } /* * For devices that issue interrupts, the driver must install * itself on the interrupt chain for each level that the hardware * can interrupt at. * This must be done before expecting to receive any interrupts. */ dev_intr_p = devinfo_p->devi_intr; /* * dev_intr_p may be NULL, but only if devi_nintr is zero. */ for (counter = devinfo_p->devi_nintr; counter > 0; --counter, ++dev_intr_p) { if (dev_intr_p == NULL) { /* * A "can't happen". */ bpp_print(LOG_ERR, "bpp_attach: dev_intr_p is NULL!\n"); goto attach_failed; /*NOTREACHED*/ break; } /* * Convert the hardware interrupt priority level (0-15) * into a psr for critical sections of code. If there * is more than 1 interrupt level, use the highest one. * Note that the units may differ. */ interrupt_level = dev_intr_p->int_pri; if (ipltospl(interrupt_level) > bpp_p->interrupt_pri) { bpp_p->interrupt_pri = ipltospl(interrupt_level); bpp_print(LOG_INFO, "Set interrupt_pri to 0x%x for unit %d.\n", bpp_p->interrupt_pri, unit_no); } /* * If the device can do dma, then the dma routines need * to know the highest priority that the device may * interrupt at. The routine adddma is used to gather * this information. */ adddma(interrupt_level); #ifndef NO_BPP_HW start_critical(unit_no); /* * Addintr() does the registration. Duplicate * registrations (such as from several units) are * ignored except for data collection for vmstat. * Addintr() panics if this registration would make * too many devices on the interrupt chain. Sorry. */ (void) addintr(interrupt_level, bpp_poll, devinfo_p->devi_name, devinfo_p->devi_unit); end_critical(unit_no); bpp_print(LOG_INFO, "Installed bpp_poll: unit %d at SBus priority 0x%x\n", unit_no, interrupt_level); #endif NO_BPP_HW } /* * The routine report_dev() writes a line on the console and system * log file to announce the attachment of the driver. This will * happen either at boot or modload time, one line for each unit. * It is nice to call report_dev before contacting the device * if possible so that if something horrible happens there is * a record of what was being attempted. */ report_dev(devinfo_p); /* * Perform device initialization. */ bpp_regs_p->dma_csr |= BPP_RESET_BPP; bpp_regs_p->dma_csr &= ~BPP_RESET_BPP; bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE; /* * Set up the polarities for the ERR, SLCT PE, and BUSY interrupts. * Changing the polarities could cause a stray interrupt, * so clear them here. * These polarities are handshake dependent. * This setup corresponds to the default handshakes. */ bpp_print(LOG_INFO, "Before setting polarities, int_cntl = 0x%x\n", bpp_regs_p->int_cntl); bpp_regs_p->int_cntl |= BPP_ERR_IRP; /* ERR rising edge */ bpp_regs_p->int_cntl |= BPP_SLCT_IRP; /* SLCT rising edge */ /* SLCT+ means off-line */ bpp_regs_p->int_cntl &= ~BPP_PE_IRP; /* PE falling edge */ bpp_regs_p->int_cntl |= BPP_BUSY_IRP; /* BUSY rising edge */ /* clear any stray interrupts */ bpp_regs_p->int_cntl |= BPP_ALL_IRQS; bpp_print(LOG_INFO, "After setting polarities, int_cntl = 0x%x\n", bpp_regs_p->int_cntl); /* Last of all, turn on interrupts */ bpp_p->bpp_regs_p->dma_csr |= BPP_INT_EN; if (bpp_p->bpp_regs_p->op_config & BPP_VERSATEC_INTERLOCK) { /* versatec connector absent */ /* block versatec handshake modes */ bpp_p->versatec_allowed = 0; bpp_print(LOG_INFO, "Versatec connector absent.\n"); } else { /* allow versatec handshake modes */ bpp_p->versatec_allowed = 1; bpp_print(LOG_INFO, "Versatec connector present.\n"); } bpp_print_regs (unit_no, "At end of attach:\n"); bpp_print(LOG_INFO, "Leaving bpp_attach: unit %d\n", unit_no); bpp_print(LOG_INFO, "ATTACH SUCCEEDED.\n"); return (0); attach_failed: bpp_print(LOG_INFO, "ATTACH FAILED.\n"); bpp_p->open_inhibit = 1; /* prevent open */ return (-1); } /* * bpp_init -- Support for a loadable device driver. * Required by the utilities modload modstat & modunload. * The VDLOAD case is called (indirectly) from modload. * * The VDUNLOAD case is called (indirectly) from modunload. * * The VDSTAT case is called (indirectly) from modstat. * */ #ifdef notdef int bpp_init(command, vdp, vdi, vds) int command; /* Identify why module called */ struct vddrv *vdp; /* Pointer to kernel's structure*/ struct vdioctl_load *vdi; /* pointer to struct vdioctl_* */ struct vdstat *vds; /* ID and status information */ { struct vdconf *vdconf_p; char *string_p; u_int bpp_unit_no; int status = 0; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ bpp_print(LOG_INFO, "-----------------(%d)--------------- \n", vdp->vdd_id); bpp_print(LOG_INFO, "Entering bpp_init, vdp 0x%x, vdi 0x%x, vds 0x%x.\n", vdp, vdi, vds); switch (command) { case VDLOAD: bpp_print(LOG_DEBUG, "Command is VDLOAD.\n"); /* * For the driver of an SBus device there are generally * only two things to do in the VDLOAD step. The first is * to get any available configuration information that may * have been passed in from a modload configuration file. * The second is to fill in the linkage pointer to the * vdldrv structure. Most initialization of the driver or * it's devices must wait until the driver's attach() * routine is called because this is when (1) the count * of physical devices is known as all calls to the * driver's identify() routine have taken place, and * (2) the dev_info pointer and therefore the devices * themselves become available. */ /* * Get the configuration info from the vd driver if any * was passed in from modload. Some of this code cannot * be useful to the bpp driver. It is included for * illustration. */ if (vdi != NULL) { bpp_print(LOG_DEBUG, "vdi_id: %d\n", vdi->vdi_id); bpp_print(LOG_DEBUG, "vdi_status: %d\n", vdi->vdi_status); bpp_print(LOG_DEBUG, "vdi_mmapaddr: 0x%x\n", vdi->vdi_mmapaddr); bpp_print(LOG_DEBUG, "vdi_symtabsize: %d\n", vdi->vdi_symtabsize); bpp_print(LOG_DEBUG, "vdi_userconf: 0x%x\n", vdi->vdi_userconf); if (vdi->vdi_userconf != NULL) { vdconf_p = vdi->vdi_userconf; if (vdconf_p->vdc_type == VDCEND) { bpp_print(LOG_DEBUG, "No config info available.\n"); } while (vdconf_p->vdc_type != VDCEND) { switch (vdconf_p->vdc_type) { case VDCCONTROLLER: string_p = "(VDCCONTROLLER)"; break; case VDCDEVICE: string_p = "(VDCDEVICE)"; break; case VDCBLOCKMAJOR: string_p = "(VDCBLOCKMAJOR)"; break; case VDCCHARMAJOR: string_p = "(VDCCHARMAJOR)"; break; case VDCSYSCALLNUM: string_p = "(VDCSYSCALLNUM)"; break; default: bpp_print(LOG_INFO, "vdconf switch error!\n"); string_p = "(SWITCH ERROR!)"; break; } bpp_print(LOG_DEBUG, "vdc_type: %d %s %s 0x%x\n", vdconf_p->vdc_type, string_p, "vdc_data:", vdconf_p->vdc_data); /* Move to next element of array */ vdconf_p++; } } else { bpp_print(LOG_INFO, "No config info, vdi_userconf is NULL\n"); } } else { bpp_print(LOG_INFO, "No config info, vdi is NULL\n"); } /* * Configure the bpp driver from the available * information. If no information was passed in from * modload, then use the default info from this file. */ /* * Fill in pointer to the vdldrv structure, * which must be already initialized at this point. */ vdp->vdd_vdtab = (struct vdlinkage *)&bpp_drv; bpp_print(LOG_DEBUG, "Done VDLOAD.\n"); break; case VDUNLOAD: bpp_print(LOG_DEBUG, "Command is VDUNLOAD.\n"); /* * Check if the driver is in a condition to allow safe * unloading. If it is, then cleanly shut the driver down, * clean up, and release all dynamically allocated space. * BE SURE to remove the driver from any interrupt chains * that it is on. */ for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++) { bpp_p = bpp_units[bpp_unit_no]; /* * Inhibit further opens on the unit. */ bpp_p->open_inhibit = 1; /* * If any any unit is open, cannot unload. */ if (bpp_p->unit_open != 0) { status = EBUSY; } /* * should check that the DMA engine * is idle before allowing unloading. */ } /* * If it looks good so far, turn off interrupts, remove * registration of interrupt vector, release all memory, * and permit unload. */ if (status == 0) { (void) bpp_decommission(); } else { /* * Refuse unload, return to business as usual. */ for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++) { bpp_p = bpp_units[bpp_unit_no]; /* * Release the open inhibits. */ bpp_p->open_inhibit = 0; } } bpp_print(LOG_DEBUG, "Done VDUNLOAD.\n"); break; case VDSTAT: bpp_print(LOG_DEBUG, "Command is VDSTAT.\n"); /* DEVICE SPECIFIC */ bpp_print(LOG_DEBUG, "Done VDSTAT.\n"); break; default: bpp_print(LOG_ERR, "bpp_init: unknown command 0x%x\n", command); status = EINVAL; break; } bpp_print(LOG_INFO, "Leaving bpp_init, status (errno): %d.\n", status); return (status); } /* * Turn off interrupts, remove registration of all interrupt vectors, * and release all memory. Note that the info for the interrupt levels * came from the Fcode prom on each device, and therefore may vary from * unit to unit. Be sure to remove all interrupt vectors, or the kernel * will bus error if the device interrupts while the driver is unloaded. * */ static void bpp_decommission() { u_int bpp_unit_no; int i; int counter; struct dev_intr *dev_intr_p; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ struct bpp_regs *bpp_regs_p; bpp_print(LOG_INFO, "Entering bpp_decommission.\n"); /* * Turn off interrupts for each unit. */ for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++) { bpp_p = bpp_units[bpp_unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) { /* was transferring */ bpp_print(LOG_ERR, "ERROR: bpp unload of unit %d while DMA active!\n", bpp_unit_no); /* turn off DMA and byte count */ bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA; bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT; /* Reset PP state machine */ bpp_regs_p->op_config |= BPP_SRST; bpp_regs_p->op_config &= ~BPP_SRST; /* flush the cache */ bpp_regs_p->dma_csr |= BPP_FLUSH; } /* * Disable the TC interrupts. * Mask the error interrupts too. * These shouldn't be on if we weren't transferring * at the time, but it's safest to just turn * them off anyway. */ bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE; bpp_regs_p->int_cntl &= ~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN); /* ALL FIELDS CLEARED, idle state */ /* * To be safer, I really should free the buf which * was being used to do the transfer, and wait on * a semaphore that tells me that bpp_read or * bpp_write have returned the partial error. */ } /* * Everything that we need to know is in the unit structures. * From there we access the devinfo struct to find the interrupt * levels that we are (might be) installed on. Since duplicate * registrations with addintr() are ignored, duplicate * unregistrations can not be considered an error. * March through and do everything meticulously. * Do not take short cuts or you die. */ for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++) { bpp_p = bpp_units[bpp_unit_no]; start_critical(bpp_unit_no); dev_intr_p = bpp_p->devinfo_p->devi_intr; /* * dev_intr_p may be NULL, but only if devi_nintr is zero. */ for (counter = bpp_p->devinfo_p->devi_nintr; counter > 0; --counter, ++dev_intr_p) { if (dev_intr_p == NULL) { /* * A "can't happen". */ bpp_print(LOG_ERR, "bpp_decommission ASSERT FAILED.\n"); break; } /* * In the multiple-unit case, * on the first remintr really removes the * interrupt routine from the poll chain. * All subsequent attempts will generate * a message. This is normal. */ #ifdef VDDRV (void) remintr(dev_intr_p->int_pri, bpp_poll); #endif VDDRV } end_critical(bpp_unit_no); } #ifdef notdef OLD METHOD /* * Release the memory allocated for the unit structures. */ if ((bpp_units != NULL) && (nbpp > 0)) { (void) kmem_free ((caddr_t)bpp_units, (u_long)(nbpp * sizeof (struct bpp_unit))); bpp_units = NULL; } #endif notdef /* * Release the memory allocated for the unit structures. */ if ((bpp_units[0] != NULL) && (nbpp > 0)) { for (i = 0; i < nbpp; i++) { (void) kmem_free ((caddr_t)bpp_units[i], (u_int)(sizeof (struct bpp_unit))); bpp_units[i] = NULL; } } bpp_print(LOG_INFO, "Leaving bpp_decommission.\n"); return; } #endif notdef /* Normal Device Driver routines */ /* * Open the device. */ int bpp_open(dev, openflags) dev_t dev; int openflags; { u_char unit_no; u_short retval = 0; /* return value (errno) for system call */ register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ unit_no = BPP_UNIT(dev); bpp_p = bpp_units[unit_no]; bpp_print(LOG_INFO, "bpp%d: Entering bpp_open, flags %d.\n", unit_no, openflags); /* * Check for how many are allocated. At this point * nbpp must be set. */ if (unit_no >= nbpp) { retval = ENXIO; goto out; } /* * Check for allocation of unit structures. */ if (bpp_units[unit_no] == NULL) { retval = ENXIO; /* attach failed ?? */ goto out; } start_critical(unit_no); /* * Only allow a single open. If this device has * already been opened, return an error. */ if (bpp_p->unit_open != 0) { bpp_print(LOG_NOTICE, "bpp%d already opened.\n", unit_no); retval = EBUSY; goto out; } /* * Check if the open mode requested is permissible. */ if (bpp_p->open_inhibit != 0) { bpp_print(LOG_NOTICE, "bpp%d open inhibit.\n", unit_no); retval = ENXIO; goto out; } /* * Mark the bpp as opened. */ bpp_p->unit_open = 1; end_critical(unit_no); /* * Initialize the transfer parameters structure * and initialize the control pins structure * for this unit. */ bpp_p->transfer_parms = bpp_default_transfer_parms; bpp_p->pins = bpp_default_pins; bpp_p->openflags = openflags; /* record the open mode */ out: bpp_print(LOG_INFO, "Leaving bpp_open, unit %d: errno %d.\n", unit_no, retval); return (retval); } /* * Close the device. */ /*ARGSUSED*/ int bpp_close(dev, openflags) dev_t dev; int openflags; { u_char unit_no; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ unit_no = BPP_UNIT(dev); bpp_p = bpp_units[unit_no]; bpp_print(LOG_INFO, "Entering bpp_close, unit number %d.\n", unit_no); /* * Implement the close. */ bpp_print(LOG_INFO, "In bpp_close, Timeout block is 0x%x.\n", bpp_p->timeouts); if (bpp_p->timeouts) { /* any timeouts pending? */ bpp_print(LOG_INFO, "Some timeouts still pending.\n"); if (bpp_p->timeouts & TRANSFER_TIMEOUT) { bpp_print(LOG_INFO, "Clearing transfer timeout.\n"); untimeout(bpp_transfer_timeout, (caddr_t)unit_no); } if (bpp_p->timeouts & FAKEOUT_TIMEOUT) { #ifdef NO_BPP_HW bpp_print(LOG_INFO, "Clearing fakeout timeout.\n"); untimeout(bpp_fakeout, (caddr_t) 0); #else NO_BPP_HW bpp_print(LOG_WARNING, "BOGUS fakeout timeout.\n"); #endif NO_BPP_HW } } bpp_p->timeouts = NO_TIMEOUTS; bpp_print(LOG_INFO, "At end of bpp_close, Timeout block is 0x%x.\n", bpp_p->timeouts); /* * Mark unit closed. */ bpp_p->unit_open = 0; bpp_print(LOG_INFO, "Leaving bpp_close, unit %d:\n", unit_no); return (0); } /* * Read system call. */ /*ARGSUSED*/ int bpp_read(dev, uio) dev_t dev; struct uio *uio; { u_char unit_no; u_short retval = 0; /* return value (errno) for system call */ struct bpp_regs *bpp_regs_p; struct bpp_transfer_parms *bpp_transfer_parms_p; /* time to allow the scanner to change from data sink to source */ static int scan_turnaround = 1000; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ unit_no = BPP_UNIT(dev); bpp_p = bpp_units[unit_no]; bpp_print(LOG_INFO, "Entering bpp_read, unit number %d.\n", unit_no); bpp_print_regs(unit_no, "in bpp_read"); bpp_regs_p = bpp_p->bpp_regs_p; bpp_transfer_parms_p = &bpp_p->transfer_parms; /* * delay to allow for scanning write/read turnaround */ if (last_trans == write_trans) { DELAY(scan_turnaround); } /* * Set the handshake bits */ start_critical(unit_no); /* * make sure the memory clear operation is turned off */ bpp_regs_p->op_config &= ~BPP_EN_MEM_CLR; switch (bpp_transfer_parms_p->read_handshake) { case BPP_NO_HS: bpp_print(LOG_INFO, "BPP_NO_HS case\n"); bpp_regs_p->op_config &= ~(BPP_ACK_OP | BPP_BUSY_OP); bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_BUSY_BIDIR); break; case BPP_ACK_HS: bpp_print(LOG_INFO, "BPP_ACK_HS case\n"); bpp_regs_p->op_config &= ~BPP_BUSY_OP; bpp_regs_p->op_config |= BPP_ACK_OP; bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_ACK_BIDIR | BPP_BUSY_BIDIR); break; case BPP_BUSY_HS: case BPP_HSCAN_HS: bpp_print(LOG_INFO, "BPP_BUSY_HS case\n"); bpp_regs_p->op_config |= BPP_BUSY_OP; bpp_regs_p->op_config &= ~BPP_ACK_OP; bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_BUSY_BIDIR); break; case BPP_ACK_BUSY_HS: bpp_print(LOG_INFO, "BPP_ACK_BUSY_HS case\n"); bpp_regs_p->op_config |= (BPP_BUSY_OP | BPP_ACK_OP); bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_ACK_BIDIR | BPP_BUSY_BIDIR); break; case BPP_XSCAN_HS: /* *reads with the Xerox use ACK handshake * and unidirectional operation */ bpp_print(LOG_INFO, "BPP_XSCAN_HS case\n"); bpp_regs_p->op_config &= ~BPP_BUSY_OP; bpp_regs_p->op_config |= BPP_ACK_OP; bpp_regs_p->op_config &= ~(BPP_DS_BIDIR | BPP_BUSY_BIDIR | BPP_ACK_BIDIR); break; case BPP_CLEAR_MEM: bpp_print(LOG_INFO, "BPP_CLEAR_MEM case\n"); bpp_regs_p->op_config &= ~BPP_DMA_DATA; bpp_regs_p->op_config |= BPP_EN_MEM_CLR; break; case BPP_SET_MEM: bpp_print(LOG_INFO, "BPP_SET_MEM case\n"); bpp_regs_p->op_config |= BPP_DMA_DATA; bpp_regs_p->op_config |= BPP_EN_MEM_CLR; break; } /* * The direction should not be marked until after the handshake * bits have been set. */ bpp_regs_p->trans_cntl |= BPP_DIRECTION; end_critical(unit_no); /* set the dss and dsw values */ set_dss_dsw(unit_no, 1); /* * If we're opened for read/write, * toggle the scan/print line for scanning */ if ((bpp_p->openflags & FREAD) && (bpp_p->openflags & FWRITE)) { start_critical(unit_no); if (bpp_transfer_parms_p->read_handshake == BPP_HSCAN_HS) { /* The HP Scanjet uses AFX */ bpp_regs_p->out_pins |= BPP_AFX_PIN; } else { bpp_regs_p->out_pins |= BPP_SLCTIN_PIN; } end_critical(unit_no); } bpp_print_regs(unit_no, "after switch, before physio:"); retval = physio(bpp_strategy, &bpp_p->buf, dev, B_READ, bpp_minphys, uio); bpp_print(LOG_INFO, "Leaving bpp_read, unit %d: errno %d.\n", unit_no, retval); last_trans = read_trans; /* set last transfer type */ return (retval); } /* * Write data to the bpp. Block until the write is finished. */ int bpp_write(dev, uio) dev_t dev; struct uio *uio; { u_char unit_no; u_short retval = 0; /* return value (errno) for system call */ register struct bpp_regs *bpp_regs_p; register struct bpp_transfer_parms *bpp_transfer_parms_p; register struct bpp_error_status *bpp_errorstat_p; /* error stat */ register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ unit_no = BPP_UNIT(dev); bpp_print(LOG_INFO, "Entering bpp_write, unit number %d.\n", unit_no); bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_transfer_parms_p = &bpp_p->transfer_parms; bpp_errorstat_p = &bpp_p->error_stat; /* clear any old error status */ *bpp_errorstat_p = bpp_default_error_stat; /* * Set up the polarities for the ERR, SLCT PE, and BUSY interrupts. * Changing the polarities could cause a stray interrupt, * so clear them here. * These polarities are handshake dependent. * This setup corresponds to the default handshakes. */ bpp_print(LOG_INFO, "Before setting polarities, int_cntl = 0x%x\n", bpp_regs_p->int_cntl); start_critical(unit_no); bpp_regs_p->int_cntl |= BPP_ERR_IRP; /* ERR rising edge */ bpp_regs_p->int_cntl |= BPP_SLCT_IRP; /* SLCT rising edge */ /* SLCT+ is off-line */ bpp_regs_p->int_cntl &= ~BPP_PE_IRP; /* PE falling edge */ bpp_regs_p->int_cntl |= (BPP_ERR_IRQ | BPP_SLCT_IRQ | BPP_PE_IRQ); end_critical(unit_no); bpp_print(LOG_INFO, "After setting polarities, int_cntl = 0x%x\n", bpp_regs_p->int_cntl); start_critical(unit_no); check_for_active_pins(unit_no); end_critical(unit_no); /* * if any active pins were found, don't attempt the transfer, * unless we're in scanner mode (read-write), scanners use the PE line * to get the host's attention. */ if ((bpp_p->openflags & (FREAD | FWRITE)) == (FREAD | FWRITE)) { /* * Toggle the scan/print line for scanning */ start_critical(unit_no); if (bpp_transfer_parms_p->read_handshake == BPP_HSCAN_HS) { /* The HP Scanjet uses AFX */ bpp_regs_p->out_pins &= ~BPP_AFX_PIN; } else { bpp_regs_p->out_pins &= ~BPP_SLCTIN_PIN; } end_critical(unit_no); } else { if ((bpp_errorstat_p->pin_status & (BPP_ERR_ERR | BPP_SLCT_ERR | BPP_PE_ERR))) { /* printer error - no transfer allowed */ bpp_print(LOG_INFO, "In bpp_write, pending error pin condition\n"); retval = ENXIO; goto out; } } start_critical(unit_no); /* mark the transfer direction in the hardware */ bpp_regs_p->trans_cntl &= ~BPP_DIRECTION; /* * make sure the memory clear operation is turned off */ bpp_regs_p->op_config &= ~BPP_EN_MEM_CLR; /* * Set the handshake bits */ if (bpp_transfer_parms_p->write_handshake == BPP_NO_HS) { bpp_print(LOG_INFO, "BPP_NO_HS case\n"); bpp_regs_p->op_config &= ~(BPP_ACK_OP | BPP_BUSY_OP); } else if (bpp_transfer_parms_p->write_handshake == BPP_ACK_HS) { bpp_print(LOG_INFO, "BPP_ACK_HS case\n"); bpp_regs_p->op_config &= ~BPP_BUSY_OP; bpp_regs_p->op_config |= BPP_ACK_OP; } else if (bpp_transfer_parms_p->write_handshake == BPP_BUSY_HS) { bpp_print(LOG_INFO, "BPP_BUSY_HS case\n"); bpp_regs_p->op_config |= BPP_BUSY_OP; bpp_regs_p->op_config &= ~BPP_ACK_OP; } /* Make sure that ACK and BUSY are unidirectional */ bpp_regs_p->op_config &= ~(BPP_ACK_BIDIR | BPP_BUSY_BIDIR); end_critical(unit_no); /* set the dss and dsw values */ set_dss_dsw(unit_no, 0); retval = physio(bpp_strategy, &bpp_p->buf, dev, B_WRITE, bpp_minphys, uio); out: bpp_print(LOG_INFO, "Leaving bpp_write, unit %d: errno %d.\n", unit_no, retval); last_trans = write_trans; /* set last transfer type */ return (retval); } /* * Limit transfer size to the smaller of * - system minphys size * - 16 MB limit in HIOD address register */ static void bpp_minphys(bp) struct buf *bp; { minphys(bp); if (bp->b_bcount > BPP_MAX_DMA) bp->b_bcount = BPP_MAX_DMA; } /* * Check to see if any of the control pins are active. * NOTE: if this routine is called from the top half, * it should be bracketed by start_critical and end_critical. */ int check_for_active_pins(unit_no) u_char unit_no; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register struct bpp_regs *bpp_regs_p; register struct bpp_error_status *bpp_errorstat_p; /* error stat */ bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_errorstat_p = &bpp_p->error_stat; bpp_print(LOG_INFO, "Entering check_for_active_pins, unit number %d.\n", unit_no); /* * Check that there are no pending ERR, SLCT or PE error * conditions. If there are, do not attempt the transfer. */ bpp_print(LOG_INFO, "check_active_pins: in_pins = 0x%x\n", bpp_regs_p->in_pins); bpp_print(LOG_INFO, "check_active_pins: int_cntl = 0x%x\n", bpp_regs_p->int_cntl); if (((bpp_regs_p->in_pins & BPP_ERR_PIN) && (bpp_regs_p->int_cntl & BPP_ERR_IRP)) || ((~bpp_regs_p->in_pins & BPP_ERR_PIN)&& (~bpp_regs_p->int_cntl & BPP_ERR_IRP))) { /* ERR active */ bpp_print(LOG_INFO, "In ck_active_pins, pending ERR condition\n"); bpp_errorstat_p->pin_status |= BPP_ERR_ERR; } if (((bpp_regs_p->in_pins & BPP_SLCT_PIN) && (bpp_regs_p->int_cntl & BPP_SLCT_IRP)) || ((~bpp_regs_p->in_pins & BPP_SLCT_PIN)&& (~bpp_regs_p->int_cntl & BPP_SLCT_IRP))) { /* SLCT active */ bpp_print(LOG_INFO, "In ck_active_pins, pending SLCT condition\n"); bpp_errorstat_p->pin_status |= BPP_SLCT_ERR; } if (((bpp_regs_p->in_pins & BPP_PE_PIN) && (bpp_regs_p->int_cntl & BPP_PE_IRP)) || ((~bpp_regs_p->in_pins & BPP_PE_PIN)&& (~bpp_regs_p->int_cntl & BPP_PE_IRP))) { /* PE active */ bpp_print(LOG_INFO, "In check_active_pins, pending PE condition\n"); bpp_errorstat_p->pin_status |= BPP_PE_ERR; } bpp_print(LOG_INFO, "Leaving check_for_active_pins, unit number %d.\n", unit_no); } /* * Setup and start a transfer on the device. */ /*ARGSUSED*/ static bpp_strategy(bp) register struct buf *bp; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ u_int unit_no; int timeout_value; /* read or write timeout in secs */ u_long start_address; /* kernel virt. DVMA start address */ #ifdef notdef u_long end_address; /* kernel virt. DVMA end address */ #endif notdef u_long size; /* size of DVMA transfer */ register struct bpp_transfer_parms *bpp_transfer_parms_p; register struct bpp_regs *bpp_regs_p; unit_no = BPP_UNIT(bp->b_dev); bpp_print(LOG_INFO, "bpp%d:Entering bpp_strategy: length 0x%x.\n", unit_no, bp->b_bcount); /* * Use the unit number to locate our data structures. */ bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_transfer_parms_p = &bpp_p->transfer_parms; /* Clear the unit error status struct */ bpp_p->error_stat = bpp_default_error_stat; bpp_print_regs(unit_no, "Start strategy, before checking for bpp_idle"); start_critical(unit_no); /* * Get dvma bus resource, sleeping if necessary. */ bp->b_mbinfo = mb_mapalloc(dvmamap, bp, 0, ((int (*)())0), (caddr_t) 0); #ifdef DEBUG bpp_print(LOG_INFO, "In bpp_strategy, *(&bp->b_mbinfo) = %x\n", *(&bp->b_mbinfo)); #endif DEBUG #ifdef notdef /* FIXME: This code has never been tested properly */ /* * The bpp hardware will not properly transfer across * a 16 MB boundary. If the transfer will cross the boundary, * I trim the size I pass to the DMA engine, so * that the resulting end address is just short of * the boundary. */ size = bp->b_bcount; start_address = (u_long) MBI_ADDR(bp->b_mbinfo + DVMA); end_address = start_address + size; if ((start_address % BPP_MAX_DMA) > (end_address % BPP_MAX_DMA)) { /* transfer would cross a boundary */ bpp_print(LOG_DEBUG, "bpp_strategy, unit %d, transfer crosses boundary.\n", unit_no); size = BPP_MAX_DMA - (start_address % BPP_MAX_DMA); bpp_p->transfer_remainder = (bp->b_bcount - size); } else { size = bp->b_bcount; bpp_p->transfer_remainder = 0; } #else notdef size = bp->b_bcount; start_address = (u_long) MBI_ADDR(bp->b_mbinfo + DVMA); #ifdef notdef end_address = start_address + size; #endif notdef bpp_p->transfer_remainder = 0; #endif notdef /* * Write the dma start address to the hardware. * Write the transfer byte count to the hardware. */ bpp_regs_p->dma_addr = start_address; bpp_regs_p->dma_bcnt = size; end_critical(unit_no); bpp_print(LOG_INFO, "bpp_strategy: Transfer %d bytes starting at 0x%x.\n", bpp_regs_p->dma_bcnt, bpp_regs_p->dma_addr); bpp_print(LOG_INFO, "before enabling interrupts, dma csr=0x%x, int_cntl=0x%x.\n", bpp_regs_p->dma_csr, bpp_regs_p->int_cntl); /* * Enable byte-counter during DVMA. * Enable TC interrupts so we will know when the DVMA is done. * Start the DVMA. * Enable the peripheral error interrupts. */ start_critical(unit_no); /* * Do not close critical section until timeouts have been * enabled, otherwise we might get an untimeout before * the timeout has been set! */ bpp_regs_p->dma_csr |= BPP_ENABLE_BCNT; bpp_regs_p->dma_csr &= ~BPP_TC_INTR_DISABLE; bpp_regs_p->dma_csr |= BPP_ENABLE_DMA; if (bp->b_flags & B_READ) { bpp_print(LOG_INFO, "bp->b_flags indicates READ mode\n"); timeout_value = bpp_transfer_parms_p->read_timeout; } else { bpp_print(LOG_INFO, "bp->b_flags indicates WRITE mode\n"); if (!(bpp_p->openflags & FREAD && bpp_p->openflags & FWRITE)) { bpp_regs_p->int_cntl |= (BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN); } bpp_print(LOG_INFO, "after enable error int. int cntl contains 0x%x.\n", bpp_regs_p->int_cntl); timeout_value = bpp_transfer_parms_p->write_timeout; } bpp_print(LOG_INFO, "after enabling interrupts, dma csr = 0x%x, int_cntl = 0x%x.\n", bpp_regs_p->dma_csr, bpp_regs_p->int_cntl); bpp_print(LOG_INFO, "Setting timeout to call bpp_transfer_timeout in %d sec\n", timeout_value); timeout(bpp_transfer_timeout, (caddr_t)unit_no, (timeout_value*hz)); bpp_p->timeouts |= TRANSFER_TIMEOUT; bpp_print(LOG_INFO, "In bpp_strategy, Timeout block is 0x%x.\n", bpp_p->timeouts); end_critical(unit_no); #ifdef NO_BPP_HW bpp_print(LOG_INFO, "Setting timeout to call bpp_fakeout in 2 sec\n"); timeout(bpp_fakeout, unit_no, 2*hz); /* 2 seconds */ bpp_p->timeouts |= FAKEOUT_TIMEOUT; bpp_print(LOG_INFO, "In bpp_strategy, Timeout block is 0x%x.\n", bpp_p->timeouts); #endif NO_BPP_HW bpp_print_regs(unit_no, "end of strategy"); bpp_print(LOG_INFO, "Leaving bpp_strategy.\n"); return; } /* * Handle special control requests */ /*ARGSUSED*/ int bpp_ioctl(dev, cmd, arg, flag) dev_t dev; int cmd; caddr_t arg; int flag; { u_char unit_no; u_short retval = 0; /* return value (errno) for system call */ u_short read_retval = 0; u_short write_retval = 0; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register struct bpp_regs *bpp_regs_p; register struct bpp_transfer_parms *bpp_transfer_parms_p; register struct bpp_error_status *bpp_errorstat_p; /* error stat */ register struct bpp_pins *bpp_pins_p; /* error stat */ register enum handshake_t write_handshake; register enum handshake_t read_handshake; unit_no = BPP_UNIT(dev); bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_transfer_parms_p = &bpp_p->transfer_parms; bpp_errorstat_p = &bpp_p->error_stat; bpp_pins_p = &bpp_p->pins; write_handshake = bpp_transfer_parms_p->write_handshake; read_handshake = bpp_transfer_parms_p->read_handshake; bpp_print(LOG_INFO, "Entering bpp_ioctl, unit number %d.\n", unit_no); switch (cmd) { case BPPIOC_SETPARMS: /* set transfer parameters */ bpp_print(LOG_INFO, "BPPIOC_SETPARMS case.\n"); bpp_transfer_parms_p = (struct bpp_transfer_parms *)arg; if (flag & FREAD) { bpp_print(LOG_INFO, "Checking read parameters.\n"); read_retval = check_read_params(bpp_transfer_parms_p, flag); } if (flag & FWRITE) { bpp_print(LOG_INFO, "Checking write parameters.\n"); write_retval = check_write_params(bpp_transfer_parms_p, flag); } if (read_retval || write_retval) { retval = EINVAL; } else { /* valid parameters */ bpp_p->transfer_parms = *(struct bpp_transfer_parms *)arg; } break; case BPPIOC_GETPARMS: /* get transfer parameters */ bpp_print(LOG_INFO, "BPPIOC_GETPARMS case.\n"); *(struct bpp_transfer_parms *)arg = bpp_p->transfer_parms; retval = 0; break; case BPPIOC_SETOUTPINS: /* set output pins */ bpp_print(LOG_INFO, "BPPIOC_SETOUTPINS case.\n"); bpp_pins_p = (struct bpp_pins *)arg; if (flag & FREAD) { bpp_print(LOG_INFO, "Checking read pins.\n"); read_retval = check_read_pins(bpp_pins_p, flag, read_handshake); if (read_retval == 0) { /* valid pins */ bpp_p->pins = *(struct bpp_pins *)arg; } } if (flag & FWRITE) { bpp_print(LOG_INFO, "Checking write pins.\n"); write_retval = check_write_pins(bpp_pins_p, flag, write_handshake); if (write_retval == 0) { /* valid pins */ bpp_p->pins = *(struct bpp_pins *)arg; } } if (read_retval || write_retval) { retval = EINVAL; } else { /* All is well, write the registers */ start_critical(unit_no); bpp_regs_p->out_pins = bpp_p->pins.output_reg_pins; /* the previous line will not cstyle */ bpp_regs_p->in_pins = bpp_p->pins.input_reg_pins; end_critical(unit_no); } break; case BPPIOC_GETOUTPINS: /* get output pins */ bpp_print(LOG_INFO, "BPPIOC_GETOUTPINS case.\n"); /* read the current pin state into the struct */ read_outpins(unit_no, flag, write_handshake); *(struct bpp_pins *)arg = bpp_p->pins; retval = 0; break; case BPPIOC_GETERR: /* get error block status */ bpp_print(LOG_INFO, "BPPIOC_GETERR case.\n"); *(struct bpp_error_status *)arg = bpp_p->error_stat; retval = 0; break; case BPPIOC_TESTIO: /* test transfer readiness */ bpp_print(LOG_INFO, "BPPIOC_TESTIO case.\n"); bpp_errorstat_p = &bpp_p->error_stat; retval = 0; /* clear any old error status */ *bpp_errorstat_p = bpp_default_error_stat; start_critical(unit_no); check_for_active_pins(unit_no); end_critical(unit_no); /* if any active pins were found, return -1 */ if (bpp_errorstat_p->pin_status & (BPP_ERR_ERR | BPP_SLCT_ERR | BPP_PE_ERR)) { bpp_print(LOG_INFO, "In TESTIO, found error pin condition\n"); retval = EIO; } else retval = 0; break; /* TEST - request partial fake transfer */ case BPPIOC_SETBC: bpp_print(LOG_INFO, "BPPIOC_SETBC case.\n"); sim_byte_count = *(u_int *)arg; retval = 0; break; /* TEST - get DMA_BCNT from last data transfer */ case BPPIOC_GETBC: bpp_print(LOG_INFO, "BPPIOC_GETBC case.\n"); retval = 0; break; /* TEST - get contents of device registers */ case BPPIOC_GETREGS: bpp_print(LOG_INFO, "BPPIOC_GETREGS case.\n"); *(struct bpp_regs *)arg = *(bpp_p->bpp_regs_p); retval = 0; break; /* TEST - set special fakeout error code to simulate errs */ case BPPIOC_SETERRCODE: bpp_print(LOG_INFO, "BPPIOC_SETERRCODE case.\n"); err_code = *(int *)arg; retval = 0; break; /* TEST - get pointer to fakeout transferred data */ case BPPIOC_GETFAKEBUF: bpp_print(LOG_INFO, "BPPIOC_GETFAKEBUF case.\n"); #ifdef NO_BPP_HW bpp_print(LOG_INFO, "bpp_hard_buf = 0x%x\n", bpp_hard_buf); bpp_print(LOG_INFO, "arg = 0x%x\n", arg); bpp_print(LOG_INFO, "*arg = 0x%x\n", *arg); { u_char **argpp; argpp = (u_char **)arg; retval = (copyout(&bpp_hard_buf[0], *argpp, FAKE_BUF_SIZE)); bpp_print(LOG_INFO, "argpp = 0x%x\n", argpp); bpp_print(LOG_INFO, "*argpp = 0x%x\n", *argpp); } #else NO_BPP_HW bpp_print(LOG_WARNING, "Bad ioctl call!\n"); #endif NO_BPP_HW retval = ENOTTY; break; default: bpp_print(LOG_NOTICE, "Error in bpp_ioctl switch!\n"); retval = ENOTTY; break; } bpp_print(LOG_INFO, "Leaving bpp_ioctl, unit %d: errno %d.\n", unit_no, retval); return (retval); } /* * Handle an interrupt or interrupts that may or may not be from * one or more of the bpp units. This routine is safely declared * static because it is explicitly registered by address. */ static int bpp_poll() { u_int bpp_unit_no; u_int int_serviced = 0; register struct bpp_regs *bpp_regs_p; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ bpp_print(LOG_INFO, "Entering bpp_poll.\n"); /* * March through the units, checking for an interrupt pending. */ for (bpp_unit_no = 0; bpp_unit_no < nbpp; ++bpp_unit_no) { bpp_p = bpp_units[bpp_unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; if ((bpp_regs_p->dma_csr & BPP_INT_PEND) || (bpp_regs_p->dma_csr & BPP_ERR_PEND)) { bpp_print(LOG_INFO, "Interrupt found, unit #%d.\n", bpp_unit_no); /* * Mark that we found an interrupting device. * Call the interrupt service routine. */ int_serviced = 1; (void) bpp_intr(bpp_unit_no); } } bpp_print(LOG_INFO, "Leaving bpp_poll, int_serviced = 0x%x.\n", int_serviced); return (int_serviced); } /* * Interrupt routine for the bpp hardware. This routine is always called * from bpp_poll with the unit number. When this routine is called, * we know that this unit issued an interrupt that needs service. */ static void bpp_intr(unit_no) u_int unit_no; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register struct bpp_regs *bpp_regs_p; register struct bpp_error_status *bpp_errorstat_p; /* error stat */ register struct buf *bp; bpp_print(LOG_INFO, "Entering bpp_intr, unit %d.\n", unit_no); /* * Set pointer to dma registers. * Set pointer to error stat struct. * Set pointer to buf structure. */ bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_errorstat_p = &bpp_p->error_stat; bp = &bpp_p->buf; bpp_print(LOG_INFO, "dma csr contains 0x%x.\n", bpp_regs_p->dma_csr); bpp_print(LOG_INFO, "int cntl contains 0x%x.\n", bpp_regs_p->int_cntl); /* * The bpp hardware can interrupt for errors, or * for several transfer conditions. These can happen * at the same time. * I process any errors first, checking to see * if a transfer was in process. * In the future, I may want to count how many times I've * tried to flush the cache, and eventually give up and * reset the hardware. * Then I process "normal" conditions. */ /* * Check for an error, recover if possible. */ if (bpp_regs_p->dma_csr & BPP_ERR_PEND) { bpp_print(LOG_INFO, "Error interrupt detected\n"); if (bpp_regs_p->dma_csr & BPP_SLAVE_ERR) { bpp_print(LOG_INFO, "Slave error detected\n"); } if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) { /* was transferring */ /* * The transfer has failed. Notify the application * how many bytes got out, and that there was * an IO error, and turn off the transfer. */ bpp_transfer_failed(unit_no); return; } else { /* will make return value -1 */ bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; } /* * capture the error status in the error_stat struct * so the application can get it with the GETERR * ioctl later. */ bpp_errorstat_p->bus_error = 1; /* Mark the error. */ bp->b_error = EIO; /* clear the error interrupt */ while (bp->b_flags & B_READ) /* cannot assert FLUSH till cache drains */ /* spin on draining bit */ bpp_regs_p->dma_csr |= BPP_FLUSH; } if (bpp_regs_p->dma_csr & BPP_INT_PEND) { bpp_print(LOG_INFO, "Interrupt pending found.\n"); bpp_print(LOG_INFO, "dma csr contains 0x%x.\n", bpp_regs_p->dma_csr); bpp_print(LOG_INFO, "int cntl contains 0x%x.\n", bpp_regs_p->int_cntl); /* TC case - terminal count */ if (bpp_regs_p->dma_csr & BPP_TERMINAL_CNT && ((bpp_regs_p->dma_csr & BPP_TC_INTR_DISABLE) == 0)) { bpp_print(LOG_INFO, "Terminal count interrupt found.\n"); /* mask this interrupt */ bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE; bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT; /* and clear the interrupting condition */ bpp_regs_p->dma_csr |= BPP_TERMINAL_CNT; bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA; bp->b_resid = bpp_p->transfer_remainder; /* Mask the error interrupt conditions */ bpp_regs_p->int_cntl &= ~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN); bpp_print(LOG_INFO, "dma csr contains 0x%x.\n", bpp_regs_p->dma_csr); bpp_print(LOG_INFO, "int cntl contains 0x%x.\n", bpp_regs_p->int_cntl); } /* ERR_IRQ case - error pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_ERR_IRQ) && (bpp_regs_p->int_cntl & BPP_ERR_IRQ_EN)) { bpp_print(LOG_INFO, "Error pin interrupt found.\n"); bpp_errorstat_p->pin_status |= BPP_ERR_ERR; if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) { /* was transferring */ bpp_transfer_failed(unit_no); } /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_ERR_IRQ; } /* SLCT_IRQ case - select pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_SLCT_IRQ) && (bpp_regs_p->int_cntl & BPP_SLCT_IRQ_EN)) { bpp_print(LOG_INFO, "Select pin interrupt found.\n"); bpp_errorstat_p->pin_status |= BPP_SLCT_ERR; if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) { /* was transferring */ bpp_transfer_failed(unit_no); } /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_SLCT_IRQ; } /* PE_IRQ case - paper error pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_PE_IRQ) && (bpp_regs_p->int_cntl & BPP_PE_IRQ_EN)) { bpp_print(LOG_INFO, "Paper error pin interrupt found.\n"); bpp_errorstat_p->pin_status |= BPP_PE_ERR; if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) { /* was transferring */ bpp_transfer_failed(unit_no); } /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_PE_IRQ; } /* * The interrupts below (BUSY, ACK, and DS) * are available in the hardware, but are not * being used for anything now. */ /* BUSY_IRQ case - busy pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_BUSY_IRQ) && (bpp_regs_p->int_cntl & BPP_BUSY_IRQ_EN)) { bpp_print(LOG_INFO, "Busy pin interrupt found.\n"); /* for pio only */ /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_BUSY_IRQ; } /* ACK_IRQ case - acknowledge pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_ACK_IRQ) && (bpp_regs_p->int_cntl & BPP_ACK_IRQ_EN)) { bpp_print(LOG_INFO, "Acknowledge pin interrupt found.\n"); /* for pio only */ /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_ACK_IRQ; } /* DS_IRQ case - data strobe pin interrupt */ if ((bpp_regs_p->int_cntl & BPP_DS_IRQ) && (bpp_regs_p->int_cntl & BPP_DS_IRQ_EN)) { bpp_print(LOG_INFO, "Data strobe pin interrupt found.\n"); /* for pio only */ /* clear interrupting condition */ bpp_regs_p->int_cntl |= BPP_DS_IRQ; } } /* end of INT_PEND check */ bpp_print(LOG_INFO, "dma csr contains 0x%x.\n", bpp_regs_p->dma_csr); bpp_print(LOG_INFO, "int cntl contains 0x%x.\n", bpp_regs_p->int_cntl); /* Clear the transfer timeout */ bpp_print(LOG_INFO, "In bpp_intr, Clearing transfer timeout.\n"); untimeout(bpp_transfer_timeout, (caddr_t)unit_no); bpp_p->timeouts &= ~TRANSFER_TIMEOUT; bpp_print(LOG_INFO, "In bpp_intr, Timeout block is 0x%x.\n", bpp_p->timeouts); /* * Release the dvma bus resource. */ #ifdef DEBUG bpp_print(LOG_INFO, "In bpp_intr, *(&bp->b_mbinfo) = %x\n", *(&bp->b_mbinfo)); #endif DEBUG (void) mb_mapfree(dvmamap, &bp->b_mbinfo); bpp_print(LOG_DEBUG, "bpp_intr, unit %d, Calling iodone.\n", unit_no); /* * Mark the io on the buf as finished, with the side effect * of waking up others who want to use the buf. */ (void) iodone(bp); bpp_print(LOG_INFO, "Leaving bpp_intr.\n"); return; } /* * A transfer has failed for some reason. * Mark the bp struct to indicate how much happened, * and turn off the transfer and its interrupts. * NOTE: if this routine is called from the top half, * it should be bracketed by start_critical and end_critical. */ static int bpp_transfer_failed(unit_no) u_int unit_no; /* unit which had transfer failure */ { struct buf *bp; register struct bpp_regs *bpp_regs_p; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ bpp_p = bpp_units[unit_no]; bp = (struct buf *)(&bpp_p->buf), bpp_regs_p = bpp_p->bpp_regs_p; bpp_print(LOG_INFO, "Entering bpp_transfer_failed.\n"); bpp_print(LOG_WARNING, "ERROR: bpp%d transfer failed!\n", unit_no); /* * The transfer has failed. Notify the application * how many bytes got out, and turn off the transfer. * NOTE: don't set B_ERROR in b_flags else the return from * write() will be -1. See syscall(). * NOTE: the kernel ignores the the b_error field * in the short-write case - errno is always set to zero. */ /* Disable the DMA first for safety */ bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA; bpp_print_regs(unit_no, "Just after disable DMA in transfer_failed"); /* If the DMA state machines are not idle, reset them. */ if (!(bpp_regs_p->op_config & BPP_IDLE)) { bpp_print(LOG_INFO, "Warning: DMA is not IDLE!\n"); bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT; bpp_print(LOG_INFO, "In bpp_strategy, resetting PP state machine\n"); bpp_regs_p->op_config |= BPP_SRST; bpp_regs_p->op_config &= ~BPP_SRST; /* * we have not received the acknowledge for the last * byte transferred, so the byte counter was never * decremented for it. */ bp->b_resid = ((bpp_regs_p->dma_bcnt - 1) + bpp_p->transfer_remainder); } else { bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT; bp->b_resid = (bpp_regs_p->dma_bcnt + bpp_p->transfer_remainder); } /* flush the cache */ bpp_regs_p->dma_csr |= BPP_FLUSH; bpp_print(LOG_INFO, "In bpp_transfer_failed, Residual is %d.\n", bp->b_resid); /* make sure the DMA doesn't start again. */ bpp_regs_p->dma_bcnt = 0; /* * Disable the TC interrupts. * Mask the error interrupts too. */ bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE; bpp_regs_p->int_cntl &= ~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN); /* Check for any of the input pins active */ /* * Already inside a critical section here, * thus this routine is not bracketed with * start_critical and end_critical calls. */ check_for_active_pins(unit_no); bpp_print_regs(unit_no, "At end of bpp_transfer_failed"); bpp_print(LOG_INFO, "Leaving bpp_transfer_failed.\n"); return; } /* * This routine is called when the DVMA does not complete * and generate a TC interrupt. * I mark the bp struct to indicate that the transfer failed, * and turn off the transfer. I then call iodone to wake up strategy. */ static bpp_transfer_timeout(unit_no) u_int unit_no; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register struct buf *bp; register struct bpp_regs *bpp_regs_p; bpp_print(LOG_INFO, "Entering bpp_transfer_timeout, unit #%d.\n", unit_no); bpp_p = bpp_units[unit_no]; bpp_print(LOG_INFO, "In bpp_transfer_timeout, Timeout block is 0x%x.\n", bpp_p->timeouts); ASSERT(bpp_p->timeouts != 0); /* * Use the unit number to locate our data structures. */ bp = (struct buf *)(&bpp_p->buf), bpp_regs_p = bpp_p->bpp_regs_p; /* * If we're talking to the * Ricoh scanner, handle it's special "hold busy" * protocol -- Toggle the print/scan (PR/SC) bit and * reset the parallel port */ bpp_print(LOG_INFO, "byte count is %d.\n", bpp_regs_p->dma_bcnt); bpp_print(LOG_INFO, "write_timeout is %d.\n", bpp_p->transfer_parms.write_timeout); if (bpp_regs_p->trans_cntl & BPP_DIRECTION) { /* read mode - partial reads will time out */ bpp_print(LOG_DEBUG, "read timeout, clearing registers.\n"); start_critical(unit_no); bp->b_resid = (bpp_regs_p->dma_bcnt + bpp_p->transfer_remainder); /* make sure the DMA doesn't start again. */ bpp_regs_p->dma_bcnt = 0; /* * Disable the byte counting, and the TC interrupts. * Mask the error interrupts too. */ bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE; bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT; bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA; bpp_regs_p->int_cntl &= ~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN); end_critical(unit_no); } else if ((bpp_regs_p->trans_cntl & BPP_DIRECTION) == 0) { /* other write cases (read can time out w/no error) */ start_critical(unit_no); bpp_transfer_failed(unit_no); /* Mark the error. */ /* * bp->b_resid will be set to indicate the number * of bytes actually transferred by bpp_transfer_failed(). * If no bytes were transferred, set B_ERROR * so that -1 is returned, and set the b_error value. */ if (!(bp->b_resid)) { /* not a partial transfer */ bp->b_flags |= B_ERROR; bp->b_error = EIO; } end_critical(unit_no); } /* mark the timeout as no longer pending */ bpp_p->timeouts &= ~TRANSFER_TIMEOUT; /* mark the error status structure */ bpp_p->error_stat.timeout_occurred = 1; bpp_print(LOG_INFO, "In bpp_transfer_timeout, Timeout blk is 0x%x.\n", bpp_p->timeouts); /* * Release the dvma bus resource. */ start_critical(unit_no); (void) mb_mapfree(dvmamap, &bp->b_mbinfo); end_critical(unit_no); #ifdef DEBUG bpp_print(LOG_INFO, "In bpp_transfer_timeout, *(&bp->b_mbinfo) = %x\n", *(&bp->b_mbinfo)); #endif DEBUG bpp_print(LOG_DEBUG, "bpp_transfer_timeout, unit %d, Calling iodone.\n", unit_no); (void) iodone(bp); bpp_print(LOG_INFO, "Leaving bpp_transfer_timeout, unit #%d.\n", unit_no); return; } /*<<<<<<<<<<<<<<<<<<<<< PRIVATE SUPPORT ROUTINES >>>>>>>>>>>>>>>>>>>>>*/ /* * Protect a critical section of code from interruption by the unit whose * data structures are currently being serviced. In BPP_DEBUG mode, * assertion checking is done to assure the proper pairing of * start_critical() and end_critical() calls. * * ARGS: unit_no The current unit number, an index * into the bpp_units array. * * RETURN VALUE: None. */ static void start_critical(unit_no) register u_int unit_no; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register int temp_saved_spl; bpp_print(LOG_INFO, "Entering start_critical: unit %d\n", unit_no); bpp_p = bpp_units[unit_no]; temp_saved_spl = splr((int)(bpp_p->interrupt_pri)); bpp_print(LOG_DEBUG, "in start_critical, tmp_svd_spl = %x\n", temp_saved_spl); bpp_print(LOG_DEBUG, "in start_critical, svd_spl = %x\n", bpp_p->saved_spl); #ifdef BPP_DEBUG /* * Do assertion checking. This is a critical section in itself. * If saved_spl is something other than -1, you are calling * start_critical for the second time in a row. * No nested critical sections are allowed! */ if (bpp_p->saved_spl != (-1)) { bpp_print(LOG_INFO, "start_critical: ASSERT failed, saved_spl: %d\n", bpp_p->saved_spl); } #endif BPP_DEBUG bpp_p->saved_spl = temp_saved_spl; bpp_print(LOG_INFO, "Leaving start_critical: unit %d\n", unit_no); return; } /* * End the protection of code from interruption by the unit whose * data structures are currently being serviced. In BPP_DEBUG mode, * assertion checking is done to assure the proper pairing of * start_critical() and end_critical() calls. * * ARGS: unit_no The current unit number, an index * into the bpp_units array. * * RETURN VALUE: None. */ static void end_critical(unit_no) register u_int unit_no; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register int temp_saved_spl; bpp_print(LOG_INFO, "Entering end_critical: unit %d\n", unit_no); bpp_p = bpp_units[unit_no]; temp_saved_spl = bpp_p->saved_spl; bpp_print(LOG_DEBUG, "in end_critical, tmp_svd_spl = %x\n", temp_saved_spl); bpp_print(LOG_DEBUG, "in end_critical, svd_spl = %x\n", bpp_p->saved_spl); #ifdef BPP_DEBUG /* * Do assertion checking. * If saved_spl is -1, you called end_critical without * calling start_critical first. */ if (bpp_p->saved_spl == (-1)) { bpp_print(LOG_INFO, "end_critical: ASSERT failed, saved_spl: %d\n", bpp_p->saved_spl); } bpp_p->saved_spl = (-1); #endif BPP_DEBUG /* * Release the critical section last. * The above is a critical section in itself. */ (void) splx(temp_saved_spl); bpp_print(LOG_INFO, "Leaving end_critical: unit %d\n", unit_no); return; } /* Utility Functions */ /* * see_devinfo: * Show interesting information stored in this node of the devinfo tree * (for development and debugging). */ static void see_devinfo(devinfo_p) register struct dev_info *devinfo_p; { struct dev_reg *dev_reg_p; struct dev_intr *dev_intr_p; u_int counter; bpp_print(LOG_INFO, "Entering see_devinfo: devinfo_p 0x%x\n", devinfo_p); bpp_print(LOG_DEBUG, "devinfo_p->devi_name: %s\n", devinfo_p->devi_name); bpp_print(LOG_DEBUG, "devinfo_p->devi_unit: %d\n", devinfo_p->devi_unit); bpp_print(LOG_DEBUG, "devinfo_p->devi_nreg: %d\n", devinfo_p->devi_nreg); bpp_print(LOG_DEBUG, "devinfo_p->devi_reg: 0x%x\n", devinfo_p->devi_reg); /* * Show all the device registers. */ dev_reg_p = devinfo_p->devi_reg; /* * dev_reg_p may be NULL, but only if devi_nreg is zero. */ for (counter = 1; counter <= devinfo_p->devi_nreg; ++counter, ++dev_reg_p) { if (dev_reg_p == NULL) { break; } bpp_print(LOG_DEBUG, "REGISTER #%d: devi_reg->reg_bustype: 0x%x\n", counter, dev_reg_p->reg_bustype); bpp_print(LOG_DEBUG, "REGISTER #%d: devi_reg->reg_addr: 0x%x\n", counter, dev_reg_p->reg_addr); bpp_print(LOG_DEBUG, "REGISTER #%d: devi_reg->reg_size: 0x%x\n", counter, dev_reg_p->reg_size); } bpp_print(LOG_DEBUG, "devinfo_p->devi_nintr: %d\n", devinfo_p->devi_nintr); bpp_print(LOG_DEBUG, "devinfo_p->devi_intr: 0x%x\n", devinfo_p->devi_intr); /* * Show all the interrupt data. */ dev_intr_p = devinfo_p->devi_intr; /* * dev_intr_p may be NULL, but only if devi_nintr is zero. */ for (counter = 1; counter <= devinfo_p->devi_nintr; ++counter, ++dev_intr_p) { if (dev_intr_p == NULL) { break; } bpp_print(LOG_DEBUG, "INTERRUPT #%d: devi_intr->int_pri: 0x%x\n", counter, dev_intr_p->int_pri); bpp_print(LOG_DEBUG, "INTERRUPT #%d: devi_intr->int_vec: 0x%x\n", counter, dev_intr_p->int_vec); } bpp_print(LOG_DEBUG, "devinfo_p->devi_data: 0x%x\n", devinfo_p->devi_data); bpp_print(LOG_DEBUG, "devinfo_p->devi_nodeid: 0x%x\n", devinfo_p->devi_nodeid); bpp_print(LOG_INFO, "Leaving see_devinfo: devinfo_p 0x%x\n", devinfo_p); return; } /* * Get a property from the fcode prom on the SBus card, or if the property * is not available, substitute the assigned default. Only string valued * properties are handled here. You have to know the name of a property * that you want to get. There is no way to get all defined properties. * * ARGS: propname A pointer to string which is the assigned * name of the desired property. * * defaultvalue A pointer to string which is the default * value to use if the named property is not * defined in the devinfo structure. * * RETURN VALUE: A pointer to the value string selected. * This pointer may have any value that was * passed in for the default, including * a NULL string or a NULL pointer. */ static char * get_property(devinfo_p, propname, defaultvalue) struct dev_info *devinfo_p; char *propname; char *defaultvalue; { char *ret_getlongprop; char *propval; char *msg_string; bpp_print(LOG_INFO, "Entering get_property: devinfo_p: 0x%x\n", devinfo_p); ret_getlongprop = (char *) getlongprop(devinfo_p->devi_nodeid, propname); if (ret_getlongprop == NULL) { propval = defaultvalue; msg_string = "default"; } else { propval = ret_getlongprop; msg_string = "from prom"; } bpp_print(LOG_INFO, "Leaving get_property: %s=%s (%s)\n", propname, propval, msg_string); } /* * Get a numeric property from the fcode prom on the SBus card, * or if the property * is not available, substitute the assigned default. Only numeric valued * properties are handled here. You have to know the name of a property * that you want to get. There is no way to get all defined properties. * * ARGS: propname A pointer to string which is the assigned * name of the desired property. * * defaultvalue A pointer to string which is the default * value to use if the named property is not * defined in the devinfo structure. * * RETURN VALUE: The numeric property from the prom, * or the default value. */ static int get_num_property(devinfo_p, propname, defaultvalue) struct dev_info *devinfo_p; char *propname; int defaultvalue; { addr_t ret_getprop; int propval; char *msg_string; bpp_print(LOG_INFO, "Entering get_num_property: devinfo_p: 0x%x\n", devinfo_p); ret_getprop = (addr_t)getprop(devinfo_p->devi_nodeid, propname, defaultvalue); propval = (int)ret_getprop; if (propval == defaultvalue) { msg_string = "default"; } else { msg_string = "from prom"; } bpp_print(LOG_INFO, "Leaving get_num_property: %s=%d (%s)\n", propname, propval, msg_string); return (propval); } /* * The values of read_setup_time and read_strobe_width * have already been bounds-checked. Convert the requested times * (in nanoseconds) to SBus clock cycles for the dss and dsw registers. * Always round the requested setup time up to the next clock * cycle boundary. */ static void set_dss_dsw(unit_no, read_mode) u_char unit_no; int read_mode; /* 1 if called from read */ { int dss_temp; /* tentative dss value */ int dsw_temp; /* tentative dsw value */ register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ register struct bpp_transfer_parms *bpp_transfer_parms_p; bpp_print(LOG_INFO, "Entering set_dss_dsw, unit:%d, read_mode = %x.\n", unit_no, read_mode); bpp_p = bpp_units[unit_no]; bpp_transfer_parms_p = &bpp_p->transfer_parms; if (read_mode) { dss_temp = bpp_transfer_parms_p->read_setup_time / sbus_clock_cycle; if (bpp_transfer_parms_p->read_setup_time % sbus_clock_cycle) dss_temp ++; /* round up */ dsw_temp = bpp_transfer_parms_p->read_strobe_width / sbus_clock_cycle; if (bpp_transfer_parms_p->read_strobe_width % sbus_clock_cycle) dsw_temp ++; /* round up */ } else { dss_temp = bpp_transfer_parms_p->write_setup_time / sbus_clock_cycle; if (bpp_transfer_parms_p->write_setup_time % sbus_clock_cycle) dss_temp ++; /* round up */ dsw_temp = bpp_transfer_parms_p->write_strobe_width / sbus_clock_cycle; if (bpp_transfer_parms_p->write_strobe_width % sbus_clock_cycle) dsw_temp ++; /* round up */ } bpp_print(LOG_INFO, "dss = 0x%x, dsw = 0x%x\n", dss_temp, dsw_temp); start_critical(unit_no); bpp_p->bpp_regs_p->hw_config = ((((u_char)dsw_temp) << 8) | ((u_char)dss_temp)); end_critical(unit_no); bpp_print(LOG_INFO, "Leaving set_dss_dsw, unit:%d.\n", unit_no); } /* * Check the values of the write parameters in the passed bpp_transfer_parms * structure. If all the parameters are in range, 0 is returned. * If there is an out-of-range parameter, EINVAL is returned. */ /*ARGSUSED*/ static u_short check_write_params(parms_p, flags) struct bpp_transfer_parms *parms_p; int flags; /* this unit's (read-write) open flags */ { u_short retval = 0; /* return value (errno) for system call */ static int max_setup = 0; /* maximum setup time allowed (ns) */ static int max_width = 0; /* maximum width time allowed (ns) */ retval = 0; bpp_print(LOG_INFO, "Entering check_write_params, parms_p = %x.\n", parms_p); bpp_print(LOG_DEBUG, "write_hs %d, write_time %d, ", parms_p->write_handshake, parms_p->write_setup_time); bpp_print(LOG_DEBUG, "write_width %d, timeout %d.\n", parms_p->write_strobe_width, parms_p->write_timeout); #ifndef lint /* better rangechecking will be added later */ /* check for legal range */ if ((parms_p->write_handshake < BPP_NO_HS) || (parms_p->write_handshake > BPP_VPLOT_HS)) { bpp_print(LOG_WARNING, "Handshake out of legal range!\n"); retval = EINVAL; goto out; } /* the handshake values overlap. Check for read handshakes */ if ((parms_p->write_handshake > BPP_BUSY_HS) && (parms_p->write_handshake < BPP_VPRINT_HS)) { bpp_print(LOG_WARNING, "Handshake out of legal write range!\n"); retval = EINVAL; goto out; } /* versatec handshakes illegal in read-write mode */ if ((flags & FREAD) && (parms_p->write_handshake > BPP_BUSY_HS)) { bpp_print(LOG_WARNING, "No versatec handshakes in read md!\n"); retval = EINVAL; goto out; } /* * Originally there was a plan to support a versatec mode. * The decision was made not to support it in software. * However, the hooks are still there in the hardware. * I leave the versatec fragments in case the decision is ever * reversed. */ /* versatec handshakes not implemented in current code */ if ((parms_p->write_handshake > BPP_BUSY_HS)) { bpp_print(LOG_WARNING, "No versatec handshakes allowed yet!\n"); retval = EINVAL; goto out; } #endif lint /* check range of setup time and strobe width here */ max_setup = BPP_DSS_SIZE * sbus_clock_cycle; max_width = BPP_DSW_SIZE * sbus_clock_cycle; if ((parms_p->write_setup_time < 0) || (parms_p->write_setup_time > max_setup)) { bpp_print(LOG_WARNING, "Write setup time out of legal range!\n"); retval = EINVAL; goto out; } if ((parms_p->write_strobe_width < 0) || (parms_p->write_strobe_width > max_width)) { bpp_print(LOG_WARNING, "Write strobe width out of legal range!\n"); retval = EINVAL; goto out; } /* check range of write timeout */ if ((parms_p->write_timeout < 0) || (parms_p->write_timeout > MAX_TIMEOUT)) { bpp_print(LOG_WARNING, "Write timeout out of legal range!\n"); retval = EINVAL; goto out; } out: bpp_print(LOG_INFO, "Leaving check_write_params, retval = %d.\n", retval); return (retval); } /* * Check the values of the read parameters in the passed bpp_transfer_parms * structure. If all the parameters are in range, 0 is returned. * If there is an out-of-range parameter, EINVAL is returned. */ /*ARGSUSED*/ static u_short check_read_params(parms_p, flags) struct bpp_transfer_parms *parms_p; int flags; /* this unit's (read-write) open flags */ { u_short retval = 0; /* return value (errno) for system call */ static int max_setup = 0; /* maximum setup time allowed (ns) */ static int max_width = 0; /* maximum width time allowed (ns) */ retval = 0; bpp_print(LOG_INFO, "Entering check_read_params, parms_p = %x.\n", parms_p); bpp_print(LOG_DEBUG, "read_hs %d, read_time %d, read_width %d, timeout %d.\n", parms_p->read_handshake, parms_p->read_setup_time, parms_p->read_strobe_width, parms_p->read_timeout); #ifndef lint /* check for legal range */ if ((parms_p->read_handshake < BPP_NO_HS) || (parms_p->read_handshake > BPP_SET_MEM)) { bpp_print(LOG_WARNING, "Handshake out of legal range!\n"); retval = EINVAL; goto out; } #endif lint /* check range of setup time and strobe width here */ max_setup = BPP_DSS_SIZE * sbus_clock_cycle; max_width = BPP_DSW_SIZE * sbus_clock_cycle; if ((parms_p->read_setup_time < 0) || (parms_p->read_setup_time > max_setup)) { bpp_print(LOG_WARNING, "Read setup time out of legal range!\n"); retval = EINVAL; goto out; } if ((parms_p->read_strobe_width < 0) || (parms_p->read_strobe_width > max_width)) { bpp_print(LOG_WARNING, "Read strobe width out of legal range!\n"); retval = EINVAL; goto out; } /* check range of read timeout */ if ((parms_p->read_timeout < 0) || (parms_p->read_timeout > MAX_TIMEOUT)) { bpp_print(LOG_WARNING, "Read timeout out of legal range!\n"); retval = EINVAL; goto out; } out: bpp_print(LOG_INFO, "Leaving check_read_params, retval = %d.\n", retval); return (retval); } /*ARGSUSED*/ static u_short check_read_pins(pins_p, flags, handshake) struct bpp_pins *pins_p; int flags; /* this unit's (read-write) open flags */ /* this unit's read handshake */ register enum handshake_t handshake; { u_short retval = 0; /* return value (errno) for system call */ bpp_print(LOG_INFO, "Entering check_read_pins, pins_p = 0x%x ", pins_p); bpp_print(LOG_DEBUG, "outpins = 0x%x, inpins = 0x%x.\n", pins_p->output_reg_pins, pins_p->input_reg_pins); /* check for bogus bits turned on */ if ((pins_p->output_reg_pins & ~BPP_ALL_OUT_PINS) || (pins_p->input_reg_pins & ~BPP_ALL_IN_PINS)) { bpp_print(LOG_WARNING, "Check pins : Bogus bit in bpp pins structure!\n"); retval = EINVAL; goto out; } out: bpp_print(LOG_INFO, "Leaving check_read_pins, retval = %d.\n", retval); return (retval); } /*ARGSUSED*/ static u_short check_write_pins(pins_p, flags, handshake) struct bpp_pins *pins_p; int flags; /* this unit's (read-write) open flags */ /* this unit's write handshake */ register enum handshake_t handshake; { u_short retval = 0; /* return value (errno) for system call */ bpp_print(LOG_INFO, "Entering check_write_pins, pins_p = 0x%x, ", pins_p); bpp_print(LOG_DEBUG, "outpins = 0x%x, inpins = 0x%x.\n", pins_p->output_reg_pins, pins_p->input_reg_pins); /* check for bogus bits turned on */ if ((pins_p->output_reg_pins & ~BPP_ALL_OUT_PINS) || (pins_p->input_reg_pins & ~BPP_ALL_IN_PINS)) { bpp_print(LOG_WARNING, "Check pins : Bogus bit in bpp pins structure!\n"); retval = EINVAL; goto out; } /* * Originally there was a plan to support a versatec mode. * The decision was made not to support it in software. * However, the hooks are still there in the hardware. * I leave the versatec fragments in case the decision is ever * reversed. */ #ifndef lint /* versatec handshakes not implemented in current code */ if ((handshake > BPP_BUSY_HS)) { bpp_print(LOG_WARNING, "No versatec handshakes allowed yet!\n"); /* * really, need to check for one bit only of remote * pins set. */ retval = EINVAL; goto out; } #endif lint out: bpp_print(LOG_INFO, "Leaving check_write_pins, retval = %d.\n", retval); return (retval); } static void read_outpins(unit_no, flags, handshake) u_char unit_no; int flags; /* this unit's (read-write) open flags */ /* this unit's write handshake */ register enum handshake_t handshake; { register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ u_char temppins; bpp_print(LOG_INFO, "Entering read_outpins, unit = %d, flags = 0x%x, ", unit_no, flags); bpp_p = bpp_units[unit_no]; bpp_print(LOG_DEBUG, "handshake = %d.\n", handshake); if (flags & FWRITE) { #ifndef lint if ((handshake > BPP_BUSY_HS)) { bpp_print(LOG_WARNING, "No versatec handshakes allowed yet!\n"); } #endif lint temppins = bpp_p->bpp_regs_p->out_pins & (BPP_SLCTIN_PIN | BPP_AFX_PIN | BPP_INIT_PIN); bpp_p->pins.output_reg_pins |= temppins; } bpp_print(LOG_INFO, "Leaving read_outpins.\n"); } /* * bpp_stuff_fregs() * * This routine will simulate the device registers for the bpp DMA * registers of the SBus-HIOD * */ /*ARGSUSED*/ static void bpp_stuff_fregs(unit_no) u_int unit_no; /* attaching unit's number */ { #ifdef NO_BPP_HW /* * set up default registers to the expected (inital) values */ bpp_def_regs.dma_csr = 0x40000100; /* device id = 0100 */ bpp_def_regs.dma_addr = 0; bpp_def_regs.dma_bcnt = 0; bpp_def_regs.unused = 0; bpp_def_regs.hw_config = 0; bpp_def_regs.op_config = BPP_DS_BIDIR; bpp_def_regs.data = 0; bpp_def_regs.trans_cntl = BPP_DIRECTION | 0x40; bpp_def_regs.out_pins = 0; bpp_def_regs.in_pins = 0; bpp_def_regs.int_cntl = 0; /* * now copy default registers into mimic struct */ bpp_fake_regs[unit_no] = bpp_def_regs; #endif NO_BPP_HW } /* * Peek at the bpp registers to make sure that they really * exist. Also check initial conditions. If any of this * fails, return a non-zero value. */ static int check_bpp_registers(unit_no) u_int unit_no; /* attaching unit's number */ { u_long *l_reg_addr; /* address of a 32-bit register */ u_long l_reg_contents; /* contents of a 32-bit register */ u_short *s_reg_addr; /* address of a 16-bit register */ u_short s_reg_contents; /* contents of a 16-bit register */ u_char *c_reg_addr; /* address of a 8-bit register */ u_short c_reg_contents; /* contents of a 8-bit register */ register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ bpp_print(LOG_INFO, "Entering check_bpp_registers, unit %d.\n", unit_no); bpp_p = bpp_units[unit_no]; /* check the long dma registers */ /* dma csr */ l_reg_addr = &(bpp_p->bpp_regs_p->dma_csr); if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed\n", "dma csr, address %x, contents %x\n", l_reg_addr, l_reg_contents); return (1); } else bpp_print(LOG_INFO, "dma_csr contains %x\n", l_reg_contents); /* dma addr */ l_reg_addr = &(bpp_p->bpp_regs_p->dma_addr); if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed dma addr, address %x\n", l_reg_addr, l_reg_contents); return (1); } else bpp_print(LOG_INFO, "dma_addr contains %x\n", l_reg_contents); /* dma bcnt */ l_reg_addr = &(bpp_p->bpp_regs_p->dma_bcnt); if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed dma bcnt, address %x\n", l_reg_addr); return (1); } else bpp_print(LOG_INFO, "dma_bcnt contains %x\n", l_reg_contents); /* short hardware registers */ /* hw_config */ s_reg_addr = &(bpp_p->bpp_regs_p->hw_config); if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed hw_config, address %x\n", s_reg_addr); return (1); } if (poke((short *)s_reg_addr, (short)s_reg_contents) != 0) { bpp_print(LOG_WARNING, "ck_bpp_registers: poke failed hw_config, address %x\n", s_reg_addr); return (1); } else bpp_print(LOG_INFO, "hw_config contains %x\n", s_reg_contents); /* op_config */ s_reg_addr = &(bpp_p->bpp_regs_p->op_config); if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed op_config, address %x\n", s_reg_addr); return (1); } else bpp_print(LOG_INFO, "op_config contains %x\n", s_reg_contents); /* int_cntl */ s_reg_addr = &(bpp_p->bpp_regs_p->int_cntl); if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed int_cntl, address %x\n", s_reg_addr); return (1); } else bpp_print(LOG_INFO, "int_cntl contains %x\n", s_reg_contents); /* char hardware registers */ /* data */ c_reg_addr = &(bpp_p->bpp_regs_p->data); if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed data, address %x\n", c_reg_addr); return (1); } else bpp_print(LOG_INFO, "data contains %x\n", c_reg_contents); /* trans_cntl */ c_reg_addr = &(bpp_p->bpp_regs_p->trans_cntl); if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers:peek failed trans_cntl, address %x\n", c_reg_addr); return (1); } else bpp_print(LOG_INFO, "trans_cntl contains %x\n", c_reg_contents); /* out_pins */ c_reg_addr = &(bpp_p->bpp_regs_p->out_pins); if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed out_pins, address %x\n", c_reg_addr); return (1); } else bpp_print(LOG_INFO, "out_pins contains %x\n", c_reg_contents); /* in_pins */ c_reg_addr = &(bpp_p->bpp_regs_p->in_pins); if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) { bpp_print(LOG_WARNING, "ck_bpp_registers: peek failed in_pins, address %x\n", c_reg_addr); return (1); } else bpp_print(LOG_DEBUG, "in_pins contains %x\n", c_reg_contents); bpp_print(LOG_INFO, "Leaving check_bpp_registers, unit %d.\n", unit_no); return (0); } static void bpp_print_regs (unit_no, msg) u_int unit_no; char *msg; { register struct bpp_regs *bpp_regs_p; register struct bpp_unit *bpp_p; /* will point to this */ /* unit's state struct */ bpp_p = bpp_units[unit_no]; bpp_regs_p = bpp_p->bpp_regs_p; bpp_print(LOG_INFO, "%s, dma_csr = 0x%x, dma_addr = 0x%x.\n", msg, bpp_regs_p->dma_csr, bpp_regs_p->dma_addr); bpp_print(LOG_INFO, "dma_bcnt = %d, hw_config = 0x%x.\n", bpp_regs_p->dma_bcnt, bpp_regs_p->hw_config); bpp_print(LOG_INFO, "op_config = 0x%x, data = 0x%x.\n", bpp_regs_p->op_config, bpp_regs_p->data); bpp_print(LOG_INFO, "trans_cntl = 0x%x, out_pins = 0x%x.\n", bpp_regs_p->trans_cntl, bpp_regs_p->out_pins); bpp_print(LOG_INFO, "in_pins = 0x%x, int_cntl = 0x%x.\n", bpp_regs_p->in_pins, bpp_regs_p->int_cntl); } /* VARARGS */ static void bpp_print(level, fmt, a, b, c, d, e, f, g, h) int level; char *fmt; int a, b, c, d, e, f, g, h; { if (level <= bpp_printlevel) { log(level, fmt, a, b, c, d, e, f, g, h); } } #ifdef NO_BPP_HW #include "bpp_test_code.c" #endif NO_BPP_HW