This chapter provides you with an opportunity to study an EISA bus device driver called /dev/envram. You can use the /dev/envram device driver as the basis for writing your own working EISA bus device drivers.
Note
The /dev/envram device driver does not operate on the ISA bus.
The /dev/envram device driver operates on an EISA bus and implements many of the device driver interfaces discussed in Chapter 3. It also implements other sections that the EISA bus nonvolatile random-access memory (NVRAM) expansion board needs. The chapter begins with an overview of the tasks performed by the /dev/envram device driver. Following this overview are sections that describe each piece of the /dev/envram device driver. Table 7-1 lists the parts of the /dev/envram device driver and the sections of the chapter where each is described.
Part | Section |
The envram_reg.h Header File | Section 7.2 |
The envram_data.c File | Section 7.3 |
Include Files Section | Section 7.4 |
Declarations Section | Section 7.5 |
Autoconfiguration Support Section | Section 7.6 |
Status Section | Section 7.7 |
Battery Status Section | Section 7.8 |
Read and Write Device Section | Section 7.9 |
Zero NVRAM Section | Section 7.10 |
The source code uses the following convention:
#define ENVRAM_MAPPED 1 [1]
The /dev/envram device driver is a character device driver that provides read and write services to the /dev/presto device driver. The /dev/presto device driver is a disk driver that uses nonvolatile memory as a cache. It works as a layer between other drivers and the rest of the Digital UNIX kernel.
The /dev/presto device driver's interfaces appear as the entry points in the cdevsw and bdevsw switch tables, instead of the interfaces of the other drivers (including the /dev/envram driver) it works with. Whenever /dev/presto needs to perform actual I/O operations (for example, when the cache needs filling or draining), it calls the layered driver's entry points (strategy, close, read, and write).
Figure 7-1 shows the relationship between the /dev/envram and /dev/presto device drivers. The figure shows the following flow of data between the different layers:
The following envram_reg.h file is the device register header file for the /dev/envram device driver. It contains public declarations and the definitions that map to the device registers for the EISA bus NVRAM expansion board.
#define ENVRAM_CSR 0xc00 [1] #define ENVRAM_BAT 0xc04 #define ENVRAM_HIBASE 0xc08 #define ENVRAM_CONFIG 0xc0c #define ENVRAM_ID 0xc80 #define ENVRAM_CTRL 0xc84 #define ENVRAM_DMA0 0xc88 #define ENVRAM_DMA1 0xc8c
#define ENVRAM_DIAG_REGISTER 0x3f8 [2] #define BOARD_FAILED 0x00000008 #define ENVRAM_DIAG_RESVED 0x400 #define ENVRAM_CACHE_OFFSET 0x400
#define SET_LED 0x0100 [3] #define BAT_FAIL 0x0800 #define WRMEM 0x2000 #define SET_DREQ 0x4000 #define DMA_CHAN_7 0X80 #define DMA_CHAN_5 0x40
#define BAT_DISCON_BIT 0x0080 [4]
#define EISA_ENABLE_BOARD 0x1 [5]
#define ENVRAM_ID_MASK 0x0025a310 [6]
#define ENVRAM_MAPPED 1 [7] #define ENVRAM_NOTMAPPED 0 #define ENVRAM_CACHED 1 #define ENVRAM_NOTCACHED 0
#define ENVRAM_XFER_SIZE 1024 [8] #define ENVRAM_ALLIGN 8192 [9]
This is the control status register (CSR) for the EISA bus NVRAM expansion board. Section 7.6.1, Section 7.8.2, and Section 7.8.3 show that the envram_probe, eisa_nvram_battery_enable, and eisa_nvram_battery_disable interfaces pass ENVRAM_CSR as an argument to ENVRAM_WRITEIO_D16. This interface writes data to the CSR. Section 7.5 shows that the /dev/envram driver uses write_io_port to construct ENVRAM_WRITEIO_D16.
Section 7.9.2 shows that the envram_write interface passes ENVRAM_CSR as an argument to ENVRAM_READIO_D16 and ENVRAM_WRITEIO_D16. The ENVRAM_READIO_D16 interface reads data from the CSR. Section 7.5 shows that the /dev/envram driver uses read_io_port to construct ENVRAM_READIO_D16.
This is the battery disconnect device register for the EISA bus NVRAM expansion board. Section 7.8.2 and Section 7.8.3 show that the eisa_nvram_battery_enable and eisa_nvram_battery_disable interfaces pass ENVRAM_BAT as an argument to ENVRAM_WRITEIO_D8. This interface writes data to the battery disconnect device register. Section 7.5 shows that the /dev/envram driver uses write_io_port to construct ENVRAM_WRITEIO_D8.
This is the extended memory configuration device register for the EISA bus NVRAM expansion board. The ECU sets up this device register.
This is the configuration device register for the EISA bus NVRAM expansion board. The ECU sets up this device register.
This is the ID device register for the EISA bus NVRAM expansion board. Section 7.6.1 shows that the envram_probe interface passes ENVRAM_ID as an argument to ENVRAM_READIO_D32. The ENVRAM_READIO_D32 interface checks ENVRAM_ID to ensure that the module is correct for this driver. Section 7.5 shows that the /dev/envram driver uses read_io_port to construct ENVRAM_READIO_D32.
This is the controller device register for the EISA bus NVRAM expansion board. Section 7.6.1 shows that the envram_probe interface passes ENVRAM_CTRL as an argument to ENVRAM_WRITEIO_D8. This interface writes data to the controller device register. Section 7.5 shows that the /dev/envram driver uses write_io_port to construct ENVRAM_WRITEIO_D8.
This is the DMA address device register 0 for the EISA bus NVRAM expansion board. Section 7.9.2 shows that the envram_write interface passes ENVRAM_DMA0 as an argument to ENVRAM_WRITEIO_D16. This interface writes data to DMA address device register 0. Section 7.5 shows that the /dev/envram driver uses write_io_port to construct ENVRAM_WRITEIO_D16.
This is the DMA address device register 1 for the EISA bus NVRAM expansion board. Section 7.9.2 shows that the envram_write interface passes ENVRAM_DMA1 as an argument to ENVRAM_WRITEIO_D16. This interface writes data to DMA address device register 1. Section 7.5 shows that the /dev/envram driver uses write_io_port to construct ENVRAM_WRITEIO_D16.
This is the software diagnostic register for the EISA bus NVRAM expansion board. The envram_probe interface uses the console diagnostics to set this device register.
This software diagnostic register is set if the EISA bus NVRAM expansion board fails software diagnostic tests. Section 7.6.1 shows that the envram_probe interface uses BOARD_FAILED to set the software diagnostic status in the softc structure.
This software diagnostic register specifies the amount of space the software diagnostics require.
This software diagnostic register on the EISA bus NVRAM expansion board is the offset from the starting address of the NVRAM. This address identifies where the /dev/presto driver can use the NVRAM. Section 7.6.1 shows how the envram_probe interface uses ENVRAM_CACHE_OFFSET.
This CSR bit mask turns on the light-emitting diode (LED) for the EISA bus NVRAM expansion board. Section 7.8.2 shows that the eisa_nvram_battery_enable interface uses this CSR bit mask to turn on the LED.
This CSR bit mask enables the battery fail interrupt for the EISA bus NVRAM expansion board. The /dev/envram driver does not currently use this CSR bit mask.
This CSR bit mask enables the power failure interrupt for the EISA bus NVRAM expansion board. The /dev/envram driver does not currently use this CSR bit mask.
Section 7.8.1 shows that the eisa_nvram_battery_status interface uses BAT_FAIL to check for a battery failure on the EISA bus NVRAM expansion board.
This CSR bit mask indicates the status of the disconnect circuit for the EISA bus NVRAM expansion board. Section 7.8.2 and Section 7.8.3 show that the eisa_nvram_battery_enable and eisa_nvram_battery_disable interfaces use this CSR bit mask to provide the /dev/presto driver with the ability to enable and disable the battery.
Section 7.6.1 shows that the envram_probe interface uses WRMEM to enable writes to the EISA bus NVRAM expansion board. Section 7.8.2, Section 7.8.3, and Section 7.9.2 show that the eisa_nvram_battery_enable, eisa_nvram_battery_disable, and envram_write interfaces pass the WRMEM bit mask to the ENVRAM_WRITEIO_D16 interface.
This CSR bit mask sets the device requirements for DMA operations on the EISA bus NVRAM expansion board. Section 7.9.2 shows how the envram_write interface uses this bit mask.
This CSR bit mask identifies channel 7 for DMA operations on the EISA bus NVRAM expansion board. The map load code from the ECU sets this device register.
This CSR bit mask identifies channel 5 for DMA operations on the EISA bus NVRAM expansion board. The map load code from the ECU sets this device register.
This constant indicates that the buffer is mapped. The /dev/envram driver does not currently use this constant.
Section 7.6.2 shows that the envram_attach interface uses this constant to indicate to the /dev/presto device driver that the buffer was not mapped.
Section 7.6.2 shows that the envram_attach interface uses this constant to indicate to the /dev/presto device driver the use of kernel segment (kseg) space.
This constant indicates the use of a cached space. The /dev/envram driver does not currently use this constant.
The following /usr/sys/data/envram_data.c file is the name_data.c file for the /dev/envram device driver. It contains the softc structure, which is used to share data between the different /dev/envram device driver interfaces.
struct envram_softc { io_handle_t regbase; io_handle_t cache_phys_start; io_handle_t cache_base; vm_offset_t cache_kseg_start; u_long saved_mem_sysmap; u_int cache_size; u_int cache_offset; io_handle_t diag_status; dma_handle_t sglp; struct controller *ctlr; }; [1]
struct envram_softc *envram_softc; [2] struct controller *envram_info[NENVRAM]; [3]
The following list describes the members contained in this structure:
Stores the physical base address of the I/O device registers for the EISA bus NVRAM expansion board. Section 7.5 shows that the /dev/envram driver uses this member to construct the EISA NVRAM I/O device register interfaces (macros). Section 7.6.1 shows that the envram_probe interface sets this member to the controller's base address.
The regbase member and other members of the envram_softc structure are I/O handles. An I/O handle is a data entity that is of type io_handle_t. Device drivers use the I/O handle to reference bus address space (either I/O space or memory space). On some buses, the bus configuration code passes the I/O handle to the device driver's xxprobe interface during device autoconfiguration. One purpose of the I/O handle is to hide CPU-specific architecture idiosyncracies that describe how to access a device's control status registers (CSRs) and how to perform I/O copy operations.
Specifically, this member is the I/O handle base of the EISA NVRAM I/O registers associated with a specific EISA option.
Stores the starting physical address of the NVRAM cache. Section 7.6.1 shows that the envram_probe interface sets this address.
Stores the base address of the NVRAM in EISA bus address space. Section 7.6.1 shows that the envram_probe interface sets this base address. Section 7.9.2 shows that the envram_write interface uses this member to set up the destination address passed from the /dev/presto driver.
Stores the starting kseg address of the cache that the /dev/presto driver uses. Section 7.6.1 shows that the envram_probe interface sets this address. Section 7.6.2 shows that the envram_attach interface passes this address to the /dev/presto driver's presto_init interface.
Stores the sysmap portion of the I/O handle. Section 7.6.1 shows that the envram_probe interface initializes this member. Section 7.9.1 and Section 7.9.2 show that the envram_read and envram_write interfaces pass this member to the I/O copy interfaces io_copyin and io_copyout. Section 7.10 shows that the envram_zero interface passes this member to the I/O copy interface io_zero.
Stores the size of the NVRAM cache. Section 7.6.1 shows that the envram_probe interface initializes this member. Section 7.6.2 shows that the envram_attach interface passes this size to the /dev/presto driver's presto_init interface.
Stores the offset to the first NVRAM location from the start of the EISA slot address. Section 7.6.1 shows that the envram_probe interface initializes this member.
Stores the bit masks that indicate whether the EISA bus NVRAM expansion board passed the software diagnostic tests. Section 7.6.1 shows that the envram_probe interface initializes this member. Section 7.7 shows that this member is an implicit input to the eisa_nvram_status interface.
Specifies a handle to DMA resources associated with the mapping of an in-memory I/O buffer onto a controller's I/O bus. This handle provides the information to access bus address/byte count pairs. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer. Device driver writers can view the DMA handle as the tag to the allocated system resources needed to perform a direct memory access (DMA) operation.
Section 7.6.2 and Section 7.9.2 show that the envram_attach and envram_write interfaces pass this DMA handle to the dma_map_load and dma_map_alloc interfaces.
Declares a pointer to the controller structure associated with this EISA bus NVRAM expansion board. Section 7.6.1 and Section 7.6.2 show that several members of the controller structure pointer are implicit inputs to the envram_probe and envram_attach interfaces.
The following code shows the Include Files Section for the /dev/envram device driver:
#include "envram.h" [1]
#include <vm/vm_kern.h> #include <sys/presto.h> [2] #include <io/common/devdriver.h> #include <io/dec/eisa/eisa.h> [3] #include <data/envram_data.c> [4] #include <machine/rpb.h> #include <io/dec/eisa/envram_reg.h> [5]
Section 7.6.2 shows how the /dev/envram driver initializes a data structure with the names of its entry points so that the /dev/presto driver can call them.
Writing Device Drivers: Reference provides reference page descriptions of the header files that Digital UNIX device drivers most commonly use. [Return to example]
Section 7.2 describes the contents of the envram_reg.h device register header file. [Return to example]
The following code shows the Declarations Section for the /dev/envram device driver:
#define ENVRAM_READIO_D8(a) \ read_io_port((io_handle_t)sc->regbase | a, 1, 0)) [1] #define ENVRAM_READIO_D16(a) \ read_io_port((io_handle_t)sc->regbase | a, 2, 0)) #define ENVRAM_READIO_D32(a) \ read_io_port((io_handle_t)sc->regbase | a, 4, 0))
#define ENVRAM_WRITEIO_D8(a,d) \ write_io_port((io_handle_t)sc->regbase | a, 1, 0, d)) [2] #define ENVRAM_WRITEIO_D16(a,d) \ write_io_port((io_handle_t)sc->regbase | a, 2, 0, d)) #define ENVRAM_WRITEIO_D32(a,d) \ write_io_port((io_handle_t)sc->regbase | a, 4, 0, d))
int envram_probe(), envram_attach(), eisa_nvram_status(); int eisa_nvram_battery_enable(), eisa_nvram_battery_disable(); void envram_read(), envram_write(), envram_zero(); [3]struct driver envramdriver = { envram_probe, 0, envram_attach, 0, 0, 0, 0, 0, "envram", envram_info, 0, 0, 0, 0, 0, 0, 0 }; [4]
The read_io_port interface takes three arguments:
For this first argument, the /dev/envram driver performs a bitwise inclusive OR operation on the value stored in the regbase member of the pointer to the envram_softc structure and an address representing one of the device register offsets, for example, ENVRAM_CSR.
For this third argument, the /dev/envram driver specifies the value zero (0).
The write_io_port interface takes four arguments:
For this first argument, the /dev/envram driver performs a bitwise inclusive OR operation on the value stored in the regbase member of the pointer to the envram_softc structure and an address representing one of the device register offsets, for example, ENVRAM_CSR.
For this third argument, the /dev/envram driver specifies the value zero (0).
For this fourth argument, the /dev/envram driver specifies a variable to be passed by the different device driver interfaces.
You index this array with the controller number as specified in the ctlr_num member of the controller structure.
Table 7-2 lists the three interfaces implemented as part of the Autoconfiguration Support Section for the /dev/envram device driver along with the sections in the book where each is described.
Interface | Section |
Implementing the envram_probe Interface | Section 7.6.1 |
Implementing the envram_attach Interface | Section 7.6.2 |
Implementing the envram_ssn Interface | Section 7.6.3 |
The envram_probe interface is called by the EISA bus configuration code at system boot time and performs the following tasks:
The envram_probe interface has the following implicit input:
.
.
.
ctlr->physaddr
The physaddr member stores the controller's base register physical address.
The following code implements envram_probe:
envram_probe(bus_io_handle, ctlr) io_handle_t bus_io_handle; [1] struct controller *ctlr; [2]
{ register struct envram_softc *sc; [3] u_int hw_id = 0; [4] struct bus_mem mem; [5] struct dma dma_p; [6] u_long eisa_addr_mask = 0xffffffff; [7]
if (ctlr->ctlr_num > 0) return(0); [8]
sc = (struct envram_softc *)kalloc(sizeof(struct envram_softc)); [9]
if (!sc) [10] return(0); bzero((char *)sc, sizeof(struct envram_softc)); [11] envram_softc = sc; [12]
sc->ctlr = ctlr; [13]
sc->regbase = bus_io_handle; [14]
hw_id = ENVRAM_READIO_D32(ENVRAM_ID); [15]
if (hw_id != ENVRAM_ID_MASK) [16] { printf("envram_probe: Failed to read ID register\n");
kfree(sc, sizeof(struct envram_softc)); return(0); } else printf("envram_probe: EISA NVRAM present\n");
sc->cache_offset = ENVRAM_CACHE_OFFSET; [17]
if (get_config(ctlr, RES_MEM, , &mem, 0)) { [18] printf("envram probe error\n"); return(0); } sc->cache_size = mem.size; [19] sc->cache_base = (u_long)mem.start_addr; sc->cache_phys_start = (u_long)(sc->cache_base + sc->cache_offset); sc->cache_kseg_start = (vm_offset_t) (PHYS_TO_KSEG(sc->cache_phys_start&eisa_addr_mask)); sc->saved_mem_sysmap = sc->cache_phys_start & ~eisa_addr_mask;
sc->cache_size = sc->cache_size - EISA_DIAG_RESVED; [20]
if (get_config(ctlr, EISA_DMA, , &dma_p, 0)) { [21] printf("envram probe error dma channel\n"); return(0); }
if (dma_p.channel != 7 && dma_p.channel != 5) { [22] printf("envram: invalid dma channel %d\n",dma_p.channel); return(0); }
ENVRAM_WRITEIO_D8(ENVRAM_CTRL, EISA_ENABLE_BOARD); [23] mb(); [24]
ENVRAM_WRITEIO_D16(ENVRAM_CSR,WRMEM); [25] mb(); [26]
envram_read(sc->cache_phys_start-8, &sc->diag_status, 4); [27]
if (sc->diag_status & BOARD_FAILED) { [28] printf("Envram diag reg 0x%x\n",sc->diag_status); sc->diag_status = 0; } else { sc->diag_status = 1; } return(1); [29] }
If kalloc successfully allocates the memory for the envram_softc structure, it returns the address of the allocated memory. The sc pointer now points to this memory. Otherwise, kalloc returns the value zero (0). [Return to example]
The bzero interface takes two arguments:
Section 7.3 shows how the /dev/envram driver uses read_io_port to construct ENVRAM_READIO_D32. [Return to example]
The envram_probe interface returns the value zero (0) to the bus configuration code to indicate an error and that the probe operation failed.
If the ID device register is valid, envram_probe calls printf to display a message on the console terminal. [Return to example]
The get_config interface takes five arguments:
In this call, envram_probe passes the value zero (0).
The following list describes the initialized members:
This member stores the size of the cache that the /dev/presto driver can use.
This member stores an I/O handle that indicates where the bus configuration code maps the memory block. The EISA/ISA bus configuration code sets the I/O handle during device autoconfiguration.
This member stores the starting physical address of the NVRAM cache. The envram_probe interface uses the physical starting address stored in cache_base and the offset to cache to calculate the starting physical address of the NVRAM cache.
This member stores the starting kseg address of the cache that the /dev/presto driver uses. The envram_probe interface obtains the starting kseg address by calling PHYS_TO_KSEG.
The PHYS_TO_KSEG interface takes one argument: the physical address to convert to a buffer virtual address. In this call, the physical address to convert is stored in the cache_phys_start member.
This member stores the sysmap portion of the I/O handle.
The envram_probe interface passes the same values to the first, third, and fifth arguments as it passed in the previous call to get_config. To request DMA channel-related information, envram_probe passes the constant RES_DMA to the second argument. The envram_probe interface passes the address of the dma data structure called dma_p0 to the fourth argument so that get_config can store in it the DMA channel-related information. [Return to example]
The ENVRAM_WRITEIO_D8 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D8 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the controller device register, ENVRAM_CTRL.
The ENVRAM_WRITEIO_D16 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D16 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the control status register, ENVRAM_CSR.
The envram_attach interface is called by the EISA bus configuration code at system boot time and performs the following tasks:
The envram_attach interface has the following implicit input:
.
.
.
ctlr->physaddr
The physaddr member stores the controller's base register physical address.
The following code implements envram_attach:
envram_attach(ctlr) struct controller *ctlr; [1] {
register struct envram_softc *sc = envram_softc; [2]
if (dma_map_alloc(ENVRAM_XFER_SIZE, sc->ctlr, &sc->sglp, 0) == 0) [3] panic("envram: dma_map_alloc error\n");
presto_interface0.nvram_status = eisa_nvram_status; [4] presto_interface0.nvram_battery_status= eisa_nvram_battery_status; presto_interface0.nvram_battery_disable= eisa_nvram_battery_disable; presto_interface0.nvram_battery_enable= eisa_nvram_battery_enable;
presto_interface0.nvram_ioreg_read = envram_read; [5] presto_interface0.nvram_ioreg_write = envram_write; presto_interface0.nvram_block_read = envram_read; presto_interface0.nvram_block_write = envram_write; presto_interface0.nvram_ioreg_zero = envram_zero; presto_interface0.nvram_block_zero = envram_zero;
presto_interface0.nvram_min_ioreg = sizeof(int); [6] presto_interface0.nvram_ioreg_align = sizeof(int); presto_interface0.nvram_min_block = PRFSIZE; presto_interface0.nvram_block_align = PRFSIZE;
presto_init(sc->cache_kseg_start, sc->cache_size, ENVRAM_NOTMAPPED, ENVRAM_CACHED, envram_ssn()); [7] }
The dma_map_alloc interface takes four arguments:
In this call, envram_attach passes the constant ENVRAM_XFER_SIZE, which represents the maximum size of the data to be transferred. Section 7.2 shows that this constant is defined in the envram_reg.h file.
In this call, envram_attach passes the ctlr pointer accessed through the sc pointer. Section 7.6.1 shows that envram_probe sets this ctlr pointer.
Typically, the device driver passes an argument of type dma_handle_t *. The dma_map_alloc interface returns to this variable the address of the DMA handle. The device driver uses this address in a call to dma_map_load.
In this call, envram_attach simply passes the address of sglp, the DMA handle accessed through the sc pointer. Section 7.3 shows that this pointer is defined as part of the envram_softc structure.
The /usr/sys/include/sys/presto.h file defines a data structure called presto_interface. It also declares an instance of this structure called presto_interface0. This file provides additional information on the members of the presto_interface structure. [Return to example]
The presto_init interface takes five arguments:
In this call, envram_attach passes the constant ENVRAM_NOTMAPPED to indicate that the NVRAM is not mapped. Section 7.2 shows that this constant (and the ENVRAM_MAPPED constant) are defined in the envram_reg.h file.
The envram_attach interface passes envram_ssn as an argument to presto_init. The presto_init interface calls envram_ssn to obtain the machine (CPU) ID.
The envram_ssn interface performs the following tasks:
The envram_ssn interface returns the machine (CPU) ID located in the HWRPB.
The following code implements envram_ssn:
envram_ssn() { extern struct rpb *rpb; [1]
u_int ssn = 0; [2] int i; char *cp;cp = rpb->rpb_ssn + 9; [3]
if (*cp == '\0') { [4] cp = "NO System Serial Number"+8; printf("envram_ssn: %s\n",cp-8); } for (i = 0 ; i < 8 ; i++, cp--){ [5] if (*cp < '9') ssn += (*cp - '0' ) << (i*4); else if (*cp < 'G') ssn += (*cp - 'A' + 0xa ) << (i*4); else if (*cp < 'a') ssn += ( *cp % 0xf ) << (i*4); else if (*cp < 'g') ssn += (*cp - 'a' + 0xa ) << (i*4); else ssn += ( *cp % 0xf ) << (i*4); } return(ssn); [6] }
The eisa_nvram_status interface provides the /dev/presto device driver with the status of diagnostics run on the NVRAM. Section 7.6.1 shows that the envram_probe interface sets the diag_status member to indicate whether the EISA bus NVRAM expansion board passed software diagnostic tests. Section 7.6.2 shows that the envram_attach interface sets the nvram_status member of the presto_interface0 structure to eisa_nvram_status. This is the mechanism the /dev/presto device driver uses to call the interface that returns the diagnostic status of the NVRAM.
The following code implements eisa_nvram_status:
int eisa_nvram_status() {
register struct envram_softc *sc = envram_softc; [1]
if (sc->diag_status) [2] return(NVRAM_RDONLY); else return(NVRAM_BAD); }
Table 7-3 lists the three interfaces implemented as part of the Battery Status Section for the /dev/envram device driver along with the sections in the book where each is described.
Part | Section |
Implementing the eisa_nvram_battery_status Interface | Section 7.8.1 |
Implementing the eisa_nvram_battery_enable Interface | Section 7.8.2 |
Implementing the eisa_nvram_battery_disable Interface | Section 7.8.3 |
The eisa_nvram_battery_status interface provides the /dev/presto device driver with the status of the battery on the NVRAM. Specifically, eisa_nvram_battery_status performs the following tasks:
Section 7.6.2 shows that the envram_attach interface sets the nvram_battery_status member of the presto_interface0 data structure to eisa_nvram_battery_status. This is the mechanism the /dev/presto device driver uses to call the interface that returns the status of the battery for the NVRAM.
The following code implements eisa_nvram_battery_status:
int eisa_nvram_battery_status() { register struct envram_softc *sc = envram_softc; [1]
nvram_batteries0.nv_nbatteries = 1; [2] nvram_batteries0.nv_minimum_ok = 1; nvram_batteries0.nv_primary_mandatory = 1; nvram_batteries0.nv_test_retries = 1;
if ((ENVRAM_READIO_D16(ENVRAM_CSR) & BAT_FAIL)) [3] { nvram_batteries0.nv_status[0] = BATT_OK; return(0); } else {
return(1); [4] } }
Stores the number of batteries that the hardware supports. The eisa_nvram_battery_status interface sets this member to the value 1, indicating that the EISA bus NVRAM expansion board supports one battery.
Stores the minimum number of batteries that are enabled and that have enough power for use by the /dev/presto driver. The eisa_nvram_battery_status interface sets this member to the value 1 because the EISA bus NVRAM expansion board supports only one battery.
Stores the value indicating whether the primary battery is operational. The eisa_nvram_battery_status interface sets this member to the value 1, indicating that the primary (and only) battery must be operational.
Stores the number of successive calls to eisa_nvram_battery_status for each battery check that the /dev/presto device driver makes. The eisa_nvram_battery_status sets this member to the value 1, indicating one retry.
Note the use of the ENVRAM_READIO_D16 interface to read the CSR. In this call, eisa_nvram_battery_status passes ENVRAM_CSR, which represents the CSR. The ENVRAM_READIO_D16 interface returns the requested data. Section 7.5 shows how the /dev/envram driver uses read_io_port to construct ENVRAM_READIO_D16. [Return to example]
The eisa_nvram_battery_enable interface is called by the /dev/presto device driver and performs the following tasks:
Section 7.6.2 shows that the envram_attach interface sets the nvram_battery_enable member of the presto_interface0 data structure to eisa_nvram_battery_enable. This is the mechanism the /dev/presto device driver uses to call the interface that enables the battery on the EISA bus NVRAM expansion board.
The following code implements eisa_nvram_battery_enable:
int eisa_nvram_battery_enable() {
register struct envram_softc *sc = envram_softc; [1]
ENVRAM_WRITEIO_D16(ENVRAM_CSR, WRMEM|SET_LED); [2] ENVRAM_WRITEIO_D8(ENVRAM_BAT,!BAT_DISCON_BIT); [3] mb(); [4]
return(0); [5] }
The ENVRAM_WRITEIO_D16 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D16 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the CSR device register offset called ENVRAM_CSR.
The ENVRAM_WRITEIO_D8 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D8 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the battery disconnect device register called ENVRAM_BAT.
The eisa_nvram_battery_disable interface performs the following tasks:
The eisa_nvram_battery_disable interface is called by the /dev/presto device driver when it needs to disable the battery on the EISA bus NVRAM expansion board. Section 7.6.2 shows that the envram_attach interface sets the nvram_battery_disable member of the presto_interface0 data structure to eisa_nvram_battery_disable. This is the mechanism the /dev/presto device driver uses to call the interface that enables the battery on the EISA bus NVRAM expansion board.
The following code implements eisa_nvram_battery_disable:
int eisa_nvram_battery_disable() {
register struct envram_softc *sc = envram_softc; [1]
ENVRAM_WRITEIO_D16(ENVRAM_CSR,WRMEM); [2] ENVRAM_WRITEIO_D8(ENVRAM_BAT,BAT_DISCON_BIT); [3] mb(); ENVRAM_WRITEIO_D8(ENVRAM_BAT,BAT_DISCON_BIT); mb(); ENVRAM_WRITEIO_D8(ENVRAM_BAT,!BAT_DISCON_BIT); mb(); ENVRAM_WRITEIO_D8(ENVRAM_BAT,!BAT_DISCON_BIT); mb(); ENVRAM_WRITEIO_D8(ENVRAM_BAT,BAT_DISCON_BIT); mb();
return(0); [4] }
The ENVRAM_WRITEIO_D16 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D16 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the CSR device register offset called ENVRAM_CSR.
The ENVRAM_WRITEIO_D8 interface writes a byte (the BAT_DISCON_BIT battery disconnect bit mask) to the battery disconnect device register (represented by the ENVRAM_BAT offset) located in the bus I/O space. Section 7.5 shows how the /dev/envram driver uses the write_io_port interface to construct ENVRAM_WRITEIO_D8.
The ENVRAM_WRITEIO_D8 interface takes two arguments:
In this call, ENVRAM_WRITEIO_D8 performs a bitwise inclusive OR operation on the value stored in the regbase member of the sc pointer and the battery disconnect device register offset called ENVRAM_BAT.
Table 7-4 lists the two interfaces implemented as part of the Read and Write Device Section for the /dev/envram device driver along with the sections in the book where each is described.
Part | Section |
Implementing the envram_read Interface | Section 7.9.1 |
Implementing the envram_write Interface | Section 7.9.2 |
The envram_read interface is called by the envram_probe interface and the /dev/presto device driver and performs the following tasks:
Section 7.6.2 shows that the envram_attach interface sets the nvram_ioreg_read (read small pieces of NVRAM) and nvram_block_read (read large pieces of NVRAM) members of the presto_interface0 data structure to envram_read. These members both point to the same interface, which means envram_read handles both small and large reads from the EISA bus NVRAM expansion board.
Note
An xxread interface implemented on Digital UNIX typically has three arguments: dev, uio, and flag. The reason that envram_read has different arguments is that it is not called directly from the I/O system as the result of a read system call. The read request from the I/O system is made to the /dev/presto driver's read entry point, which then calls envram_read to perform the actual read operation.
The following code implements envram_read:
void envram_read(source, dest, len) caddr_t source; [1] caddr_t dest; [2] u_int len; [3] {
register struct envram_softc *sc = envram_softc; [4]
io_copyin((io_handle_t) KSEG_TO_PHYS((u_long)source|sc->saved_mem_sysmap), (vm_offset_t)dest,len); [5] }
The io_copyin interface takes three arguments:
In this call, the I/O handle is actually the physical address returned by KSEG_TO_PHYS.
The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address. In this call, the buffer virtual address is the result of ORing the source address and the value stored in the sysmap portion of the I/O handle. The envram_probe interface and the /dev/presto driver pass this source address to envram_read.
The envram_write interface is called by the /dev/presto device driver and performs the following tasks:
Section 7.6.2 shows that the envram_attach interface sets the nvram_ioreg_write (write small pieces of NVRAM) and nvram_block_write (write large pieces of NVRAM) members of the presto_interface0 data structure to envram_write. These members both point to the same interface. This means envram_write handles both small and large reads from the EISA bus NVRAM expansion board.
Note
An xxwrite interface implemented on Digital UNIX typically has three arguments: dev, uio, and flag. The reason that envram_write has different arguments is that it is not called directly from the I/O system as the result of a write system call. The read request from the I/O system is made to the /dev/presto driver's write entry point, which then calls envram_write to perform the actual write operation.
The following code implements envram_write:
void envram_write(source, dest, len) caddr_t source; [1] caddr_t dest; [2] u_int len; [3] {
register struct envram_softc *sc = envram_softc; [4] vm_offset_t destptr; [5] register int xfer; [6] int retry; [7] char *ddest = dest; [8]
if (len > 32) { [9]
destptr = KSEG_TO_PHYS(dest) - sc->cache_base; [10] ddest = (char *)destptr; [11]
if (!(xfer = ENVRAM_XFER_SIZE - ((int)ddest & (ENVRAM_XFER_SIZE-1)))) [12] xfer = ENVRAM_XFER_SIZE;
if (xfer > len) xfer = len;
if ((u_int)source/ENVRAM_ALLIGN != ((u_int)source+xfer)/ENVRAM_ALLIGN) [13] xfer = xfer - (((u_int)source+xfer) & (ENVRAM_ALLIGN-1));
while (1) {
if (!(dma_map_load(xfer, source, (struct proc *)0, [14] sc->ctlr, &sc->sglp, 0, DMA_OUT))) panic("envram: dma_map_load failure\n");
ENVRAM_WRITEIO_D16(ENVRAM_DMA0,((u_int)(ddest-4) << 6)); [15] ENVRAM_WRITEIO_D16(ENVRAM_DMA1,((u_int)ddest >> 5));
ENVRAM_WRITEIO_D16(ENVRAM_CSR,SET_DREQ|WRMEM|SET_LED); [16] mb(); [17] len -= xfer; [18] source += xfer; ddest += xfer;
if (!(xfer = ENVRAM_XFER_SIZE - (((int)ddest [19] & (ENVRAM_XFER_SIZE-1))))) xfer = ENVRAM_XFER_SIZE;
if (xfer > len) xfer = len; if ((u_int)source/ENVRAM_ALLIGN != [20] ((u_int)source+xfer)/ENVRAM_ALLIGN) xfer = xfer - (((u_int)source+xfer) & (ENVRAM_ALLIGN-1));
retry = 10; [21] while (--retry) if (!(ENVRAM_READIO_D16(ENVRAM_CSR) & SET_DREQ)) break;
if (!length) break;
if (!retry) [22] panic("envram: DMA retry expired\n"); } return; }
io_copyout((vm_offset_t)source, (io_handle_t) (KSEG_TO_PHYS((u_long)dest)|sc->saved_mem_sysmap), len); [23] }
The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address. In this call, the buffer virtual address is the result of subtracting the physical starting address where the memory block was mapped to from the location of the write. The /dev/presto driver passes this location to envram_write. [Return to example]
The dma_map_load interface takes seven arguments:
In this call, envram_write passes the size stored in the xfer variable.
In this call, envram_write passes the address contained in source. The /dev/presto driver passed in this address.
In this call, envram_write passes the value zero (0) to indicate that the address is a kernel address.
In this call, envram_write passes the controller structure pointer associated with this EISA bus NVRAM expansion board. Section 7.6.1 shows that envram_probe set this controller structure pointer in the softc structure.
In this call, envram_write passes the address of the DMA handle defined in the softc structure. Section 7.3 shows the declaration of the sglp member in the softc structure.
The io_copyout interface takes three arguments:
In this call, envram_write passes the address contained in the source argument. The /dev/presto driver passed in this address.
In this call, the I/O handle is actually the physical address returned by KSEG_TO_PHYS.
The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address. In this call, the buffer virtual address is the result of ORing the destination address and the value stored in the sysmap portion of the I/O handle. The /dev/presto driver passes this destination address to envram_write.
The envram_zero interface is called by the /dev/presto device driver to zero (clear) a specified length of NVRAM starting at a specified address. Section 7.6.2 shows that the envram_attach interface sets the nvram_ioreg_zero (zero small pieces of NVRAM) and nvram_block_zero (zero large pieces of NVRAM) members of the presto_interface0 data structure to envram_zero. These members both point to the same interface. This means envram_zero zeros (clears) both small and large lengths from the EISA bus NVRAM expansion board.
The following code implements envram_zero:
void envram_zero(addr, len) caddr_t addr; [1] u_int len; [2] {
register struct envram_softc *sc = envram_softc; [3]
io_zero((io_handle_t) KSEG_TO_PHYS((u_long)addr|sc->saved_mem_sysmap), len); [4] }
The io_zero interface takes two arguments:
In this call, the I/O handle is actually the physical address returned by KSEG_TO_PHYS.
The KSEG_TO_PHYS interface takes one argument: the buffer virtual address to convert to a physical address. In this call, the buffer virtual address is the result of ORing the source address and the value stored in the sysmap portion of the I/O handle. The envram_probe interface and the /dev/presto driver pass this source address to envram_zero.