[Return to Library] [Contents] [Previous Chapter] [Next Chapter] [Index] [Help]


B    TURBOchannel Device Driver Source Listing

This appendix contains the source listing for the /dev/cb device driver. .po -0.5i

/***************************************************
 * cbreg.h   Header file for cb.c 17-Nov-1995      *
 *                                                 *
 ***************************************************/
/***************************************************
 *                                                 *
 *           Copyright (c) 1995 by                 *
 *   Digital Equipment Corporation, Maynard, MA    *
 *            All rights reserved.                 *
 *                                                 *
 * This software is furnished under the terms and  *
 * conditions of the TURBOchannel Technology       *
 * license and may be used and copied only in      *
 * accordance with the terms of such license and   *
 * with the inclusion of the above copyright       *
 * notice.  No title to and ownership of the       *
 * software is hereby transferred.                 *
 *                                                 *
 * The information in this software is subject to  *
 * change without notice and should not be         *
 * construed as a commitment by Digital Equipment  *
 * Corporation.                                    *
 *                                                 *
 * Digital assumes no responsibility for the use   *
 * or reliability of its software on equipment     *
 * which is not supplied by Digital.               *
 ***************************************************/

/*************************************************** * * * Define an offset of registers from base address * * of option; a macro to convert register offset * * to kernel virtual address; and a macro to * * scramble physical address to TC DMA address. * ***************************************************/

#define CB_REL_LOC 0x00040000 #define CB_ADR(n) ((io_handle_t)(n + CB_REL_LOC)) #define CB_SCRAMBLE(x) (((unsigned)x<<3)&~(0x1f))|(((unsigned)x>>29)&0x1f)

/*************************************************** * TURBOchannel test board CSR Enable and Status * * bits * * * ***************************************************/

#define CB_INTERUPT 0x0e00 /* Bits: 8 = 0; 9, 10 & 11 = 1 */ #define CB_CONFLICT 0x0d00 /* Bits: 9 = 0; 8, 10 & 11 = 1 */ #define CB_DMA_RD 0x0b00 /* Bits: 10 = 0; 8, 9 & 11 = 1 */ #define CB_DMA_WR 0x0700 /* Bits: 11 = 0; 8, 9 & 10 = 1 */ #define CB_DMA_DONE 0x0010 /* Use in timeout loop */

/*************************************************** * Define ioctl macros for the cb driver. * * * ***************************************************/

#define CBPIO _IO('v',0) /* Set Read/Write mode to PIO */ #define CBDMA _IO('v',1) /* Set Read/Write mode to DMA */ #define CBINT _IO('v',2) /* Perform Interrupt test */

#define CBROM _IOWR('v',3,int) /* Return specified word */ #define CBCSR _IOR('v',4,int) /* Update & return CSR word */

#define CBINC _IO('v',5) /* Start incrementing lights */ #define CBSTP _IO('v',6) /* Stop incrementing lights */

/*************************************************** * Register offset definitions for a CB device. * * The registers are aligned on longword (32-bit) * * boundaries, even when they are implemented with * * less than 32 bits. * * * ***************************************************/

#define CB_ADDER 0x0 /* 32-bit read/write DMA address register */ #define CB_DATA 0x4 /* 32-bit read/write data register */ #define CB_CSR 0x8 /* 16-bit read/write CSR/LED register */ #define CB_TEST 0xC /* Go bit: Write sets and Read Clears */

/***************************************************
 *                                                 *
 *           Copyright (c) 1995 by                 *
 *   Digital Equipment Corporation, Maynard, MA    *
 *            All rights reserved.                 *
 *                                                 *
 * This software is furnished under the terms and  *
 * conditions of the TURBOchannel Technology       *
 * license and may be used and copied only in      *
 * accordance with the terms of such license and   *
 * with the inclusion of the above copyright       *
 * notice.  No title to and ownership of the       *
 * software is hereby transferred.                 *
 *                                                 *
 * The information in this software is subject to  *
 * change without notice and should not be         *
 * construed as a commitment by Digital Equipment  *
 * Corporation.                                    *
 *                                                 *
 * Digital assumes no responsibility for the use   *
 * or reliability of its software on equipment     *
 * which is not supplied by Digital.               *
 ***************************************************/

/*************************************************** * * * The /dev/cb device driver operates on a * * TURBOchannel (TC) bus. The device it controls * * is called a TURBOchannel test board. The * * TURBOchannel test board is a minimal * * implementation of all TURBOchannel hardware * * functions: * * * * o Programmed I/O (PIO) * * o Direct Memory Access (DMA) read * * o DMA write * * o Input/Output (I/O) read/write conflict * * testing * * * * The software view of the board consists of: * * * * o An EPROM address space * * o A 32-bit ADDRESS register with bits * * scrambled for direct use as a TC * * DMA address * * o A 32-bit DATA register used for * * programmed I/O and as the holding * * register for DMA * * o A 16-bit Light Emitting Diode (LED)/ * * Control Status Register (CSR) * * o A 1-bit TEST register * * * * All registers MUST be accessed as 32-bit * * longwords, even when they are not implemented * * as 32 bits. The CSR contains bits to enable * * option DMA read testing, conflict signal * * testing, I/O interrupt testing, and option * * DMA write testing. It also contains a bit to * * indicate that one or more of the tests are * * enabled, 4-byte mask flag bits, and a DMA * * done bit. * ***************************************************/ /*************************************************** * This example Digital UNIX driver provides a * * simple interface to the TURBOchannel test * * board. It: * * * * (1) Reads from the data register on the * * test board to words in system memory * * (2) Writes to the data register on the test * * board from words in system memory * * (3) Tests the interrupt logic on the * * test board * * (4) Reads one 32-bit word from the test * * board address (ROM/register) space into * * system memory * * (5) Updates, reads, and returns the 32-bit * * CSR value * * (6) Starts and stops clock-driven * * incrementing of the four spare LEDs on * * the board. * * * * ioctl calls are used to: * * * * (1) Set the I/O mode to Programmed I/O * * (the default) * * (2) Set the I/O mode to DMA I/O * * (3) Enable a single interrupt test * * (4) Read one 32-bit word from the test * * board address (ROM/register) space * * (5) Start clock-driven incrementing of the * * 4 spare LEDs on the board or * * (6) Stop clock-driven incrementing of the 4 * * spare LEDs on the board. * * * * Standard read and write calls are used to * * perform the data register reads and writes. * ***************************************************/

/*************************************************** * Larry Robinson and Jim Crapuchettes, Digital * * TRIADD Program. Ported to Digital UNIX by Mark * * Parenti, Digital. Made loadable on Digital UNIX* * by Tim Burke, Digital, and Jeff Anuszczyk, * * formerly of Digital. Updated to take advantage * * of the single binary technology by Karl Ebner. * ***************************************************/

/*************************************************** * Include Files Section * * * ***************************************************/

/*************************************************** * Define a constant called NCB that is used to * * allocate the data structures needed by the * * /dev/cb driver. Note that the define uses the * * TC_OPTION_SLOTS constant, which is defined in * * tc.h. There can be at most three instances of * * the CB controller on the system. This is a * * small number of instances of the device on the * * system and the data structures themselves are * * not large, so it is acceptable to allocate for * * the maximum configuration. This is an example * * of the static allocation model technique. * ***************************************************/

/*************************************************** * The following include files assume that the * * current directory is a subdirectory of * * /usr/sys. *

***************************************************/

#include <sys/param.h> #include <kern/lock.h> #include <sys/ioctl.h> #include <sys/user.h> #include <sys/proc.h> #include <hal/cpuconf.h> #include <io/common/handler.h> #include <sys/vm.h> #include <sys/buf.h> #include <sys/errno.h> #include <sys/conf.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/types.h>

#include <io/common/devdriver.h> #include <sys/sysconfig.h> #include <io/dec/tc/tc.h>

#include <io/CB100/cbreg.h> /* Device register header file */

#define NCB TC_OPTION_SLOTS #define NO_DEV -1

/***************************************************
 * Autoconfiguration Support Declarations and      *
 * Definitions Section                             *
 ***************************************************/

