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