/* astro.c -- Sunos device driver for the SDSU SBus Interface Card. */ /* Development notes: Writes to device registers may be buffered by the system and this may affect synchronization. For this driver, the BCR and AR registers can be read without side effects so reading the register after a write will verify the operation. The CSR may pose further problems. VERSION 2.0 1/12/96 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "astro_impl.h" #include "astro_io.h" #ifdef ASTRO_TRACK # define ASTRO_DEBUG #endif /* Minor device number encoding */ #define ASTRO_UNIT(dev) minor(dev) extern printf() ; #ifdef ASTRO_DEBUG # define ASTRO_ERROR_MSG log # define ASTRO_DEBUG_MSG log #ifdef ASTRO_TRACK # define ASTRO_TRACK_MSG log #endif ASTRO_TRACK #endif ASTRO_DEBUG /* Prototypes for main entry points */ int astro_open (); int astro_close (); int astro_read (); int astro_write (); int astro_ioctl (); int astro_vdcmd (); /* Prototypes for functions pointed to by the dev_ops structure. */ int astro_identify (); int astro_attach (); struct astro_state *asp = NULL; u_char nastros = 1 ; u_char instance = 0; int logprie = LOG_ERR; int logprid = LOG_INFO; int logprit = LOG_DEBUG; void START_CRITICAL() ; void END_CRITICAL() ; void see_devinfo() ; void astro_decommission() ; char* get_property() ; int astro_identify() ; static struct dev_ops astro_ops = { 1, astro_identify, astro_attach, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* other values in astro_ops are filled in later */ /* Prototypes for other functions */ u_int astro_intr (); int astro_strategy (); void astro_dma_stop (); void astro_dma_chain (); void astro_dma_reset (); int astro_timeout (); int astro_identify (name) char *name; { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Entering astro_identify\n"); #endif if (strcmp (name, prom_id_string_one) == 0) return (1); else if (strcmp (name, prom_id_string_two) == 0) return (1); else if (strcmp (name, prom_id_string_three) == 0) return (1); else { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Leaving astro_identify: REJECTED\n"); #endif return (0); } } /* astro_attach(kdevinfo_p) Actions performed are: 1. Maps the device registers into kernal virtual memory 2. Puts the driver on any needed interrupt service chains 3. Turns on interrupts, if desired 4. Initializes the device 5. Allocates the unit structure */ int astro_attach(kdevinfo_p) register struct dev_info *kdevinfo_p ; { struct dev_reg *dev_reg_p ; struct dev_intr *dev_intr_p ; u_int counter, unit_no, dsize ; int spl, astro_ip, t, j, value; long status; struct image_buf *ip; /* The static variable unit_count is the source of unit numbers to assign to the devinfo structure. It is the responsibility of the attach routine to fill in the value of kdevinfo_p->devi_unit */ static int unit_count = 0 ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_attach, kdevinfo_p: 0x%x \n", kdevinfo_p); #endif if (kdevinfo_p == NULL) { /* Should this unlikely event occur, 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 */ #ifdef ASTRO_TRACK_MSG ASTRO_ERROR_MSG(logprie, "astro_attach: kdevinfo_p is NULL! \n") ; #endif goto ATTACH_FAILED ; } /* Allocate zeroed memory for the unit structure */ if (asp == NULL) { asp = (struct astro_state *)kmem_zalloc((u_int)(nastros * sizeof (struct astro_state))); } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"asp is 0x%x\n",asp); #endif /* Assign a unit structure and fill in the value of dip->devi_unit */ unit_no = unit_count++ ; kdevinfo_p->devi_unit = instance = unit_no ; /* save the devinfo pointer for decommissioning */ asp->dip = kdevinfo_p ; /* initialize saved_spl */ asp->saved_spl = -1 ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"asp->saved_spl IS 0x%x\n",asp->saved_spl); #endif /* look at the properties in the devinfo structure. Demonstrate how to get other properties by getting the value of the name property the hard way */ see_devinfo(kdevinfo_p) ; get_property(kdevinfo_p, "name", "NO NAME PROPERTY?") ; /* Map in the any device registers. If the register address space is in several areas, this would be an array */ dev_reg_p = kdevinfo_p->devi_reg ; /* dev_reg_p may be null, but only if devi_nreg is zero */ for (counter = 1; counter <= kdevinfo_p->devi_nreg; ++counter,++dev_reg_p) { if (dev_reg_p == NULL) { /* not very likely */ #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: dev_reg_p is NULL! \n") ; #endif goto ATTACH_FAILED ; } /* The following assumes that only one set of contiguous addresses is required for the device registers. If this is not the case, modify the code to handle an array of dev_reg structures. */ switch(counter) { case 1: /* first register */ /* Check that the size of the register set is the same as the size of the structure that overlays it */ dsize = sizeof(struct astro_regs) ; /* set for the word width address size */ dev_reg_p->reg_size = 0x20; for (t=0; t<10000; t++) t=t ; /* kill some time */ if (dsize != dev_reg_p->reg_size) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"Struct (%d bytes) != reg (%d bytes) !\n", dsize , dev_reg_p->reg_size) ; #endif goto ATTACH_FAILED ; } /* map the structure into kernel virtual space */ asp->regp = (struct astro_regs *)map_regs(dev_reg_p->reg_addr, dsize, dev_reg_p->reg_bustype) ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"regp is 0x%x\n", asp->regp); #endif if (asp->regp == NULL) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: map regs failed\n") ; #endif goto ATTACH_FAILED ; } break ; /* end case 1 */ case 2: /* DChan register */ dsize = sizeof(struct Dchan_regs) ; if (dsize != dev_reg_p->reg_size) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"Struct (%d bytes) != reg (%d bytes) !\n", dsize , dev_reg_p->reg_size) ; #endif goto ATTACH_FAILED ; } /* map the structure into kernel virtual space */ asp->dchan = (struct Dchan_regs *)map_regs(dev_reg_p->reg_addr, dsize, dev_reg_p->reg_bustype); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "dchan is 0x%x\n", asp->dchan); #endif if (asp->dchan == NULL) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: map regs failed\n") ; #endif goto ATTACH_FAILED ; } break ; /* end case 2 */ case 3: /* EChan register */ dsize = sizeof(struct Echan_regs) ; if (dsize != dev_reg_p->reg_size) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"Struct (%d bytes) != reg (%d bytes) !\n", dsize , dev_reg_p->reg_size) ; #endif goto ATTACH_FAILED ; } /* map the structure into kernel virtual space */ asp->echan = (struct Echan_regs *)map_regs(dev_reg_p->reg_addr, dsize, dev_reg_p->reg_bustype); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "echan is 0x%x\n", asp->echan); #endif if (asp->echan == NULL) { #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: map regs failed\n") ; #endif goto ATTACH_FAILED ; } break ; /* end case 3 */ default: #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: too many registers (#%d) \n", counter) ; #endif goto ATTACH_FAILED ; } /* end switch */ } /* end for counter == 1 etc. */ /* Install the interrupt vectors, if any */ if (kdevinfo_p->devi_nintr == 0) goto SKIP_INTR_INSTALLATION ; /* For devices that issue interrupts the driver installs itself on the interrupt chain of each interrupt level that the hardware uses */ dev_intr_p = kdevinfo_p->devi_intr ; if (dev_intr_p == NULL) { /* not very likely */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"dev_intr_p is NULL!\n"); #endif #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie,"astro_attach: dev_intr_p is NULL! \n") ; #endif goto ATTACH_FAILED; } /* dev_intr_p may be null, but only if devi_nintr is zero */ for (counter = 1; counter <= kdevinfo_p->devi_nintr; ++counter, ++dev_intr_p) { /* Convert the hardware interrupt priority level (ipl, 0-15) into an spl for critical sections of code. The routine ipltospl returns a value that is properly shifted for direct insertion into the psr. If there is more than one interrupt level, use the highest one. Note that each unit may use a different level */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "dev_intr_p is 0x%xN\n",dev_intr_p); ASTRO_TRACK_MSG(logprit, "astro_attach: dev_intr_p->int_pri = 0x%x\n", dev_intr_p->int_pri); #endif spl = ipltospl(0x3d); asp->interrupt_pri_crit = spl ; spl = ipltospl(dev_intr_p->int_pri) ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "astro_attach: spl = 0x%x\n", spl); #endif astro_ip = asp->interrupt_pri ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "astro_attach: astro_ip = 0x%x\n", astro_ip); #endif if (spl > astro_ip) { asp->interrupt_pri = spl ; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "Set asp->interrupt_pri to hex 0x%x for unit %d. \n", asp->interrupt_pri, unit_no) ; #endif } /* if the device can do DVMA then the DVMA routines need to know the highest interrupt level at which the device can interrupt. The routine adddma is used to gather this info */ adddma(astro_ip) ; START_CRITICAL(unit_no) ; /* addintr() does the registration. Duplicate registrations (same level from several units) is ignored except for data collection for vmstat. addintr() panics if this registration would make too many devices on the interrupt chain. So it goes */ addintr(dev_intr_p->int_pri, astro_intr, kdevinfo_p->devi_name, kdevinfo_p->devi_unit) ; END_CRITICAL(unit_no) ; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "Installed astro_intr at SBus int pri level hex %x\n", dev_intr_p->int_pri) ; #endif } /* end for counter == 1 etc. */ SKIP_INTR_INSTALLATION: /* The routine report_dev() writes a line at the console and system log to announce the attachment of the driver. This will happen at modload time, one line for each unit. Call report_dev() before contacting the device so the message is there if the system freezes at this point */ report_dev(kdevinfo_p) ; /* Perform device initialization. Generally, interrupts must be turned on at this point. */ status = asp->regp->csr; while (status & dmac_draining) status = asp->regp->csr; asp->regp->csr = status & ~dmac_en_dma; status = asp->regp->csr; asp->regp->csr = dmac_reset; readback = asp->regp->csr; asp->regp->unused = 0x0; readback = asp->regp->csr; asp->regp->ww = 0x0; /* Setup image buffers */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_attach: setting up image buffers\n"); #endif for (j = 0; j < 3; j++) { asp->image_buf[j].address = (caddr_t) -1; } for (j = 0; j < 3; j++) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_attach: kmem_zalloc of image buffers\n"); #endif ip = &asp->image_buf[j]; ip->address = (caddr_t)kmem_zalloc(image_buf_size); if (ip->address == NULL) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_attach: allocation of buffers failed\n"); #endif value = EIO; goto ATTACH_FAILED; } } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"ATTACH SUCCEEDED\n") ; #endif /* Add other device csr initialization here */ return(0) ; ATTACH_FAILED: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "ATTACH FAILED\n") ; #endif asp->open_inhibit = 1 ; /* prevent open */ return(-1) ; } /* end astro_attach() */ /* LOADABLE DEVICE DRIVER SUPPORT */ extern int nulldev(); extern int seltrue(); /* Block device entry points for this driver. These are in bdevsw[] in sun/conf.c for preconfigured drivers.*/ #ifdef NOT_IN_CHARACTER_ONLY_DRIVERS struct bdevsw astro_bdevsw = { astro_open, astro_close, astro_strategy, astro_dump, astro_size, 0 }; #endif NOT_IN_CHARACTER_ONLY_DRIVERS /* Character device entry points for this driver. These are in cdevsw[] in sun/conf.c for preconfigured drivers.*/ struct cdevsw astro_cdevsw = { astro_open, astro_close, astro_read, astro_write, astro_ioctl, nulldev, seltrue, 0, 0 }; /* The vd driver is a non-loadable pseudo driver that loads loadable modules. The following initialized structure is used by the vd driver to locate entry points and data when loading and configuring the loadable driver. The fields are explained below. VDMAGIC_DRV is the magic number in the Drv_magic field that tells the vd driver that this loadable module is a device driver. If there was no physical SBus device associated with this driver, then VDMAGIC_PSEUDO would be used to indicate that this is a pseudo driver. Drv_name is a pointer to a string which is the name of the driver. Drv_dev_ops is a pointer to the dev_ops structure defined in sun/openprom.h which contains all of the entry points to driver routines which are known outside of the driver. The driver is responsible for having the dev_ops structure initialized. Address of Drv_bdevsw can be NULL if there is no block device for this driver. Address of Drv_cdevsw can be NULL if there is no character device for this driver. If the driver has a preconfigured location in the bedvsw or cdevsw structures, then that location index for the bdevsw goes in Drv_blockmajor, and that location index for the cdevsw goes in Drv_charmajor. This is the case for ll devices that are already supported by sun/conf.c. For drivers that do not have a preconfigured location in the bdevsw, Drv_vlockmajor should be 0, and the system will choose a major device number (location index) when the module is loaded by the vd driver. For drivers that do not have a preconfigured location in the cdevsw, Drv_charmajor should be 0, and the system will choose a major device number (location index) when the module is loaded by the vd driver.*/ struct vdldrv astro_drv = { VDMAGIC_DRV, /* Drv_magic */ "astro\0", /* *Drv_name */ NULL, /* *Drv_dev_ops init to &astro_ops */ NULL, /* *Drv_bdevsw */ NULL, /* *Drv_cdevsw init to &astro_cdevsw */ 0, /* Drv_blockmajor */ 0 /* Drv_charmajor */ }; /* astro_vdcmd(command, vdp, vdi, vds) Support for a loadable device driver. Required by the utilities modload modstat & modunload. The vd driver is a non-loadable pseudo driver that loads loadable modules. The vd driver is used by modload modstat & modunload. All aloadable modules are required to supply a routine that is called by the vd driver when a module is loaded, unloaded, or the object of a VDSTAT status request ioctl on the vd driver. On the modload man page this routine is referred to as the entry point. Note that the default entry point name "xxxinit()" should never be used, as the names will collide if two or more drivers use this name. Inputs: command - Identifies why this module was called. During loading (command == VDLOAD) the driver should do any initialization that is wants to do, get any desired config- uration information that may be available from modload, and then fill in the linkage pointer as shown below. Before unloading (command == VDUNLOAD) the driver is called to determine if it is OK to be unloaded, and to allow the driver to do any cleanup that it needs to do. During a status request (command == VDSTAT) the driver is called to fill in any type specific information into vds_modinfo. vdp - Is a pointer to the vddrv structure. This is where the vd driver (think of it as the kernel) collects all information about a loaded module. The driver being loaded will fill in vddrv.vdd_vdtab, which is a pointer to the vdldrv structure initialized above. Since this pointer is not passed to the kernel until the VDLOAD command is handled by this interface routine, these initializations need not be static. vdi - Is a pointer to one of the following structures: If the command is VDLOAD: The vdioctl_load structure contains configuration information if any was passed in from modload. If the command is VDUNLOAD: The vdioctl_unload structure contains only the module id. If the command is VDSTAT: The vdioctl_stat structure contains hooks to allow you to build an elaborate status function if you wish. vds - Is a pointer to a status structure that may be filled in when the command is VDSTAT. This pointer is NULL when command has a value other than VDSTAT. For more information see /usr/include/sun/vddrv.h. Return: 0 on success, errno value on failure. */ int astro_vdcmd(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; int status = 0; astro_drv.Drv_dev_ops = &astro_ops ; astro_drv.Drv_cdevsw = &astro_cdevsw ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"-------------(%d)----------------- \n", vdp->vdd_id); ASTRO_TRACK_MSG(logprit,"Entering astro_vdcmd, vdp 0x%x, vdi 0x%x,\ vds 0x%x.\n", vdp, vdi, vds); #endif switch (command) { case VDLOAD: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Command is VDLOAD.\n"); #endif /* 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 configruation information that may have 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 its 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 astro driver. It is included for illustration. */ if (vdi !=NULL) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "vdi_id: %d\n", vdi->vdi_id); ASTRO_TRACK_MSG(logprit, "vdi_status: %d\n", vdi->vdi_status); ASTRO_TRACK_MSG(logprit, "vdi_mmapaddr: 0x%x\n", vdi->vdi_mmapaddr); ASTRO_TRACK_MSG(logprit, "vdi_symtabsize: %d\n", vdi->vdi_symtabsize); ASTRO_TRACK_MSG(logprit, "vdi_userconf: 0x%x\n", vdi->vdi_userconf); #endif if (vdi->vdi_userconf != NULL) { vdconf_p = vdi->vdi_userconf; if (vdconf_p->vdc_type == VDCEND) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"No config info available.\n"); #endif } 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: #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "vdconf\ switch error!\n"); #endif string_p = "(SWITCH ERROR!)"; break; } /* end switch() */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "vdc_type: %d %s %s 0x%x\n", vdconf_p->vdc_type, string_p, "vdc_data:", vdconf_p->vdc_data); #endif /*-- Move to next element of array*/ vdconf_p++; } /* end while (vdconf_p->vdc_type != VDCEND) */ } /* end if (vdi->vdi_userconf != NULL) */ else { #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "No config info,\ vdi_userconf is NULL\n"); #endif } } /* end if (vdi !=NULL) */ if (vdi == NULL) { #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "No config info, vdi is NULL\n"); #endif } /* Configure the astro 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 *)&astro_drv; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Done VDLOAD.\n"); #endif break; /* end case VDLOAD: */ case VDUNLOAD: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Command is VDUNLOAD.\n"); #endif /* 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 */ /* Inhibit further opens on the unit */ asp->open_inhibit = 1; /* If unit is open, cannot unload */ if (asp->open !=0) status = EBUSY; /* If it looks good so far, turn off interrupts, remove registration of interrupt vector, release all memory, and permit unload */ if (status == 0) (void) astro_decommission(); else { /* Release the open inhibits */ asp->open_inhibit = 0; } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Done VDUNLOAD.\n"); #endif break; /* end case VDUNLOAD: */ case VDSTAT: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Command is VDSTAT.\n"); ASTRO_TRACK_MSG(logprit, "nastros is 0x%x\n", nastros) ; ASTRO_TRACK_MSG(logprit, "asp is 0x%x \n", asp ) ; ASTRO_TRACK_MSG(logprit, "Done VDSTAT.\n"); #endif break; /* end case VDSTAT: */ default: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "astro_vdcmd: unknown command 0x%x\n",command); #endif status = EINVAL; break; /* end case default */ } /* end switch (command) */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Leaving astro_vdcmd, status (errno): %d.\n", status); #endif return (status); } /* START_CRITICAL(unit_no) Protect a critical section of code from interruption by the unit whose data structures are currently being serviced. In ASTRO_DEBUG mode, assertion checking is done to assure the proper pairing of START_CRITICAL() and END_CRITICAL() calls. */ void START_CRITICAL(unit_no) register u_int unit_no; { register int temp_saved_spl; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "Entering START_CRITICAL: unit %d\n", unit_no); #endif temp_saved_spl = splr(asp->interrupt_pri_crit); #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"START_CRITICAL: asp->interrupt_pri = %d\n", asp->interrupt_pri); ASTRO_DEBUG_MSG(logprid,"START_CRITICAL: temp_saved_spl = %d\n", temp_saved_spl); #endif #ifdef ASTRO_DEBUG if (asp->saved_spl != (-1)) { #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "START_CRITICAL: ASSERT failed,\ saved_spl: %d\n", asp->saved_spl); #endif } #endif ASTRO_DEBUG asp->saved_spl = temp_saved_spl; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"Leaving START_CRITICAL: unit %d.\n", unit_no); #endif return; } /* END_CRITICAL(unit_no) End the protection of code from interruption by the unit whose data structures are currently being serviced. In ASTRO_DEBUG mode, assertion checking is done to assure the proper pairing of START_CRITICAL() and END_CRITICAL calls. */ void END_CRITICAL(unit_no) register u_int unit_no; { register int temp_saved_spl; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"Entering END_CRITICAL: unit %d\n", unit_no); #endif temp_saved_spl = asp->saved_spl; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"END_CRITICAL: asp->saved_spl = %d\n", asp->saved_spl); #endif if (asp->saved_spl == (-1)) { #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"END_CRITICAL: ASSERT failed, saved_spl: %d\n", asp->saved_spl); #endif } asp->saved_spl = (-1); splx(temp_saved_spl); return; } /* see_devinfo(dip) Show interesting information stored in this node of the devinfo tree for developing and debugging. Input: dip Pointer to a node of the tree. */ void see_devinfo(dip) register struct dev_info *dip; { struct dev_reg *dev_reg_p; struct dev_intr *dev_intr_p; u_int counter; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering see_devinfo: dip 0x%x\n",dip); #endif #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid,"dip->devi_name: %s\n",dip->devi_name); ASTRO_DEBUG_MSG(logprid,"dip->devi_unit: %d\n",dip->devi_unit); ASTRO_DEBUG_MSG(logprid,"dip->devi_nreg: %d\n",dip->devi_nreg); ASTRO_DEBUG_MSG(logprid,"dip->devi_reg: 0x%x\n",dip->devi_reg); #endif dev_reg_p = dip->devi_reg; /* dev_reg_p may be NULL, but only if devi_nreg is zero */ for (counter = 1; counter <= dip->devi_nreg; ++counter, ++dev_reg_p) { if (dev_reg_p == NULL) break; #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_bustype:\ 0x%x\n", counter, dev_reg_p->reg_bustype); ASTRO_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_addr:\ 0x%x\n", counter, dev_reg_p->reg_addr); ASTRO_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_size:\ 0x%x\n", counter, dev_reg_p->reg_size); #endif } #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "dip->devi_nintr: %d\n",dip->devi_nintr); ASTRO_DEBUG_MSG(logprid, "dip->devi_intr: %d\n",dip->devi_intr); #endif /* Show all the interrupt data */ dev_intr_p = dip->devi_intr; /* dev_intr_p may be NULL, but only if devi_nintr is zero */ for (counter = 1; counter <= dip->devi_nintr; ++counter, ++dev_intr_p) { if (dev_intr_p == NULL) { break; } #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "INTERRUPT #%d: devi_intr->int_pri: 0x%x\n", counter, dev_intr_p->int_pri); ASTRO_DEBUG_MSG(logprid, "INTERRUPT #%d: devi_intr->int_vec: 0x%x\n", counter, dev_intr_p->int_vec); #endif } #ifdef ASTRO_DEBUG_MSG ASTRO_DEBUG_MSG(logprid, "dip->devi_data: 0x%x\n",dip->devi_data); ASTRO_DEBUG_MSG(logprid, "dip->devi_nodeid: 0x%x\n",dip->devi_nodeid); ASTRO_DEBUG_MSG(logprid, "Leaving see_devinfo: dip 0x%x\n", dip); #endif return; } /* astro_decomission() Turn off interrupts, remove registration of all interrupt vectors, and relaease 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. */ void astro_decommission() { struct dev_intr *dev_intr_p; int t, j, counter; struct image_buf *ip; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Entering astro_decommission.\n"); ASTRO_TRACK_MSG(logprit, "nastros is 0x%x, asp is 0x%x\n", nastros, asp); #endif if (nastros == 0) goto SKIP_DECOMM ; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Dmacsr is 0x%x \n", asp->regp->csr) ; #endif for (t=0; t<10000; t++) t=t ; /* kill some time */ asp->regp->csr = dmac_reset; asp->regp->csr = 0; /* 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 ingnored, duplicate unregistrations cannot be considered an error. March through and do everything meticulously. Do not take short cuts or you may die */ START_CRITICAL(instance); for (j = 0; j < 3; j++){ ip = &asp->image_buf[j]; if ((ip->address != (caddr_t) -1) && (ip->address != NULL)) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_decommission: kmem_free of buffer %d\n", j+1); #endif kmem_free((caddr_t)ip->address, (u_int)image_buf_size); } } dev_intr_p = asp->dip->devi_intr; for (counter = asp->dip->devi_nintr; counter>0; --counter, ++dev_intr_p){ if (dev_intr_p == NULL) { /* shouldn't happen */ #ifdef ASTRO_ERROR_MSG ASTRO_ERROR_MSG(logprie, "astro_decommission ASSERT FAILED.\n"); #endif break; } (void) remintr(dev_intr_p->int_pri, astro_intr); } END_CRITICAL(instance); /*-- Release the memory allocated for the unit structure*/ if ((asp != NULL) && (nastros >0)) { (void) kmem_free ((caddr_t)asp,(u_long)(nastros * sizeof (struct astro_state))); asp = NULL; } SKIP_DECOMM: #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit, "Leaving astro_decommission.\n"); #endif return; } /* get_property(dip, propname, defaultvalue) 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. The kernel support routine getlongprop() allows drivers to retrieve the value of a property for arbitrary sized properties. A pointer to the value of 'name' in the property list for 'devi' is returned. If no value is found, NULL is returned. The space is allocated using kmem_zalloc, so it is assumed that this routine is NOT called from an interrupt routine. If getlongprop() is called often, you should consider recycling the space with kmem_free(). */ char * get_property(dip, propname, defaultvalue) struct dev_info *dip; char *propname; char *defaultvalue; { char *ret_getlongprop; char *propval; char *msg_string; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering get_property: dip: 0x%x\n",dip); #endif ret_getlongprop = (char *) getlongprop(dip->devi_nodeid, propname); if (ret_getlongprop == NULL) { propval = defaultvalue; msg_string = "default"; } else { propval = ret_getlongprop; msg_string = "from prom"; } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving get_property: %s=%s (%s)\n", propname, propval, msg_string); #endif } /*=================================================================================*/ /* Main entry points */ int astro_open (dev, flag) dev_t dev; int flag; { u_char unit_no; unit_no = ASTRO_UNIT(dev); if (asp == NULL) return ENXIO; START_CRITICAL(unit_no); asp->open++; astro_dma_reset(asp); readback = asp->regp->csr; asp->regp->ww = 0; readback = asp->regp->csr; /* flush to device */ END_CRITICAL(unit_no); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_open\n"); #endif return 0; } int astro_close (dev, flag) dev_t dev; int flag; { u_char unit_no; unit_no = ASTRO_UNIT(dev); if (asp == NULL) return ENXIO; START_CRITICAL(unit_no); asp->open = 0; astro_dma_reset(asp); readback = asp->regp->csr; asp->regp->ww = 0; readback = asp->regp->csr; END_CRITICAL(unit_no); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_close\n"); #endif return 0; } /* Read uses the DMA controller next transfer feature, so that during a transfer, the next byte and next address registers are used for >64K transfers. */ static int astro_read (dev, uiop) dev_t dev; struct uio *uiop; { struct image_buf *ip; int value = 0, j, count = 0; u_int byteremain; u_long time1, time2; u_long bcr, csr; u_char unit_no; int read_priority = 0; int chain_one = 0; int counter = 0; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_read\n"); #endif instance = unit_no = ASTRO_UNIT(dev); if (asp == NULL) return ENXIO; START_CRITICAL(unit_no); while (asp->busy) { asp->want = 1; } asp->busy = 1; END_CRITICAL(unit_no); /* Setup image buffers */ #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: setting up image buffers\n"); #endif for (j = 0; j < 3; j++) { asp->image_buf[j].mapalloc_address = (caddr_t) -1; asp->image_buf[j].dvma_address = (caddr_t) -1; } for (j = 0; j < 3; j++) { ip = &asp->image_buf[j]; ip->operation = B_READ; ip->state = image_buf_waiting; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: mb_nbmapalloc of image buffers\n"); #endif ip->mapalloc_address = (caddr_t)mb_nbmapalloc(dvmamap, (caddr_t)ip->address, image_buf_size, 0, ((int (*) ())0), (caddr_t)0); if (ip->mapalloc_address == NULL) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: allocation of buffers failed\n"); #endif value = ENOMEM; goto EXITREAD; } ip->dvma_address = (caddr_t) (MBI_ADDR(ip->mapalloc_address) + DVMA); if (ip->dvma_address == NULL) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: allocation of buffers failed\n"); #endif value = ENOMEM; goto EXITREAD; } } asp->buf_numbusy = 0; asp->buf_numwaiting = 3; asp->buf_done = asp->buf_ready = asp->buf_busy = asp->buf_waiting = 0; byteremain = uiop->uio_iov->iov_len; if(byteremain == 8) tdl_flag = 1; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: byteremain = %d\n", byteremain); ASTRO_TRACK_MSG(logprit,"astro_read: entering 1st while loop\n"); #endif while ((byteremain > 0) || (asp->buf_numwaiting < 3)) { count = 0; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: byteremain = %d\n", byteremain); #endif if ((byteremain > 0) && (asp->buf_numwaiting > 0)) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: byteremain>0 && asp->buf_numwaiting>0\n"); #endif ip = &asp->image_buf[asp->buf_waiting]; if (ip->state != image_buf_waiting) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: ip->state != image_buf_waiting\n"); #endif value = ECHILD; START_CRITICAL(unit_no); astro_dma_stop(asp); END_CRITICAL(unit_no); goto EXITREAD; } if (byteremain > image_buf_size) ip->size = image_buf_size; else ip->size = byteremain; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: ip->size = %d\n", ip->size); #endif byteremain -= ip->size; ip->state = image_buf_ready; asp->buf_numwaiting--; asp->buf_waiting++; if (asp->buf_waiting == 3) asp->buf_waiting = 0; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: calling astro_dma_chain\n"); #endif if (chain_one < 2) { astro_dma_chain(asp); chain_one++; } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: returning from astro_dma_chain\n"); #endif } else if (asp->buf_numwaiting < 3) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: asp->buf_numwaiting < 3\n"); #endif ip = &asp->image_buf[asp->buf_done]; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: entering 2nd while loop\n"); #endif if(tdl_flag == 1) { while ((ip->state != image_buf_done) && (ip->state != image_buf_error)) { if(counter == 0) timeout(astro_timeout, asp, hz*6); counter++; } } else { while ((ip->state != image_buf_done) && (ip->state != image_buf_error)) { timeout(astro_timeout, asp, hz*6); read_priority = getpriority(0,0); sleep((caddr_t)&asp->sleep_wake, read_priority); } } if (ip->state == image_buf_error) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: ip->state == image_buf_error\n"); #endif switch(ip->det_state) { case device_not_busy: value = ENOTBLK; break; case image_buf_not_busy: value = EDEADLK; break; case data_trans_error: value = ENOLCK; break; case bad_interrupt: value = ENOTEMPTY; break; case timed_out: value = ETIME; } astro_dma_stop(asp); goto EXITREAD; } if (ip->state != image_buf_done) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: ip->state != image_buf_done\n"); #endif value = EPERM; astro_dma_stop(asp); goto EXITREAD; } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: moving data to user space\n"); #endif value = uiomove((caddr_t)ip->address, (int)ip->size, UIO_READ, uiop); if (value != 0) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: copy error to user space\n"); #endif START_CRITICAL(unit_no); astro_dma_stop(asp); END_CRITICAL(unit_no); value = EPIPE; goto EXITREAD; } #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: end of moving data to user space\n"); #endif ip->state = image_buf_waiting; asp->buf_numwaiting++; asp->buf_done++; if (asp->buf_done == 3) asp->buf_done = 0; } } EXITREAD: START_CRITICAL(unit_no); for (j = 0; j < 3; j++){ ip = &asp->image_buf[j]; if ((ip->mapalloc_address != (caddr_t) -1) && (ip->mapalloc_address != NULL)) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_read: mb_mapfree of buffer %d\n", j+1); #endif (void) mb_mapfree(dvmamap, &ip->mapalloc_address); } } asp->busy = 0; END_CRITICAL(unit_no); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_read\n"); #endif tdl_flag = 0; return value; } /* astro_write is not used at this time. */ static int astro_write (dev, uiop) dev_t dev; struct uio *uiop; { u_char unit_no; unit_no = ASTRO_UNIT(dev); if (asp == NULL) return ENXIO; return 0; } /* Utility functions */ #ifdef __GNUC__ inline #endif static u_long extract_control_command (cmd) int cmd; { return (cmd & 0x80000000) ? ((cmd & 0x00ff) | 0x0100) : (cmd & 0x00ff); } #ifdef __GNUC__ inline #endif static u_long extract_byte_count (cmd) int cmd; { return (cmd >> 16) & 0xff; } /* astro_ioctl */ static int astro_ioctl (dev, cmd, arg, mode) dev_t dev; int cmd; caddr_t arg; int mode; { int retval = 0; u_int iocmd; u_char unit_no; u_long *data_buf, astroreg; u_short *short_data_buf; data_buf = (u_long *)arg; short_data_buf = (u_short *)arg; unit_no = ASTRO_UNIT(dev); if (asp == NULL) return ENXIO; START_CRITICAL(unit_no); /* extract control command from cmd */ iocmd = (u_int)extract_control_command (cmd); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_ioctl: iocmd = 0x%x\n", iocmd); #endif switch (iocmd) { case ASTRO_GET_CSR: { astroreg = asp->regp->csr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: csr = 0x%x\n", astroreg); #endif *data_buf = astroreg; } break; case ASTRO_GET_AR: { astroreg = asp->regp->ar; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: ar = 0x%x\n", astroreg); #endif *data_buf = astroreg; } break; case ASTRO_GET_BCR: { astroreg = asp->regp->bcr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: bcr = 0x%x\n", astroreg); #endif *data_buf = astroreg; } break; case ASTRO_SET_CSR: { astroreg = *data_buf; readback = asp->regp->csr; asp->regp->csr = astroreg; /* make sure register write gets flushed to device */ readback = asp->regp->csr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: csr set to 0x%x\n", astroreg); #endif } break; case ASTRO_SET_AR: { astroreg = *data_buf; readback = asp->regp->csr; asp->regp->ar = astroreg; /* make sure register write gets flushed to device */ readback = asp->regp->csr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: ar set to 0x%x\n", astroreg); #endif } break; case ASTRO_SET_BCR: { astroreg = *data_buf; readback = asp->regp->csr; asp->regp->bcr = astroreg; /* make sure register write gets flushed to device */ readback = asp->regp->csr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: bcr set to 0x%x\n", astroreg); #endif } break; case ASTRO_WRITE_UNUSED_REG: { u_long bytes = extract_byte_count (cmd); u_int i; for (i=0; i<(bytes >> 2); i++) { astroreg = *(data_buf + i); readback = asp->regp->unused; asp->regp->unused = astroreg; /* make sure register write is flushed to device */ readback = asp->regp->csr; DELAY(50); } } break; case ASTRO_SET_WW: { astroreg = *data_buf; readback = asp->regp->csr; asp->regp->ww = astroreg; /* make sure register write gets flushed to device */ readback = asp->regp->csr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_ioctl: word width set to 0x%x\n", astroreg); #endif } break; case ASTRO_WRITE_DCHAN: { u_long bytes = extract_byte_count (cmd); u_int i; for (i=0; i<(bytes >> 2); i++) { readback = *(asp->dchan->dr); *(asp->dchan->dr) = *(data_buf + i); /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_WRITE_ECHAN: { u_long bytes = extract_byte_count (cmd); u_int i; for (i=0; i<(bytes >> 1); i++) { readback = *(asp->echan->er); *(asp->echan->er + 6) = *(short_data_buf + i); /* make sure register write gets flushed to device */ readback = asp->regp->csr; } } break; case ASTRO_RESET: astro_dma_reset(asp); break; default: { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Error in astro_ioctl switch!\n"); #endif retval = EINVAL; } break; } END_CRITICAL(unit_no); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_ioctl\n"); #endif return retval; } /* Other functions */ /* Reset DMA controller */ static void astro_dma_reset(asp) struct astro_state *asp; { readback = asp->regp->csr; asp->regp->csr = (dmac_reset | dmac_write); readback = asp->regp->csr; asp->regp->csr = 0; readback = asp->regp->csr; asp->regp->csr = dmac_flush; readback = asp->regp->csr; /* flush to device */ } /* Stop a DMA transfer */ static void astro_dma_stop(asp) struct astro_state *asp; { int j; astro_dma_reset(asp); for (j = 0; j < 3; j++) asp->image_buf[j].state = image_buf_waiting; } /* Start/continue DMA chaining */ static void astro_dma_chain (asp) struct astro_state *asp; { struct image_buf *ip; u_long csr; u_long ar; u_long bcr; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_dma_chain\n"); #endif START_CRITICAL(instance); if (!asp->busy) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_dma_chain: not marked busy\n"); #endif END_CRITICAL(instance); return; } ip = &asp->image_buf[asp->buf_ready]; if ((ip->state == image_buf_ready) && (asp->buf_numbusy < 2)) { csr = asp->regp->csr; if (csr & dmac_na_loaded) { END_CRITICAL(instance); return; } asp->buf_numbusy++; ip->state = image_buf_busy; csr = (dmac_en_next|dmac_en_dma|dmac_faster|dmac_en_cnt|dmac_int_en); if (ip->operation == B_READ) csr |= dmac_write; readback = asp->regp->csr; asp->regp->csr = csr; readback = asp->regp->csr; /* flush to device */ ar = (u_long)ip->dvma_address; bcr = ip->size; readback = asp->regp->csr; asp->regp->bcr = bcr; readback = asp->regp->csr; asp->regp->ar = ar; readback = asp->regp->csr; asp->buf_ready++; if (asp->buf_ready == 3) asp->buf_ready = 0; } END_CRITICAL(instance); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_dma_chain\n"); #endif } /* Interrupt handler */ static u_int astro_intr (unit_no) u_int unit_no; { register struct image_buf *ip; register u_long csr; int j = 0; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_intr\n"); #endif if (asp == NULL) return ENXIO; START_CRITICAL(unit_no); ip = &asp->image_buf[asp->buf_busy]; csr = asp->regp->csr; if (!(csr & (dmac_err_pend | dmac_int_pend))) { if(tdl_flag == 0) { untimeout(astro_timeout, asp); wakeup((caddr_t)&asp->sleep_wake); } END_CRITICAL(unit_no); return ENXIO; } if (!asp->busy) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_intr: device not busy\n"); #endif ip->det_state = device_not_busy; } else if (csr & dmac_err_pend) { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_intr: data transfer error csr = 0x%x\n", csr); #endif ip->det_state = data_trans_error; } else if (csr & dmac_tc) { if ((asp->buf_numbusy == 0) || (ip->state != image_buf_busy)) { ip->det_state = image_buf_not_busy; goto FLUSH; } if (csr & dmac_write) { while (((csr = asp->regp->csr) & dmac_draining) && (j++ < 30)) { DELAY(1); } } if (!(csr & dmac_en_next)) csr = dmac_flush; readback = asp->regp->csr; asp->regp->csr = csr; readback = asp->regp->csr; /* flush to device */ ip->state = image_buf_done; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_intr: asp->buf_numbusy = %d\n", asp->buf_numbusy); #endif asp->buf_numbusy--; asp->buf_busy++; if (asp->buf_busy == 3) asp->buf_busy = 0; END_CRITICAL(unit_no); instance = unit_no; if(tdl_flag == 0) { astro_dma_chain(asp); untimeout(astro_timeout, asp); wakeup((caddr_t)&asp->sleep_wake); } else untimeout(astro_timeout, asp); return (1); } else { #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"astro_intr: bad interrupt encountered csr = 0x%x\n", csr); #endif ip->det_state = bad_interrupt; } FLUSH: astro_dma_reset(asp); ip->state = image_buf_error; END_CRITICAL(unit_no); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_intr\n"); #endif return (1); } /* function called by timeout() */ int astro_timeout (asp) struct astro_state *asp; { struct image_buf *ip; #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Entering astro_timeout\n"); #endif ip = &asp->image_buf[asp->buf_done]; ip->state = image_buf_error; ip->det_state = timed_out; wakeup((caddr_t)&asp->sleep_wake); #ifdef ASTRO_TRACK_MSG ASTRO_TRACK_MSG(logprit,"Leaving astro_timeout\n"); #endif return(0); }