extern int hz; /* System clock ticks per second */


 
/*************************************************** * Do forward declaration of driver entry points * * and define information structures for driver * * structure definition and initialization below. * ***************************************************/
 

int cbprobe(), cbattach(), cbintr(), cbopen(), cbclose(); int cbread(), cbwrite(), cbioctl(), cbstart(), cbminphys(); int cbincled(), cb_ctlr_unattach(), cbstrategy();

/*************************************************** * Declare an array of pointers to controller * * structures * ***************************************************/

struct controller *cbinfo[NCB];

/*************************************************** * Define and initialize the driver structure for * * this driver. It is used to connect the driver * * entry points and other information to the * * Digital UNIX code. The driver structure is * * used primarily during Autoconfiguration. Note * * that the "slave" and "go" entry points do not * * exist in this driver and that a number of the * * members of the structure are not used because * * this is a driver that operates on the * * TURBOchannel bus (not on the VMEbus or some * * other bus). * ***************************************************/

struct driver cbdriver = { cbprobe, /* probe */ 0, /* slave */ cbattach, /* cattach */ 0, /* dattach */ 0, /* go */ 0, /* addr_list */ 0, /* dev_name */ 0, /* dev_list */ "cb", /* ctlr_name */ cbinfo, /* ctlr_list */ 0, /* xclu */ 0, /* addr1_size */ 0, /* addr1_atype */ 0, /* addr2_size */ 0, /* addr2_atype */ cb_ctlr_unattach, /* ctlr_unattach */ 0 /* dev_unattach */ };


 
/*************************************************** * cfgmgr Framework-Related Variables and * * Structures Definitions Section * ***************************************************/

/****************************************************** * The following code declares variables that are * * required by the cfgmgr framework. You use these * * variables as fields in the driver's attribute * * table. The attribute table for the /dev/cb driver * * is called cb_attributes. * ******************************************************/

static int majnum = NO_DEV; static int cbversion = 0; static int cb_developer_debug = 0;
 
static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = ; static unsigned char modtype[CFG_ATTR_NAME_SZ] = ; static unsigned char devmajor[CFG_ATTR_NAME_SZ] = ; static unsigned char unused[350] = ; static unsigned char cma_dd[120] = ;
 

/****************************************************** * Declares an array of cfg_subsys_attr_t structures * * and calls it cb_attributes. The cfgmgr framework * * fills in the elements in cb_attributes because the * * operation types designated by these are * * CFG_OP_CONFIGURE. The cfgmgr framework reads the * * the entries for the /dev/cb driver from the * * sysconfigtab database. (A sysconfigtab file * * fragment was created for the /dev/cb driver. The * * The sysconfigdb utility appends this sysconfigtab * * file fragment to the sysconfigtab database.) * * * * To determine if any of these element reads failed, * * the /dev/cb driver verifies each from the * * cfg_attr_t structure passed into the cb_configure * * interface. * * * * Several of the following fields are used in the * * /dev/cb device driver while other fields exist to * * illustrate what can be loaded by the cfgmgr * * framework winto the driver attributes table. * * * * These fields can represent tunable parameters to * * indicate whether the driver is statically or * * dynamically configured into the kernel. * ******************************************************/

cfg_subsys_attr_t cb_attributes[] = { {"Module_Config_Name", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0}, {"Module_Type", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)modtype,2,CFG_ATTR_NAME_SZ,0}, {"Device_Char_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)devmajor,0,CFG_ATTR_NAME_SZ,0}, {"CMA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)cma_dd,0,350,0}, {"TC_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)&tc_optiondata,0,300,0},
 

 
/********************************************** * The following device driver-related * * attributes are not used by the /dev/cb * * driver. The reason for defining these * * attributes in the cb_attributes array is * * prevent the cfgmgr framework from printing * * error messages on the console terminal * * when the driver is configured into the * * kernel. * **********************************************/
 

 
{"Module_Path", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Major_Req", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_User", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Group", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Mode", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"EISA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"ISA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"PCI_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,350,0}, /********************************************** * The /dev/cb device driver modifies the * * following attributes during a configure * * or unconfigure operation. The cfgmgr * * framework's device driver method uses * * these attributes to provide the device * * special files that device drivers need. * **********************************************/ {"majnum", CFG_ATTR_INTTYPE, CFG_OP_QUERY, (caddr_t)&majnum,0,512,0}, {"version", CFG_ATTR_INTTYPE, CFG_OP_QUERY, (caddr_t)&cbversion,0,9999999,0},
 

 
/********************************************** * The CB_Developer_Debug attribute is used * * with the printf interface to display * * runtime messages on the console terminal. * * A device driver writer might want to * * define such an attribute to collect * * detailed information relating to a driver * * problem. When the attribute is set to 1 * * by the cfgmgr framework (through the * * CFG_OP_RECONFIGURE operation) or * * initialized by sysconfigtab in the * * bootpath, these messages appear on the * * console terminal. These messages can * * provide the driver writer with * * configuration information that relates to * * detailed bus configuration information. * * * * The CB_Developer_Debug attribute * * (or another attribute similar to it) is * * used for detailed internal debugging of * * code by Digital personnel or someone who * * has a source license. * * * * Note that the CB_Developer_Debug * * attribute's operations are query, * * reconfigure, and configure. * * * * cb_developer_debug = 1 turns on these * * messages * * cb_developer_debug = 0 turns off these * * messages * * * * You also need to use the #ifdef and * * #endif statements as follows: * * * * #ifdef CB_DEBUG * * driver code * * #endif * **********************************************/
 
{"CB_Developer_Debug", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE, (caddr_t)&cb_developer_debug,0,1,0}, {,0,0,0,0,0,0} };
 

/*************************************************** * External function references. These are needed * * for the dsent declaration. The pointer to an * * array of cb_id_t contains "id's" that the * * interrupt handler interfaces use to deregister * * the interrupt handlers. * ***************************************************/

extern int nodev(), nulldev(); ihandler_id_t *cb_id_t[NCB];

#define DN_BUSNAME1 "*" /* All buses with this device configure */

/*************************************************** * The variable cb_is_dynamic will be used to * * control any differences in functions performed * * by the static and loadable versions of the * * driver. In this manner any differences are * * made on a run-time basis and not on a * * compile-time basis. * ***************************************************/

int cb_is_dynamic = 0; int driver_cfg_state;
 

/*************************************************** * When the driver is loadable it may not have an * * entry in the statically built tc_option * * table (located in tc_option_data.c). It is not * * an error if this entry already existed in the * * table. The entry in tc_option_data.c is used * * only when the driver is configured statically. * * The entry below is used only when the driver is * * configured dynamically. * * * * This table contains the bus-specific ROM module * * name for the driver. This information forms * * the bus-specific parameter that is passed to * * the ldbl_stanza_resolver interface to look for * * matches in the tc_slot table. * ***************************************************/


 
int cbcallback_return_status = ESUCCESS; int num_cb = 0; /* Count on the number of controllers probed */


 
/*************************************************** * Local Structure and Variable Definitions * * Section * ***************************************************/

/*************************************************** * Declare an array of buffer headers, 1 per * * CB unit. * ***************************************************/

struct buf cbbuf[NCB];

unsigned tmpbuffer; /* Temporary one-word buffer for cbstart */

/*************************************************** * Structure declaration for a CB unit. It * * contains status, pointers, and I/O mode for a * * single CB device. * ***************************************************/

struct cb_unit { /* All items are "for this unit": */ int attached; /* An attach was done */ int opened; /* An open was done */ int iomode; /* Read/write mode (PIO/DMA) */ int intrflag; /* Flag for interrupt test */ int ledflag; /* Flag for LED increment function */ int adapter; /* TC slot number */ caddr_t cbad; /* ROM base address */ io_handle_t cbr; /* I/O handle for device registers */ struct buf *cbbuf; /* Buffer structure address */ } cb_unit[NCB];

#define MAX_XFR 4 /* Maximum transfer chunk in bytes */

/*************************************************** * Loadable Driver Local Structure and Variable * * Definitions Section * ***************************************************/

int cb_config = FALSE; /* State flags indicating driver configured */ dev_t cb_devno = NODEV; /* No major number assigned yet. */

/*************************************************** * Device switch structure for dynamic * * configuration. The following is a definition of * * the devsw entry that will be dynamically added * * for the loadable driver. For this reason the * * loadable driver does not need to have its entry * * points statically configured into conf.c. * ***************************************************/

struct dsent cb_devsw_entry = { cbopen, /* d_open */ cbclose, /* d_close */ nodev, /* d_strategy */ cbread, /* d_read */ cbwrite, /* d_write */ cbioctl, /* d_ioctl */ nodev, /* d_dump */ nodev, /* d_psize */ nodev, /* d_stop */ nodev, /* d_reset */ nodev, /* d_select */ nodev, /* d_mmap */ 0, /* d_segmap */ 0, /* d_ttys */ DEV_FUNNEL_NULL, /* d_funnel */ 0 /* d_bflags */ 0 /* d_cflags */ };

/*************************************************** * WARNING ON USE OF printf FOR DEBUGGING * * * * Only a limited number of characters * * (system-release dependent; currently, seems to * * be 128) can be sent to the "console" display * * during each call to any section of a driver. * * This is because the characters are buffered * * until the driver returns to the kernel, at * * which time they are actually sent to the * * "console." If more than this number of * * characters are sent to the "console," the * * storage pointer may wrap around, discarding all * * previous characters, or it may discard all * * following characters! (Also system-release * * dependent.) Limit "console" output from within * * the driver if you need to see the results in * * the console window. However, 'printf' from * * within a driver also puts the messages into the * * error log file. The text can be viewed with * * 'uerf.' See the `uerf' man page for more * * information. The "-o terse" option makes the * * messages easier to read by removing the time * * stamp information. * * * * WARNING ON USE OF printf FOR DEBUGGING * ***************************************************/

#define CB_DEBUG /* Define debug constants */ #undef CB_DEBUGx /* Disable xtra debug */


 
/*************************************************** * Autoconfiguration Support Section * ***************************************************/ /*************************************************** * * * * *--------------- cbprobe -------------------------* ***************************************************/

cbprobe(addr, ctlr) io_handle_t addr; /* I/O handle passed to cbprobe */ struct controller *ctlr; /* controller structure for this unit */ {

/*************************************************** * * * These data structures will be used to register * * the interrupt handler for the driver. * * * ****************************************************/

ihandler_t handler; struct handler_intr_info info; int unit = ctlr->ctlr_num;

/*************************************************** * Call printf during debug * ***************************************************/

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("CBprobe @ %8x, addr = %8x, ctlr = %8x\n",cbprobe,addr,ctlr); #endif /* CB_DEBUG */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("CBprobe: perform loadable driver config of unit %d\n",unit); #endif /* CB_DEBUG */

/*************************************************** * Specify the bus that this controller is * * attached to. * ***************************************************/

handler.ih_bus = ctlr->bus_hd;

/*************************************************** * Set up the members of the handler_intr_info * * structure. The following code sets the * * configuration_st member to point to the * * controller structure for which an associated * * interrupt handler will be written. * ***************************************************/

info.configuration_st = (caddr_t)ctlr;

/*************************************************** * Specifies the driver type as a controller. * ***************************************************/

info.config_type = CONTROLLER_CONFIG_TYPE;

/**************************************************** * Specifies the interrupt handler. The interrupt * * handler for the /dev/none driver is called * * noneintr. * ****************************************************/

info.intr = cbintr;

/*************************************************** * This parameter will be passed to the driver's * * interrupt handler, which in this example is * * cbintr. * ***************************************************/

info.param = (caddr_t)unit;

/*************************************************** * Specifies bus registration information. In * * this example, the bus registration information * * is contained in the handler_intr_info * * structure. Thus, ih_bus_info is set to the * * address of this structure (info). * ***************************************************/

handler.ih_bus_info = (char *)&info;

/*************************************************** * Save the return id from handler_add. This id * * will be used later to deregister the interrupt * * handler. * * * * After saving the return id, call handler_add * * and handler_enable to register and enable the * * interrupt handler. * ***************************************************/

cb_id_t[unit] = handler_add(&handler); if (cb_id_t[unit] == NULL) {

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("CBprobe: handler_add failed.\n"); #endif /* CB_DEBUG */

return(0); /* Return failure status */ }

if (handler_enable(cb_id_t[unit]) != 0) { handler_del(cb_id_t[unit]);

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("CBprobe: handler_enable failed.\n"); #endif /* CB_DEBUG */

return(0); /* Return failure status */ }

/*************************************************** * Increment the number of instances of this * * controller. * ***************************************************/

num_cb++;

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("CBprobe: return success.\n"); #endif /* CB_DEBUG */

return(1); /* Assume ok since TC ROM probe worked */ }

/*************************************************** *-------------- cbattach -------------------------* ***************************************************/

cbattach(ctlr) struct controller *ctlr; /* controller structure for this unit */ { struct cb_unit *cb; /* Pointer to unit data structure */

/*************************************************** * Set up per-unit data structure for this device * ***************************************************/

cb = &cb_unit[ctlr->ctlr_num]; /* Point to this device's structure */

cb->attached = 1; /* Indicate device is attached */

cb->adapter = ctlr->slot; /* Set the adapter (slot) number */

cb->cbad = ctlr->addr; /* Set base of device ROM */

cb->cbr = (io_handle_t)CB_ADR(ctlr->addr); /* Point to device's registers */ cb->cbbuf = &cbbuf[ctlr->ctlr_num]; /* Point to device's buffer header */ cb->iomode = CBPIO; /* Start in PIO mode */ }

/*************************************************** *------------ cb_ctlr_unattach -------------------* * * * Loadable driver-specific interface called * * indirectly from the bus code when a driver is * * being unloaded. * * * * Returns 0 on success, nonzero (1) on error. * ***************************************************/

int cb_ctlr_unattach(bus, ctlr) struct bus *bus; /* Pointer to bus structure */ struct controller *ctlr; /* Pointer to controller structure */ {

register int unit = ctlr->ctlr_num;

/*************************************************** * Validate the unit number. * ***************************************************/

if ((unit > num_cb) || (unit < 0)) { return(ENODEV); /* Return error status */ }

/*************************************************** * This interface should never be called for a * * static driver. The reason is that the static * * driver does not do a handler_add in the first * * place. * ***************************************************/

if (cb_is_dynamic == 0) { return(ENODEV); /* Return error status */ }

/*************************************************** * The deregistration of interrupt handlers * * consists of a call to handler_disable to * * disable any further interrupts. Then, call * * handler_del to remove the interrupt handler. * ***************************************************/

if (handler_disable(cb_id_t[unit]) != 0) { return(ENODEV); /* Return error status */ }

if (handler_del(cb_id_t[unit]) != 0) { return(ENODEV); /* Return error status */ } return(ESUCCESS); /* Return success status */ }

/*************************************************** * Device Driver Configuration Section * ***************************************************/

/*************************************************** * cb_configure() * *************************************************** * Name: cb_configure * * * * Arguments: * * * * op Configure operation * * indata Input data structure, cfg_attr_t * * indatalen Size of input data structure * * outdata Formal parameter not used * * outdatalen Formal parameter not used * * * * Kernel support interface calls: * * * * o atoi * * o bcopy * * o devsw_add * * o devsw_del * * o cfgmgr_get_state * * o configure_driver * * o makedev * * o printf * * o register_callback * * o cb_register_configuration * * o cb_register_major_number * * o strcmp * * o strcpy * * o unconfigure_driver * * * * Called by: * * * * * * o cfgmgr framework * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: EBUSY * * EINVAL * * ESRCH * * * * Description: * * * * The cb_configure interface is called * * indirectly by the cfgmgr framework. The cfgmgr * * framework is responsible for calling all single * * binary modules for registration and integration * * into the kernel. The cfgmgr framework requires * * that a single binary module has both an * * attributes table and a configure interface * * before it (the single binary module) can be * * registered as part of the cfgmgr framework and * * the Digital UNIX kernel. * * * * The cb_configure interface cooperates with * * the cfgmgr framework to complete configure, * * unconfigure, query, and other requests. * * These requests are differentiated by the "op" * * argument. * * * * Specifically, the cb_configure interface * * shows the code a device driver must supply to * * produce a single binary module. This code * * allows the /dev/cb driver to be configured * * as either a Static or dynamic subsystem * ***************************************************/

cb_configure(op,indata,indatalen,outdata,outdatalen) cfg_op_t op; /* Configure operation */ cfg_attr_t *indata; /* Input data structure, cfg_attr_t */ size_t indatalen; /* Size of input data structure */ cfg_attr_t *outdata; /* Formal parameter not used */ size_t outdatalen; /* Formal parameter not used */ { dev_t cdevno; int retval; int i; struct cb_unit *cb; /* Pointer to unit data structure */ int cbincled(); /* Forward reference function */
 
#ifdef CB_DEBUG /*************************************************** * * * MAX_DEVICE_CFG_ENTRIES represents the number of * * config lines in the stanza.loadable file * * fragment for this driver. * * * ***************************************************/
 
#define MAX_DEVICE_CFG_ENTRIES 18
 
/*************************************************** * The cfg_attr_t list passed into the * * cb_configure interface's indata parameter * * contains the strings that are stored in the * * sysconfigtab database for this subsystem (the * * /dev/cb device driver). * ***************************************************/
 
cfg_attr_t cfg_buf[MAX_DEVICE_CFG_ENTRIES]; #endif

/*************************************************** * Execute the directed Operation Code * * (Configure/Unconfigure) * ***************************************************/

switch (op) {

/*************************************************** * Configure (load) the driver. * * * ***************************************************/

case CFG_OP_CONFIGURE: /*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("cb_configure: CFG_OP_CONFIGURE.\n"); #endif /* CB_DEBUG */ #ifdef CB_DEBUG /*************************************************** * Copy the data into a local cfg_attr_t * * array. * * * ***************************************************/
 
/*************************************************** * Pass Parameter Verification * * * * Parameters passed via the cb_attribute * * structure will not be known to be correct/valid * * their status in the indata list can be checked. * * A status other CFG_FRAME_SUCCESS is an error * * condition. * ***************************************************/
 
bcopy(indata, cfg_buf[0].name, indatalen*(sizeof(cfg_attr_t))); printf(" The cb_configure routine was called. op = %x\n",op); for( i=0; i < indatalen; i++){ printf("%s: ",cfg_buf[i].name); switch(cfg_buf[i].type){ case CFG_ATTR_STRTYPE: printf("%s\n",cfg_buf[i].attr.str.val); break; default: switch(cfg_buf[i].status){ case CFG_ATTR_EEXISTS: printf("**Attribute does not exist\n"); break; case CFG_ATTR_EOP: printf("**Attribute does not support operation\n"); break; case CFG_ATTR_ESUBSYS: printf("**Subsystem Failure\n"); break; case CFG_ATTR_ESMALL: printf("**Attribute size/value too small\n"); break; case CFG_ATTR_ELARGE: printf("**Attribute size/value too large\n"); break; case CFG_ATTR_ETYPE: printf("**Attribute invalid type\n"); break; case CFG_ATTR_EINDEX: printf("**Attribute invalid index\n"); break; case CFG_ATTR_EMEM: printf("**Attribute memory allocation error\n"); break; default: printf("**Unknown attribute: "); printf("%x\n", cfg_buf[i].status); break; } break; } } #endif


 
/*************************************************** * The following code performs a check on the * * configuration name. If the configuration name * * is NULL, subsequent code uses the controller * * name stored in driver structure's ctlr_name * * member. The driver structure for the /dev/cb * * driver is called cbdriver. This structure * * was declared and initialized to appropriate * * values in the Declarations Section of the * * /dev/cb driver. * * * * The name specified in the ctlr_name member will * * be replaced if the mcfgname variable is not * * NULL. The value stored in mcfgname supersedes * * the controller name stored in ctlr_name during * * configuration of the driver. * ***************************************************/
 
if(strcmp(mcfgname,)==0) { /* If the operator is interested in knowing the configuration name of this driver, you can set this attribute to CFG_OP_QUERY in the driver's cfg_subsys_attr_t structure. */ strcpy(mcfgname,"cb"); } else { /* Module_Config_Name attribute (mcfgname) from sysconfigtab is used to configure the device driver in the following calls to the configure_driver interface. */ }
 
/* CFGMGR fatal error determining the state of the * * "cb" subsystem * */
 
if(cfgmgr_get_state(mcfgname, &driver_cfg_state) != ESUCCESS){ return(EINVAL); }
 
if(driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED) {
 
/*************************************************** * * * During Static cfgmgr configuration subsystem * * callbacks scheduled to run in the boot path * * have no way to determine if the previous * * callbacks worked. This global flag is looked at * * in each of the callback jackets to determine if * * this subsystem should still be configured in * * the boot path. * ***************************************************/
 
cbcallback_return_status = ESUCCESS;
 
/**************************************************** * During Static configuration the cfgmgr framework * * schedules work to be done at pre and post * * autoconfiguration but it does does not actually * * know at this time whether a subsystem will * * successfully configure into a kernel. The * * callbacks are responsible for determining the * * status of a subsystem's configuration in the * * boot path and reporting failure to the cfgmgr * * framework through a call to the cfgmgr_set_state * * interface. * ***************************************************/
 
cb_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED;
 
register_callback(callback_cb_register_configuration, CFG_PT_PRECONFIG, CFG_ORD_NOMINAL, (long) 0L);
 
register_callback(callback_0, CFG_PT_PRECONFIG, CFG_ORD_NOMINAL+100, (long) 0L);
 
register_callback(callback_1, CFG_PT_PRECONFIG, CFG_ORD_NOMINAL, (long) 0L);
 
register_callback(callback_2, CFG_PT_PRECONFIG, CFG_ORD_MAXIMUM, (long) 0L);
 
register_callback(callback_3, CFG_PT_PRECONFIG, CFG_ORD_MAXIMUM+100, (long) 0L);
 
register_callback(callback_cb_register_major_number, CFG_PT_POSTCONFIG, CFG_ORD_NOMINAL, (long) 0L); }
 
else {
 
/***************************************************** * During Dynamic configuration the cfgmgr framework * * is present when the following functionality is * * executed. The error returns here will signal to * * the cfgmgr framework that the subsystem's * * configuration has failed. * ***************************************************/
 
cb_is_dynamic = SUBSYSTEM_DYNAMICALLY_CONFIGURED; retval = cb_register_configuration(); if(retval != ESUCCESS) return(retval);
 
if((retval = configure_driver(mcfgname, DRIVER_WILDNUM, DN_BUSNAME1, &cbdriver)) != ESUCCESS) {
 
return(retval); }
 
retval = cb_register_major_number(); if(retval != ESUCCESS) return(retval); } break;


 

/*************************************************** * Unconfigure (unload) the driver. * ***************************************************/

case CFG_OP_UNCONFIGURE:
 
/*************************************************** * Fail the unconfiguration if the driver * * is not currently configured as a * * dynamic subsystem. Statically * * configured subsystems CANNOT be * * unconfigure/unloaded. * * * * Static subsystems will all physically * * remain in the kernel whether or not * * they are configured. Dynamic subsystem * * when they are unconfigured are also * * unloaded from the kernel. * ***************************************************/
 
if(cb_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) { return(ESUCCESS); }

/*************************************************** * Do not allow the driver to be unloaded * * if it is currently active. To see if * * the driver is active look to see if any * * users have the device open. * ***************************************************/

for (i = 0; i < num_cb; i++) { cb = &cb_unit[i]; cb->ledflag = 0; untimeout(cbincled, (caddr_t)cb); }
 
/*************************************************** * Call devsw_del to remove the driver * * entry points from the in-memory resident * * devsw table. This is done prior to * * deleting the loadable configuration * * and handlers to prevent users from * * accessing the device in the middle of * * deconfigure operation. * ***************************************************/
 
retval = devsw_del(mcfgname, MAJOR_INSTANCE); if (retval == NO_DEV) { return(ESRCH); }
 
/*************************************************** * Deregister the driver's configuration * * data structures from the hardware * * topology and cause the interrupt handlers * * data structures from the hardware * * topology and cause the interrupt handlers * * to be deleted. * ***************************************************/
 
/*************************************************** * The bus number is wildcarded to * * deregister on all instances of the tc * * bus. The controller name and number are * * wildcarded. This causes all instances * * that match the specified driver structure * * to be deregistered. Through the * * bus-specific code, this interface results * * in a call to the cb_ctlr_unattach * * interface for each instance of the * * controller. * ***************************************************/
 
if (unconfigure_driver(DN_BUSNAME1, DRIVER_WILDNUM, &cbdriver, DRIVER_WILDNAME, DRIVER_WILDNUM) != 0) {
 
return(ESRCH); } cb_is_dynamic = 0; cb_config = FALSE; break;

/************************************************** * Requests to query a loadable subsystem will * * only succeed if the CFG_OP_QUERY: entry * * returns success. * **************************************************/

case CFG_OP_RECONFIGURE: break;
 
case CFG_OP_QUERY: break;
 
default: /* Unknown operation type */ return(ENOTSUP); break; }

/*************************************************** * The driver's configure interface has * * completed successfully. Return a success * * status. * ***************************************************/

return(0); }


 
/*************************************************** * cb_register_configuration() * *************************************************** * Name: cb_register_configuration * * * * Arguments: None * * * * Kernel support interface calls: * * * * o create_controller_struct * * o create_device_struct * * * * Called by: * * * * o During dynamic configuration * * - cb_configure * * * * o During static Configuration * * - cfgmgr framework callback * * Specifically, the /dev/cb driver * * registers an interface called * * callback_cb_register_configuration. * * The cfgmgr framework calls * * callback_cb_register_configuration, * * which contains a call to the * * cb_register_configuration interface. * * * * Return Codes: * * * * Failure: ENOMEM * * * * Description: * * * * The cb_register_configuration interface is * * responsible for registering the controller and * * device information for the /dev/cb driver. * * The dynamically configured /dev/cb driver * * directly calls the cb_register_configuration * * interface. The statically configured /dev/cb * * driver calls cb_register_configuration through a* * register callback called * * callback_cb_register_configuration. (The * * cb_configure interface calls the * * cb_register_callback interface to register the * * cb_register_configuration interface so that it * * can be called at some later time. * * * * Writing an interface similar to * * cb_register_configuration is the method for * * specifying the controller and device * * information associated with a device driver. * * * * The cb_register_configuration interface causes * * the appropriate controller and device structures* * to be created and integrated into the system * * (hardware) configuration tree for statically * * and dynamically configured drivers. * * * * This method requires device driver writers to * * determine how much operator control should be * * allowed over the system's topology creation * * support code. If appropriate, driver writers * * can define sysconfigtab database fields that * * will allow the operator to control what * * hardware-related information that this * * interface passes to the appropriate structures. * ***************************************************/
 
int cb_register_configuration() {
 
/*************************************************** * Declare controller_config and device_config * * structures and structure pointers. These * * structures are the storage mechanism for * * populating the controller and device * * associated with a specific device driver. * ***************************************************/
 
struct controller_config ctlr_register; struct controller_config * ctlr_register_ptr = &ctlr_register; int i, status;
 
/*************************************************** * Register a controller structure for the * * /dev/cb driver. The system manager can * * specify attribute fields for the none: entry in * * etc/sysconfigtab database that determine the * * number of controller structures to be created. * * It is here that the user-configurable attribute * * NCB would be used to regulate the number of * * controller structures to be created up to the * * MAX_NCB value defined in the driver. * ***************************************************/
 

 
ctlr_register_ptr->revision = CTLR_CONFIG_REVISION; strcpy(ctlr_register_ptr->subsystem_name, mcfgname); strcpy(ctlr_register_ptr->bus_name,DN_BUSNAME1); ctlr_register_ptr->devdriver = &cbdriver;
 
/*************************************************** * Call the create_controller_struct to create the * * controller structures associated with the * * /dev/cb driver (1 per slot). * ***************************************************/
 
for(i = 0; i < NCB; i ++) { status = create_controller_struct(ctlr_register_ptr); if(status != ESUCCESS) return (status); }
 
return(ESUCCESS); }


 
/*************************************************** * callback_cb_register_configuration() * *************************************************** * Name: callback_cb_register_configuration() * * * * Arguments: * * * * int point - driver callback point * * int order - Priority at callback * * ulong args - User argument * * * * Kernel support interface calls: * * * * o cfgmgr_set_status * * o cb_register_configuration * * * * Called by: * * * * o cfgmgr framework through a call to * * the register_callback interface * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: EBUSY * * EINVAL * * ESRCH * * * * Description: * * * * This is the Static cfgmgr jacket used to * * do configuration registration in the boot path. * * This jacket routine handles the static callback * * specific error handling but utilizes the same * * configuration routine used by the driver when * * dynamically loading. * * * ***************************************************/
 
void callback_cb_register_configuration(point, order, argument, event_arg) int point; int order; ulong args; ulong event_arg; { int status;
 
/*************************************************** * Detect if a previous failure has occurred in * * statically configuring this driver. If so, exit * * the callback without doing any configuration * * work. ***************************************************/
 
if(cbcallback_return_status != ESUCCESS) { return; }
 
/* * Register this drivers configuration with the * kernel's topology. */
 
status = cb_register_configuration();
 
/* * If configuration registration is successful then * just return from this callback. Else signal the * cfgmgr that this driver has failed to configure and * set the callback_return_status to the failure * condition. Note: A side effect of calling the * cfgmgr_set_status() routine is that the configure * routine or this driver is called with the * unconfigure op passed. The unconfigure routine must * be prepared for this event. * */
 
if(status != ESUCCESS) { cfgmgr_set_status(mcfgname); cbcallback_return_status = status;
 
return; } }
 
void callback_0(point, order, args, event_arg) int point; int order; ulong args; ulong event_arg; { int status;
 
printf("$$$ Callback 0 reached Nominal + 100\n"); }
 
void callback_1(point, order, args, event_arg) int point; int order; ulong args; ulong event_arg; { int status;
 
printf("$$$ Callback 1 reached Nominal\n"); }
 
void callback_2(point, order, args, event_arg) int point; int order; ulong args; ulong event_arg; { int status;
 
printf("$$$ Callback 2 reached Maximum\n"); }
 
void callback_3(point, order, args, event_arg) int point; int order; ulong args; ulong event_arg; { int status;
 
printf("$$$ Callback 3 reached at Maximum + 100\n"); }

/***************************************************
 *        cb_register_major_number()               *
 ***************************************************
 * Name:  cb_register_major_number                 *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o atoi                                     *
 *      o cfgmgr_set_status                        *
 *      o atoi                                     *
 *      o atoi                                     *
 *      o devsw_reserve                            *
 *      o devsw_add                                *
 *      o strcmp                                   *
 *      o strcpy                                   *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework                     *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      EINVAL                     *
 *                      ESRCH                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 ***************************************************/

 
int cb_register_major_number() {
 
/* * If there are no devices present in the system after * driver configuration has been done then set the * cfgmgr status unconfigured and exit the callback. */
 
if (num_cb == 0) { return (ENODEV); }
 
/* * This call to devsw_add is responsible for adding * the device driver information to the device switch table. * Each allocation provides an entry for the devie table. */ majnum = devsw_add(mcfgname, MAJOR_INSTANCE, majnum, &cb_devsw_entry);
 
if (majnum == NO_DEV) {
 
/*************************************************** * The call to devsw_add could fail if * * the driver is requesting a specific * * major number and that number is * * currently in use, or if the devsw * * table is currently full. * ***************************************************/
 
return (ENODEV); }
 
/*************************************************** * Stash away the major number so that it can * * be used later to unconfigure the device. Save * * off the minor number information. This will be * * returned by the query call. * ***************************************************/
 
cb_devno = majnum;
 
/*************************************************** * This is a character driver. For this * * reason no block major number is * * assigned. * ***************************************************/
 
cb_config = TRUE;
 
return(ESUCCESS); }


 
/*************************************************** * Open and Close Device Section * ***************************************************/

/*************************************************** *---------------- cbopen -------------------------* ***************************************************/

cbopen(dev, flag, format) dev_t dev; /* Major/minor device number */ int flag; /* Flags from /usr/sys/h/file.h */ int format; /* Format of special device */ {

/*************************************************** * Get device (unit) number * ***************************************************/ int unit = minor(dev);

/*************************************************** * Error if unit number too big or if unit * * not attached * ***************************************************/ if ((unit > NCB) || !cb_unit[unit].attached) return(ENXIO); cb_unit[unit].opened = 1; /* All ok, indicate device opened */ return(0); /* Return success! */ }

/*************************************************** *---------------- cbclose ------------------------* ***************************************************/

cbclose(dev, flag, format) dev_t dev; /* Major/minor device number */ int flag; /* Flags from /usr/sys/h/file.h */ int format; /* Format of special device */ {

int unit = minor(dev); /* Get device (unit) number */ cb_unit[unit].opened = 0; /* Indicate device closed */ return(0); /* Return success! */ }

/***************************************************
 * Read and Write Device Section                   *
 ***************************************************/
/***************************************************
 *---------------- cbread -------------------------*
 ***************************************************/

cbread(dev, uio, flag) dev_t dev; /* Major/minor device numbers */ struct uio *uio; /* I/O descriptor structure */ int flag; /* Access mode of device */ { unsigned tmp; int cnt, err; int unit = minor(dev); /* Get device (unit) number */ struct cb_unit *cb; /* Pointer to unit data structure */

/*************************************************** * To do the read, the device index (unit number) * * is used to select the TC test board to be * * accessed and the mode setting within the * * controller structure for that unit is tested * * to determine whether to do a programmed read or * * a DMA read. For a programmed read, the * * contents of the data register on the test board * * are read into a 32-bit local variable and then * * the contents of that variable are moved into * * the buffer in the user's virtual address space * * with the uiomove interface. * * * * For a DMA read, the system's physio interface * * and the driver's strategy and minphys * * interfaces are used to transfer the contents of * * the data register on the test board into the * * buffer in the user's virtual address space. * * * * Note that since only a single word of 4 * * (the constant MAX_XFR) bytes can be transferred * * at a time, both modes of reading include code * * to limit the read to chunks with a maximum of * * MAX_XFR bytes each and that reading more than * * MAX_XFR bytes will propagate the contents of * * the data register throughout the words of the * * user's buffer. * ***************************************************/

err = 0; /* Initialize for no error (yet) */ cb = &cb_unit[unit]; /* Set pointer to unit's structure */ if(cb->iomode == CBPIO) { /* Programmed I/O read code */

/*************************************************** * Transfer bytes from the test board data * * register to the user's buffer until all * * requested bytes are moved or an error occurs. * * This must be done as a loop because the source * * (the board data register) can supply only * * MAX_XFR bytes at a time. The loop may not be * * required for other devices. * ***************************************************/

while((cnt = uio->uio_resid) && (err == 0)) {

/*************************************************** * Force count for THIS "section" to be less than * * or equal to MAX_XFR bytes (the size of the data * * buffer on the test board). This causes a read * * of more than MAX_XFR bytes to be chopped up * * into a number MAX_XFR-byte transfers with a * * final transfer of MAX_XFR bytes or less. * ***************************************************/

if(cnt > MAX_XFR)cnt = MAX_XFR; tmp = read_io_port(cb->cbr | CB_DATA, 4, 0);/* Read data */ /* register */

/*************************************************** * Move bytes read from the data register to the * * user's buffer. Note that: * * * * (1) The maximum number of bytes moved is * MAX_XFR for each call due to the code * * above. * * (2) The uio structure is updated as each * * move is done. * * * * Thus, uio->uio_resid will be updated for the * * "while" statement above. * ***************************************************/

err = uiomove(&tmp,cnt,uio); } return(err); } else if(cb->iomode == CBDMA) /* DMA I/O read code */

/*************************************************** * Transfer bytes from the test board data * * register to the user's buffer until all * * requested bytes are moved or an error occurs. * * The driver's strategy and minphys interfaces * * account for the fact that the source (the board * * data register) can supply only 4 bytes at a * * time and the physio interface loops as required * * to transfer all requested bytes. * ***************************************************/

return(physio(cbstrategy,cb->cbbuf,dev, B_READ,cbminphys,uio)); }

/*************************************************** *---------------- cbwrite ------------------------* ***************************************************/

cbwrite(dev, uio, flag) dev_t dev; /* Major/minor device numbers */ struct uio *uio; /* I/O descriptor structure */ int flag; /* Access mode of device */ { unsigned tmp; int cnt, err; int unit = minor(dev); /* Get device (unit) number */ struct cb_unit *cb; /* Pointer to unit data structure */

/*************************************************** * To do the write, the device index (unit number) * * is used to select the TC test board to be * * accessed and the mode setting within the * * controller structure for that unit is tested to * * determine whether to do a programmed write or a * * DMA write. * * * * For a programmed write, the contents of one * * word from the buffer in the user's virtual * * address space are moved to a 32-bit local * * variable with the uiomove interface and the * * contents of that variable are moved to the data * * register on the test board. * * * * For a DMA write, the system's physio interface * * and the driver's strategy and minphys * * interfaces are used to transfer the contents of * * the buffer in the user's virtual address space * * to the data register on the test board. * * * * Note that since only a single word of 4 * * (MAX_XFR) bytes can be transferred at a time, * * both modes of reading include code to limit the * * write to chunks with a maximum of MAX_XFR * * bytes. Note that writing more than MAX_XFR * * bytes has limited usefulness since all the * * words of the user's buffer will be written into * * the single data register on the test board. * ***************************************************/

err = 0; /* Initialize for no error (yet) */ cb = &cb_unit[unit]; /* Set pointer to unit's structure */ if(cb->iomode == CBPIO) { /* Programmed I/O write code */

/*************************************************** * Transfer bytes from the user's buffer to the * * test board data register until all requested * * bytes are moved or an error occurs. This must * * be done as a loop because the destination * * (the board data register) can accept only 4 * * bytes at a time. The loop may not be required * * for other devices. * ***************************************************/

while((cnt = uio->uio_resid) && (err == 0)) { if(cnt > MAX_XFR)cnt = MAX_XFR; /* Copy data register */

/*************************************************** * Move bytes to write from the user's buffer to * * the local variable. Note that: * * * * (1) The maximum number of bytes moved is * * MAX_XFR for each call due to the above * * code. * * (2) The uio structure is updated as each move * * is done. Thus, uio->uio_resid will be * * updated for the above "while" statement. * ***************************************************/

err = uiomove(&tmp,cnt,uio); write_io_port(cb->cbr | CB_DATA, 4, 0, tmp); /* Write data to register */ } return(err); } else if(cb->iomode == CBDMA) /* DMA I/O write code */

/*************************************************** * Transfer bytes from the user's buffer to the * * test board data register until all requested * * bytes are moved or an error occurs. The * * driver's strategy and minphys interfaces * * account for the fact that the destination * * (the board data register) can take only MAX_XFR * * bytes at a time and the physio interface loops * * as required to transfer all requested bytes. * ***************************************************/

return(physio(cbstrategy,cb->cbbuf,dev, B_WRITE,cbminphys,uio)); }


 
/*************************************************** * Strategy Section * ***************************************************/ /*************************************************** *---------------- cbminphys ----------------------* ***************************************************/

cbminphys(bp) register struct buf *bp; /* Pointer to buf structure */ { if (bp->b_bcount > MAX_XFR) bp->b_bcount = MAX_XFR; /* Maximum transfer is 4 bytes */ return; }

/*************************************************** *---------------- cbstrategy ---------------------* ***************************************************/

cbstrategy(bp) register struct buf *bp; /* Pointer to buf structure */ { register int unit = minor(bp->b_dev); /* Get device (unit) number */

register struct controller *ctlr; /* Pointer to controller struct */ struct cb_unit *cb; /* Pointer to unit data structure */ caddr_t buff_addr; /* User buffer's virtual address */ caddr_t virt_addr; /* User buffer's virtual address */ unsigned phys_addr; /* User buffer's physical address */ int cmd; /* Current command for test board */ int err; /* Error status from uiomove */ int status; /* CSR contents for status checking */ unsigned lowbits; /* Low 2 virtual address bits */ unsigned tmp; /* Temporary holding variable */ int s; /* Temporary holding variable */

#ifdef CB_DEBUG char *vtype; /* String pointer for debug */ #endif /* CB_DEBUG */

ctlr = cbinfo[unit]; /* Set pointer to unit's structure */

/*************************************************** * The buffer is accessible, initialize buffer * * structure for transfer. * ***************************************************/

bp->b_resid = bp->b_bcount; /* Initialize bytes not xferred */ bp->av_forw = 0; /* Clear buffer queue forward link */

cb = &cb_unit[unit]; /* Set pointer to unit's structure */

virt_addr = bp->b_un.b_addr; /* Get buffer's virtual address */ buff_addr = virt_addr; /* and copy it for internal use */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("\n"); /* Line between */ /* cbstrategy calls */ #endif /* CB_DEBUG */

/*************************************************** * NOTE * *************************************************** * TURBOchannel DMA can ONLY be done with FULL * * WORDS and MUST be aligned on WORD boundaries! * * Since the user's buffer can be aligned on any * * byte boundary, the driver code MUST check for * * and handle the cases where the buffer is NOT * * word aligned (unless, of course, the * * TURBOchannel interface hardware includes * * special hardware to handle non-word-aligned * * transfers. The test board does NOT have any * * such hardware). If the user's buffer is NOT * * word-aligned, the driver can: * * * * (1) Exit with an error or * * (2) Take some action to word-align the * * transfer. * * * * Since virtual to physical mapping is done on a * * page basis, the low 2 bits of the virtual * * address of the user's buffer are also the low 2 * * bits of the physical address of the user's * * buffer and the buffer alignment can be * * determined by examining the low 2 bits of the * * virtual buffer address. If these 2 bits are * * nonzero, the buffer is not word-aligned and the * * driver must take the desired action. * ***************************************************/

/*************************************************** * Use the low-order 2 bits of the buffer virtual * * address as the word-aligned indicator for this * * transfer. If they are nonzero, the user's * * buffer is not word-aligned and an internal * * buffer must be used, so replace the current * * user buffer virtual address (it is updated by * * physio as each word is transferred) with the * * internal buffer virtual address. Since DMA to * * the board can only be done a word at a time, * * the internal buffer only needs to be a single * * word. * ***************************************************/

if ((lowbits = (unsigned)virt_addr & 3) != 0) { /* Test low 2 bits */ virt_addr = (caddr_t)(&tmpbuffer); /* Use internal buffer */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("Bd %8x (%d)\n",buff_addr,bp->b_resid); #endif /* CB_DEBUG */

/*************************************************** * If the transfer type is a "write" * * (program => device), then clear the local * * one-word temporary buffer (in case less * * than 4 bytes), move the user's data bytes to * * the local temporary buffer, and return error * * status if an error occurs. The DMA "write" * * will be done from the temporary buffer. * * * * NOTE * *************************************************** * Don't use B_WRITE to test for a write. It is * * defined as 0 (zero). You MUST use the * * complement of test for B_READ! * ***************************************************/

if ( !(bp->b_flags&B_READ) ) { /* Move now for "write" */ tmpbuffer = 0 ; /* Clear the whole word */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("Ci\n"); #endif /* CB_DEBUG */

if (err = copyin(buff_addr,virt_addr, bp->b_resid)) { bp->b_error = err; /* See cbwrite */ bp->b_flags |= B_ERROR; /* error code */ iodone(bp); /* Signal I/O done */ return; /* Return error */ } } }

/*************************************************** * Convert the buffer virtual address to a * * physical address for DMA by calling the vtop * * interface. * ***************************************************/

phys_addr = vtop(bp->b_proc, virt_addr);

/*************************************************** * Convert the 32-bit physical address (actually * * the low 32 bits of the 34-bit physical address) * * from the linear form to the condensed form * * used by DMA to pack 34 address bits onto 32 * * board lines. * * * ***************************************************/ /*************************************************** * WARNING NOTE * *************************************************** * * * TURBOchannel DMA can ONLY be done with FULL * * WORDS and MUST be aligned on WORD boundaries! * * The CB_SCRAMBLE macro DISCARDS the low-order * * 2 bits of the physical address while scrambling * * the rest of the address! Therefore, anything * * that is going to be done to resolve this issue * * must be done BEFORE CB_SCRAMBLE is used. * ***************************************************/

tmp = CB_SCRAMBLE(phys_addr); write_io_port(cb->cbr | CB_ADDER, 4, 0, tmp);

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("%s %8x= %4s\n",vtype,virt_addr,virt_addr); printf("ph %8x sc %8x Pr %8x\n",phys_addr,tmp,bp->b_proc); #endif /* CB_DEBUG */

if(bp->b_flags&B_READ) /* Set up the DMA enable bits: */ cmd = CB_DMA_WR; /* Read = "Write to memory" */ else cmd = CB_DMA_RD; /* Write = "Read from memory" */

s = splbio(); /* Raise priority */

/*************************************************** * Although not required in this driver since it * * is only called from the following line, the * * "start I/O" interface is called as a function * * to separate its functionality from the strategy * * interface. This is the typical form it will * * have in other drivers. * ***************************************************/

err = cbstart(cmd,cb); /* Start I/O operation */ splx(s); /* Restore priority */

/*************************************************** * If the cbstart "timed out" ("err" count not * * positive), return error. If the loop did not * * "time out," set the bytes remaining to zero (0) * * to return with success. * ***************************************************/

if(err <= 0) { /* Check return value from cbstart */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("err %2d CSR %4x\n",err, (read_io_port(cb->cbr | CB_CSR, 4, 0))&0xffff); #endif /* CB_DEBUG */

bp->b_error = EIO; /* Set "I/O error on device" */ bp->b_flags |= B_ERROR; /* return access & error flag */ iodone(bp); /* Signal I/O done */ return; /* Return with error. */ } else {

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG tmp = read_io_port(cb->cbr | CB_DATA, /* Get data */ 4, /* register */ 0); /* to display */ status = read_io_port(cb->cbr | CB_CSR, /* Get */ 4, /* status */ 0); /* from */ /* CSR */ printf("%2d CSR %4x d %8x= %4s\n",err, status&0xffff,tmp,&tmp); #endif /* CB_DEBUG */

/*************************************************** * Did not time out: DMA transfer worked. Test the * * low-order 2 bits of the buffer virtual address * * and the transfer mode gain. If the low 2 bits * * are nonzero, then the user's buffer was not * * word-aligned and the internal buffer was used. * * If the tranfer type is a "read" * * (device => program), then move the user's data * * from the local one-word temporary buffer and * * return error status if an error occurs. The * * DMA "read" has been done into the temporary * * buffer. * ***************************************************/

if ( (lowbits)!=0 && bp->b_flags&B_READ) { /* Move if "read" */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("Co\n"); #endif /* CB_DEBUG */

if (err = copyout(virt_addr,buff_addr, bp->b_resid)) { bp->b_error = err; /* See cbread */ bp->b_flags |= B_ERROR; /* error code */ } } bp->b_resid = 0; /* DMA complete, clear remainder */ }

iodone(bp); /* Indicate done on this buffer */

return; }

/*************************************************** * Start Section * ***************************************************/ /*************************************************** *---------------- cbstart ------------------------* * * * NOTE on CSR usage: cbstart, cbioctl, and * * cbincled are the only interfaces that load the * * CSR register of the board. Since cbincled * * increments the LEDs in the high 4 bits of the * * 16-bit CSR register, cbstart and cbioctl always * * load the 4 bits into whatever value they will * * be storing into the CSR before they do the * * actual store. Note also that cbstart is called * * with system interrupts disabled, so cbincled * * should not be called while cbstart is * * incrementing. * ***************************************************/

int cbstart(cmd,cb) int cmd; /* Current command for test board */ struct cb_unit *cb; /* Pointer to unit data structure */ { int timecnt; /* Timeout loop count */ int status; /* CSR contents for status checking */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUGx printf("\n"); /* Blank line */ /* between cbstart */ /* calls */ #endif CB_DEBUGx

cmd = (read_io_port(cb->cbr | CB_CSR, 4, 0)&0xf000)|(cmd&0xfff); /* High 4 LED bits */ /* into cmd */ status = read_io_port(cb->cbr | CB_TEST, 4, 0); /* Read "test" reg to */ /* clear "go" bit */
 
write_io_port(cb->cbr | CB_CSR, 4, 0, cmd); /* Load CSR with enable bit(s) */ mb(); /* Synchronize with CSR write */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUGx printf("Chk CSR %4x\n",(read_io_port(cb->cbr | CB_CSR, 4, 0))&0xffff); #endif CB_DEBUGx

write_io_port(cb->cbr | CB_TEST, 4, 0, 0); /* Write "test" reg */ /* to set "go" bit */ mb(); /* Synchronize with test reg write */

timecnt = 10; /* Initialize timeout loop counter */ status = read_io_port(cb->cbr | CB_CSR, 4, 0); /* Get status from CSR */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("%2d CSR %4x\n",timecnt, (read_io_port(cb->cbr | CB_CSR, 4, 0))&0xffff); #endif /* CB_DEBUG */

/*************************************************** * Wait for DMA done bit set or timeout loop * * counter to expire. This driver has a very * * short timeout period because the board * * should respond within a few machine cycles if * * it is not broken. Thus, the simple timeout * * loop below takes less time than calling a * * system interface. In most drivers, where * * timeout periods are greater than a few cycles, * * timeout is done using the sleep, wakeup, * * timeout, and untimeout kernel interfaces. See * * the CBINC (increment LED) code in cbioctl and * * cbincled below for an example of using timeout * * for repetitive timing. * ***************************************************/

while((!(status & CB_DMA_DONE)) && timecnt > 0) { write_io_port(cb->cbr | CB_CSR, 4, 0, cmd); /* Write to */ /* update status */ mb(); /* Synchronize with CSR write */ status = read_io_port(cb->cbr | CB_CSR, 4, 0); /* Get status from CSR again */ timecnt --; /* Decrement counter */ }

/*************************************************** * Return "timeout" count as function result. * ***************************************************/

return(timecnt); }

/*************************************************** * ioctl Section * ***************************************************/ /*************************************************** *---------------- cbioctl ------------------------* ***************************************************/

/*************************************************** * See NOTE on CSR usage at beginning of cbstart. * ***************************************************/

#define CBIncSec 1 /* Number of seconds between increments of lights */

cbioctl(dev, cmd, data, flag) dev_t dev; /* Major/minor device number */ unsigned int cmd; /* The ioctl command */ int *data; /* ioctl command-specified data */ int flag; /* Access mode of the device */ {

int tmp; /* A destination word for throw-aways */ int *addr; /* Pointer for word access to board */ int timecnt; /* Timeout loop count */ int unit = minor(dev); /* Get device (unit) number */ struct cb_unit *cb; /* Pointer to unit data structure */ int cbincled(); /* Forward reference interface */

cb = &cb_unit[unit]; /* Set pointer to unit's structure */

        switch(cmd&0xFF) {      /* Determine operation to do: */
               case CBINC&0xFF:         /* Start incrementing lights */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("\nCBioctl: CBINC ledflag = %d\n",cb->ledflag); #endif /* CB_DEBUG */

if(cb->ledflag == 0) { /* If not started, */ cb->ledflag++; /* Set flag & start timer */ timeout(cbincled, (caddr_t)cb, CBIncSec*hz); } break;

case CBPIO&0xFF: /* Set mode: programmed I/O */ cb->iomode = CBPIO; /* Just set I/O mode for unit */ break;

case CBDMA&0xFF: /* Set mode: DMA I/O */ cb->iomode = CBDMA; /* Just set I/O mode for unit */ break;

             case CBINT&0xFF: /* Do interrupt test */
                     timecnt = 10; /* Initialize timeout
                                   counter */
                     cb->intrflag = 0; /* Clear interrupt flag */

tmp = read_io_port(cb->cbr | CB_TEST, 4, 0); /* Clear "go" bit */

tmp = CB_INTERUPT|(read_io_port(cb->cbr | CB_CSR, 4, 0)&0xf000); /* New value */ write_io_port(cb->cbr | CB_CSR, 4, 0, tmp); /* Load enables & LEDs */ mb(); /* Synch. with CSR write */

write_io_port(cb->cbr | CB_TEST, 4, 0, 1); /* Set the "go" bit */ mb(); /* Synch. with test write */

/**************************************************** * Wait for interrupt flag * * to set or timeout loop * * counter to expire. * * Call write_io_port to * * update status. Call wbflush * * to synchronize with CSR * * write. Then decrement the * * counter. * ***************************************************/

while ((cb->intrflag == 0) && (timecnt > 0)) { write_io_port(cb->cbr | CB_CSR, 4, 0, tmp); mb(); timecnt --; }

tmp = read_io_port(cb->cbr | CB_TEST, 4, 0); /* Be sure "go" is clear */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("\nCBioctl: CBINT timecnt = %d\n",timecnt); #endif /* CB_DEBUG */

return(timecnt == 0); /* Success if nonzero count */

             case CBROM&0xFF:           /* Return a ROM word */
                    tmp = *data;        /* Get specified byte offset */
                    if(tmp < 0 || tmp >= 32768*4+4*4) /* 32k wrds
                                                      + 4 regs */
                            return(-tmp);  /* Offset is out of range */
                    tmp <<= 1; /* Double address offset */
                    addr = (int *)&(cb->cbad[tmp]); /* Get byte
                                                    address */
                    *data = *addr;        /* Return word from board */
                    break;

case CBCSR&0xFF: /* Update and return CSR */ write_io_port(cb->cbr | CB_CSR, 4, 0, read_io_port(cb->cbr | CB_CSR, 4, 0)); /* Read/write to update */ mb(); /* Synch. with CSR write */ *data = read_io_port(cb->cbr | CB_CSR, 4, 0); /* Return CSR from board */ break;

case CBSTP&0xFF: /* Stop incrementing lights */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("\nCBioctl: CBSTP called\n"); #endif /* CB_DEBUG */

cb->ledflag = 0; /* Stop on next timeout */ break;

default: /* Default is error case */ return(EINVAL); } return(0); }

/*************************************************** * Increment LED Section * ***************************************************/ /*************************************************** * * *---------------- cbincled -----------------------* ***************************************************/

/*************************************************** * This interface is called by the system * * softclock interface CBIncSec seconds after the * * last call to the timeout interface. If the * * increment flag is still set, increment the * * pattern in the high 4 LEDs of the LED/CSR * * register and restart the timeout to recall * * later. * * * * NOTE * *************************************************** * Because the LEDs are on when a bit is 0, use a * * subtract to do the increment. * * * * Also, see NOTE on CSR usage at the * * beginning of cbstart. * ***************************************************/

cbincled(cb) struct cb_unit *cb; /* Pointer to unit data structure */

{ int tmp;
 
tmp = read_io_port(cb->cbr | CB_CSR, 4, 0); tmp -= 0x1000; write_io_port(cb->cbr | CB_CSR, 4, 0, tmp); /* "Increment" lights */ if(cb->ledflag != 0) { /* If still set, */ timeout(cbincled, (caddr_t)cb, CBIncSec*hz); /* restart timer */ } return; }

/*************************************************** * Device Interrupt Handler Section * ***************************************************/ /*************************************************** *---------------- cbintr -------------------------* ***************************************************/

cbintr(ctlr) int ctlr; /* Index for controller structure */ { int tmp; struct cb_unit *cb; /* Pointer to unit data structure */ cb = &cb_unit[ctlr]; /* Point to this device's structure */ tmp = read_io_port(cb->cbr | CB_TEST, 4, 0); /* Read test reg to clear "go" bit */ cb->intrflag++; /* Set flag to tell it happened */

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef CB_DEBUG printf("\nCBintr interrupt, ctlr = %d\n",ctlr); /* Show interrupt */ #endif /* CB_DEBUG */

return; }

.po +0.5i