Writing Device Drivers

Part IV Appendixes

The appendixes provide the following background material:

Appendix A Hardware Overview

This appendix discusses general issues about hardware that is capable of supporting the Oracle Solaris OS. The discussion includes the processor, bus architectures, and memory models that are supported by the operating system. Various device issues and the PROM used in Oracle platforms are also covered.


Note –

The material in this appendix is for informational purposes only. This information might be of use during driver debugging. However, many of these implementation details are hidden from device drivers by the Oracle Solaris DDI/DKI interfaces.


This appendix provides information on the following subjects:

SPARC Processor Issues

This section describes a number of SPARC processor-specific topics such as data alignment, byte ordering, register windows, and availability of floating-point instructions. For information on x86 processor-specific topics, see x86 Processor Issues.


Note –

Drivers should never perform floating-point operations, because these operations are not supported in the kernel.


SPARC Data Alignment

All quantities must be aligned on their natural boundaries, using standard C data types:

Usually, the compiler handles any alignment issues. However, driver writers are more likely to be concerned about alignment because the proper data types must be used to access the devices. Because device registers are commonly accessed through a pointer reference, drivers must ensure that pointers are properly aligned when accessing the device.

Member Alignment in SPARC Structures

Because of the data alignment restrictions imposed by the SPARC processor, C structures also have alignment requirements. Structure alignment requirements are imposed by the most strictly aligned structure component. For example, a structure containing only characters has no alignment restrictions, while a structure containing a long long member must be constructed to guarantee that this member falls on a 64-bit boundary.

SPARC Byte Ordering

The SPARC processor uses big-endian byte ordering. The most significant byte (MSB) of an integer is stored at the lowest address of the integer. The least significant byte is stored at the highest address for words in this processor. For example, byte 63 is the least significant byte for 64-bit processors.

Diagram shows how bytes are ordered in big-endian architectures,
that is, byte 0 is the most significant byte.

SPARC Register Windows

SPARC processors use register windows. Each register window consists of eight in registers, eight local registers, eight out registers, and eight global registers. Out registers are the in registers for the next window. The number of register windows ranges from 2 to 32, depending on the processor implementation.

Because drivers are normally written in C, the compiler usually hides the fact that register windows are used. However, you might have to use register windows when debugging the driver.

SPARC Multiply and Divide Instructions

The Version 7 SPARC processors do not have multiply or divide instructions. The multiply and divide instructions are emulated in software. Because a driver might run on a Version 7, Version 8, or Version 9 processor, avoid intensive integer multiplication and division. Instead, use bitwise left and right shifts to multiply and divide by powers of two.

The SPARC Architecture Manual, Version 9, contains more specific information on the SPARC CPU. The SPARC Compliance Definition, Version 2.4, contains details of the application binary interface (ABI) for SPARC V9. The manual describes the 32-bit SPARC V8 ABI and the 64-bit SPARC V9 ABI. You can obtain this document from SPARC International at http://www.sparc.com.

x86 Processor Issues

Data types have no alignment restrictions. However, extra memory cycles might be required for the x86 processor to properly handle misaligned data transfers.


Note –

Drivers should not perform floating-point operations, as these operations are not supported in the kernel.


x86 Byte Ordering

The x86 processors use little-endian byte ordering. The least significant byte (LSB) of an integer is stored at the lowest address of the integer. The most significant byte is stored at the highest address for data items in this processor. For example, byte 7 is the most significant byte for 64-bit processors.

Diagram shows how bytes are ordered in little-endian
architectures, that is, byte 0 is the least significant byte.

x86 Architecture Manuals

Both Intel Corporation and AMD publish a number of books on the x86 family of processors. See http://www.intel.com and http://www.amd.com/.

Endianness

To achieve the goal of multiple-platform, multiple-instruction-set architecture portability, host bus dependencies were removed from the drivers. The first dependency issue to be addressed was the endianness, that is, byte ordering, of the processor. For example, the x86 processor family is little-endian while the SPARC architecture is big-endian.

Bus architectures display the same endianness types as processors. The PCI local bus, for example, is little-endian, the SBus is big-endian, the ISA bus is little-endian, and so on.

To maintain portability between processors and buses, DDI-compliant drivers must be endian neutral. Although drivers can manage their endianness by runtime checks or by preprocessor directives like #ifdef _LITTLE_ENDIAN in the source code, long-term maintenance can be troublesome. In some cases, the DDI framework performs the byte swapping using a software approach. In other cases, byte swapping can be done by hardware page-level swapping as in memory management unit (MMU) or by special machine instructions. The DDI framework can take advantage of the hardware features to improve performance.

Figure A–1 Byte Ordering Required for Host Bus Dependency

Diagram shows byte swapping to reverse endianness.

Along with being endian-neutral, portable drivers must also be independent from data ordering of the processor. Under most circumstances, data must be transferred in the sequence instructed by the driver. However, sometimes data can be merged, batched, or reordered to streamline the data transfer, as illustrated in the following figure. For example, data merging can be applied to accelerate graphics display on frame buffers. Drivers have the option to advise the DDI framework to use other optimal data transfer mechanisms during the transfer.

Figure A–2 Data Ordering Host Bus Dependency

Diagram shows reordering of bytes by CPU.

Store Buffers

To improve performance, the CPU uses internal store buffers to temporarily store data. Using internal buffers can affect the synchronization of device I/O operations. Therefore, the driver needs to take explicit steps to make sure that writes to registers are completed at the proper time.

For example, consider the case where access to device space, such as registers or a frame buffer, is synchronized by a lock. The driver needs to check that the store to the device space has actually completed before releasing the lock. The release of the lock does not guarantee the flushing of I/O buffers.

To give another example, when acknowledging an interrupt, the driver usually sets or clears a bit in a device control register. The driver must ensure that the write to the control register has reached the device before the interrupt handler returns. Similarly, a device might require a delay, that is, driver busy-waits, after writing a command to the control register. In such a case, the driver must ensure that the write has reached the device before delaying.

Where device registers can be read without undesirable side effects, verification of a write can simply consist of reading the register immediately after the write. If that particular register cannot be read without undesirable side effects, another device register in the same register set can be used.

System Memory Model

The system memory model defines the semantics of memory operations such as load and store and specifies how the order in which these operations are issued by a processor is related to the order in which they reach memory. The memory model applies to both uniprocessors and shared-memory multiprocessors. Two memory models are supported: total store ordering (TSO) and partial store ordering (PSO).

Total Store Ordering (TSO)

TSO guarantees that the sequence in which store, FLUSH, and atomic load-store instructions appear in memory for a given processor is identical to the sequence in which they were issued by the processor.

Both x86 and SPARC processors support TSO.

Partial Store Ordering (PSO)

PSO does not guarantee that the sequence in which store, FLUSH, and atomic load-store instructions appear in memory for a given processor is identical to the sequence in which they were issued by the processor. The processor can reorder the stores so that the sequence of stores to memory is not the same as the sequence of stores issued by the CPU.

SPARC processors support PSO; x86 processors do not.

For SPARC processors, conformance between issuing order and memory order is provided by the system framework using the STBAR instruction. If two of the above instructions are separated by an STBAR instruction in the issuing order of a processor, or if the instructions reference the same location, the memory order of the two instructions is the same as the issuing order. Enforcement of strong data-ordering in DDI-compliant drivers is provided by the ddi_regs_map_setup(9F) interface. Compliant drivers cannot use the STBAR instruction directly.

See the SPARC Architecture Manual, Version 9, for more details on the SPARC memory model.

Bus Architectures

This section describes device identification, device addressing, and interrupts.

Device Identification

Device identification is the process of determining which devices are present in the system. Some devices are self-identifying meaning that the device itself provides information to the system so that the system can identify the device driver that needs to be used. SBus and PCI local bus devices are examples of self-identifying devices. On SBus, the information is usually derived from a small Forth program stored in the FCode PROM on the device. Most PCI devices provide a configuration space containing device configuration information. See the sbus(4) and pci(4) man pages for more information.

All modern bus architectures require devices to be self-identifying.

Supported Interrupt Types

The Oracle Solaris platform supports both polling and vectored interrupts. The Oracle Solaris DDI/DKI interrupt model is the same for both types of interrupts. See Chapter 8, Interrupt Handlers for more information about interrupt handling.

Bus Specifics

This section covers addressing and device configuration issues specific to the buses that the Oracle Solaris platform supports.

PCI Local Bus

The PCI local bus is a high-performance bus designed for high-speed data transfer. The PCI bus resides on the system board. This bus is normally used as an interconnect mechanism between highly integrated peripheral components, peripheral add-on boards, and host processor or memory systems. The host processor, main memory, and the PCI bus itself are connected through a PCI host bridge, as shown in Figure A–3.

A tree structure of interconnected I/O buses is supported through a series of PCI bus bridges. Subordinate PCI bus bridges can be extended underneath the PCI host bridge to enable a single bus system to be expanded into a complex system with multiple secondary buses. PCI devices can be connected to one or more of these secondary buses. In addition, other bus bridges, such as SCSI or USB, can be connected.

Every PCI device has a unique vendor ID and device ID. Multiple devices of the same kind are further identified by their unique device numbers on the bus where they reside.

Figure A–3 Machine Block Diagram

Diagram shows how a PCI host bridge connects the CPU
and main memory to a PCI bus.

The PCI host bridge provides an interconnect between the processor and peripheral components. Through the PCI host bridge, the processor can directly access main memory independent of other PCI bus masters. For example, while the CPU is fetching data from the cache controller in the host bridge, other PCI devices can also access the system memory through the host bridge. The advantage of this architecture is that this architecture separates the I/O bus from the processor's host bus.

The PCI host bridge also provides data access mappings between the CPU and peripheral I/O devices. The bridge maps every peripheral device to the host address domain so that the processor can access the device through programmed I/O. On the local bus side, the PCI host bridge maps the system memory to the PCI address domain so that the PCI device can access the host memory as a bus master. Figure A–3 shows the two address domains.

PCI Address Domain

The PCI address domain consists of three distinct address spaces: configuration, memory, and I/O space.

PCI Configuration Address Space

Configuration space is defined geographically. The location of a peripheral device is determined by its physical location within an interconnected tree of PCI bus bridges. A device is located by its bus number and device (slot) number. Each peripheral device contains a set of well-defined configuration registers in its PCI configuration space. The registers are used not only to identify devices but also to supply device configuration information to the configuration framework. For example, base address registers in the device configuration space must be mapped before a device can respond to data access.

The method for generating configuration cycles is host dependent. In x86 machines, special I/O ports are used. On other platforms, the PCI configuration space can be memory-mapped to certain address locations corresponding to the PCI host bridge in the host address domain. When a device configuration register is accessed by the processor, the request is routed to the PCI host bridge. The bridge then translates the access into proper configuration cycles on the bus.

PCI Configuration Base Address Registers

The PCI configuration space consists of up to six 32-bit base address registers for each device. These registers provide both size and data type information. System firmware assigns base addresses in the PCI address domain to these registers.

Each addressable region can be either memory or I/O space. The value contained in bit 0 of the base address register identifies the type. A value of 0 in bit 0 indicates a memory space and a value of 1 indicates an I/O space. The following figure shows two base address registers: one for memory and the other for I/O types.

Figure A–4 Base Address Registers for Memory and I/O

Diagram shows how bit 0 in a base address indicates a
memory or I/O space.

PCI Memory Address Space

PCI supports both 32-bit and 64-bit addresses for memory space. System firmware assigns regions of memory space in the PCI address domain to PCI peripherals. The base address of a region is stored in the base address register of the device's PCI configuration space. The size of each region must be a power of two, and the assigned base address must be aligned on a boundary equal to the size of the region. Device addresses in memory space are memory-mapped into the host address domain so that data access to any device can be performed by the processor's native load or store instructions.

PCI I/O Address Space

PCI supports 32-bit I/O space. I/O space can be accessed differently on different platforms. Processors with special I/O instructions, like the Intel processor family, access the I/O space with in and out instructions. Machines without special I/O instructions will map to the address locations corresponding to the PCI host bridge in the host address domain. When the processor accesses the memory-mapped addresses, an I/O request will be sent to the PCI host bridge, which then translates the addresses into I/O cycles and puts them on the PCI bus. Memory-mapped I/O is performed by the native load/store instructions of the processor.

PCI Hardware Configuration Files

Hardware configuration files should be unnecessary for PCI local bus devices. However, on some occasions drivers for PCI devices need to use hardware configuration files to augment the driver private information. See the driver.conf(4) and pci(4) man pages for further details.

PCI Express

The standard PCI bus has evolved into PCI Express. PCI Express is the next generation high performance I/O bus for connecting peripheral devices in such applications as desktop, mobile, workstation, server, embedded computing and communication platforms.

PCI Express improves bus performance, reduces overall system cost and takes advantage of new developments in computer design. PCI Express uses a serial, point-to-point type interconnect for communication between two devices. Using switches enables users to connect a large number of devices together in a system. Serial interconnect implies fewer pins per device package, which reduces cost and makes the performance highly scalable.

The PCI Express bus has built-in features to accommodate the following technologies:

A PCI Express interconnect that connects two devices together is called a link. A link can either be x1, x2, x4, x8, x12, x16 or x32 bidirectional signal pairs. These signals are called lanes. The bandwidth (x1) of each lane is 500 MB/sec in duplex mode. Although PCI-X and PCI Express have different hardware connections, the two buses are identical from a driver writer's point of view. PCI-X is a shared bus. For example, all the devices on the bus share a single set of data lines and signal lines. PCI-Express is a switched bus, which enables more efficient use of the bandwidth between the devices and the system bus.

For more information on PCI Express, please refer to the following web site: http://www.pcisig.com/home

SBus

Typical SBus systems consist of a motherboard (containing the CPU and SBus interface logic), a number of SBus devices on the motherboard itself, and a number of SBus expansion slots. An SBus can also be connected to other types of buses through an appropriate bus bridge.

The SBus is geographically addressed. Each SBus slot exists at a fixed physical address in the system. An SBus card has a different address, depending on which slot it is plugged into. Moving an SBus device to a new slot causes the system to treat this device as a new device.

The SBus uses polling interrupts. When an SBus device interrupts, the system only knows which of several devices might have issued the interrupt. The system interrupt handler must ask the driver for each device whether that device is responsible for the interrupt.

SBus Physical Address Space

The following table shows the physical address space layout of the Sun UltraSPARC 2 computer. A physical address on the UltraSPARC 2 model consists of 41 bits. The 41-bit physical address space is further broken down into multiple 33-bit address spaces identified by PA(40:33).

Table A–1 Device Physical Space in the Ultra 2

PA(40:33) 

33-bit Space 

Usage 

0x0 

0x000000000 - 0x07FFFFFFF

2 Gbytes main memory 

0x80 – 0xDF 

Reserved on Ultra 2

Reserved on Ultra 2 

0xE0 

Processor 0

Processor 0 

0xE1 

Processor 1

Processor 1 

0xE2 – 0xFD 

Reserved on Ultra 2

Reserved on Ultra 2 

0xFE 

0x000000000 - 0x1FFFFFFFF

UPA Slave (FFB) 

0xFF 

0x000000000 - 0x0FFFFFFFF

System I/O space 

 

0x100000000 - 0x10FFFFFFF

SBus Slot 0 

 

0x110000000 - 0x11FFFFFFF

SBus Slot 1 

 

0x120000000 - 0x12FFFFFFF

SBus Slot 2 

 

0x130000000 - 0x13FFFFFFF

SBus Slot 3 

 

0x1D0000000 - 0x1DFFFFFFF

SBus Slot D 

 

0x1E0000000 - 0x1EFFFFFFF

SBus Slot E 

 

0x1F0000000 - 0x1FFFFFFFF

SBus Slot F 

Physical SBus Addresses

The SBus has 32 address bits, as described in the SBus Specification. The following table describes how the Ultra 2 uses the address bits.

Table A–2 Ultra 2 SBus Address Bits

Bits 

Description 

0 - 27 

These bits are the SBus address lines used by an SBus card to address the contents of the card. 

28 - 31 

Used by the CPU to select one of the SBus slots. These bits generate the SlaveSelect lines. 

This addressing scheme yields the Ultra 2 addresses shown in Table A–1. Other implementations might use a different number of address bits.

The Ultra 2 has seven SBus slots, four of which are physical. Slots 0 through 3 are available for SBus cards. Slots 4-12 are reserved. The slots are used as follows:

SBus Hardware Configuration Files

Hardware configuration files are normally unnecessary for SBus devices. However, on some occasions, drivers for SBus devices need to use hardware configuration files to augment the information provided by the SBus card. See the driver.conf(4) and sbus(4) man page for further details.

Device Issues

This section describes issues with special devices.

Timing-Critical Sections

While most driver operations can be performed without mechanisms for synchronization and protection beyond those provided by the locking primitives, some devices require that a sequence of events occur in order without interruption. In conjunction with the locking primitives, the function ddi_enter_critical(9F) asks the system to guarantee, to the best of its ability, that the current thread will neither be preempted nor interrupted. This guarantee stays in effect until a closing call to ddi_exit_critical(9F) is made. See the ddi_enter_critical(9F) man page for details.

Delays

Many chips specify that they can be accessed only at specified intervals. For example, the Zilog Z8530 SCC has a “write recovery time” of 1.6 microseconds. This specification means that a delay must be enforced with drv_usecwait(9F) when writing characters with an 8530. In some instances, the specifications do not make explicit what delays are needed, so the delays must be determined empirically.

Be careful not to compound delays for parts of devices that might exist in large numbers, for example, thousands of SCSI disk drives.

Internal Sequencing Logic

Devices with internal sequencing logic map multiple internal registers to the same external address. The various kinds of internal sequencing logic include the following types:

Interrupt Issues

Note the following common interrupt-related issues:

PROM on SPARC Machines

Some platforms have a PROM monitor that provides support for debugging a device without an operating system. This section describes how to use the PROM on SPARC machines to map device registers so that they can be accessed. Usually, the device can be exercised enough with PROM commands to determine whether the device is working correctly.

See the boot(1M) man page for a description of the x86 boot subsystem.

The PROM has several purposes, including:

Open Boot PROM 3

For complete documentation on the Open Boot PROM, see the Open Boot PROM Toolkit User's Guide and the monitor(1M) man page. The examples in this section refer to a Sun4U architecture. Other architectures might require different commands to perform actions.


Note –

The Open Boot PROM is currently used on Sun machines with an SBus or UPA/PCI. The Open Boot PROM uses an “ok” prompt. On older machines, you might have to type `n' to get the “ok” prompt.


If the PROM is in secure mode (the security-mode parameter is not set to none), the PROM password might be required (set in the security-password parameter).

The printenv command displays all parameters and their values.

Help is available with the help command.

EMACS-style command-line history is available. Use Control-N (next) and Control-P (previous) to traverse the history list.

Forth Commands

The Open Boot PROM uses the Forth programming language. Forth is a stack-based language. Arguments must be pushed on the stack before running the correct command (called a word), and the result is left on the stack.

To place a number on the stack, type its value.


ok 57
ok 68

To add the two top values on the stack, use the + operator.


ok +

The result remains on the stack. The stack is shown with the .s word.


ok .s
bf

The default base is hexadecimal. The hex and decimal words can be used to switch bases.


ok decimal
ok .s
191

See the Forth User's Guide for more information.

Walking the PROMs Device Tree

The commands pwd, cd, and ls walk the PROM device tree to get to the device. The cd command must be used to establish a position in the tree before pwd will work. This example is from an Ultra 1 workstation with a cgsix frame buffer on an SBus.


ok cd /

To see the devices attached to the current node in the tree, use ls.


ok ls
f006a064 SUNW,UltraSPARC@0,0
f00598b0 sbus@1f,0
f00592dc counter-timer@1f,3c00
f004eec8 virtual-memory
f004e8e8 memory@0,0
f002ca28 aliases
f002c9b8 options
f002c880 openprom
f002c814 chosen
f002c7a4 packages

The full node name can be used:


ok cd sbus@1f,0
ok ls
f006a4e4 cgsix@2,0
f0068194 SUNW,bpp@e,c800000
f0065370 ledma@e,8400010
f006120c espdma@e,8400000
f005a448 SUNW,pll@f,1304000
f005a394 sc@f,1300000
f005a24c zs@f,1000000
f005a174 zs@f,1100000
f005a0c0 eeprom@f,1200000
f0059f8c SUNW,fdtwo@f,1400000
f0059ec4 flashprom@f,0
f0059e34 auxio@f,1900000
f0059d28 SUNW,CS4231@d,c000000

Rather than using the full node name in the previous example, you could also use an abbreviation. The abbreviated command-line entry looks like the following example:


ok cd sbus

The name is actually device@slot,offset (for SBus devices). The cgsix device is in slot 2 and starts at offset 0. If an SBus device is displayed in this tree, the device has been recognized by the PROM.

The .properties command displays the PROM properties of a device. These properties can be examined to determine which properties the device exports. This information is useful later to ensure that the driver is looking for the correct hardware properties. These properties are the same properties that can be retrieved with ddi_getprop(9F).


ok cd cgsix
ok .properties
character-set            ISO8859-1
intr                     00000005 00000000
interrupts               00000005
reg                      00000002 00000000 01000000
dblbuf                   00 00 00 00
vmsize                   00 00 00 01
...

The reg property defines an array of register description structures containing the following fields:


uint_t        bustype;       /* cookie for related bus type*/
uint_t        addr;          /* address of reg relative to bus */
uint_t        size;          /* size of this register set */

For the cgsix example, the address is 0.

Mapping the Device

A device must be mapped into memory to be tested. The PROM can then be used to verify proper operation of the device by using data-transfer commands to transfer bytes, words, and long words. If the device can be operated from the PROM, even in a limited way, the driver should also be able to operate the device.

To set up the device for initial testing, perform the following steps:

  1. Determine the SBus slot number the device is in.

    In this example, the cgsix device is located in slot 2.

  2. Determine the offset within the physical address space used by the device.

    The offset used is specific to the device. In the cgsix example, the video memory happens to start at an offset of 0x800000.

  3. Use the select-dev word to select the Sbus device and the map-in word to map the device in.

    The select-dev word takes a string of the device path as its argument. The map-in word takes an offset, a slot number, and a size as arguments to map. Like the offset, the size of the byte transfer is specific to the device. In the cgsix example, the size is set to 0x100000 bytes.

    In the following code example, the Sbus path is displayed as an argument to the select-dev word, and the offset, slot number, and size values for the frame buffer are displayed as arguments to the map-in word. Notice the space between the opening quote and / in the select-dev argument. The virtual address to use remains on top of the stack. The stack is shown using the .s word. The stack can be assigned a name with the constant operation.


    ok " sbus@1f,0" select-dev
    ok 800000 2 100000 map-in
    ok .s
    ffe98000
    ok constant fb
    

Reading and Writing

The PROM provides a variety of 8-bit, 16-bit, and 32-bit operations. In general, a c (character) prefix indicates an 8-bit (one-byte) operation; a w (word) prefix indicates a 16-bit (two-byte) operation; and an L (longword) prefix indicates a 32-bit (four-byte) operation.

A suffix of ! indicates a write operation. The write operation takes the first two items off the stack. The first item is the address, and the second item is the value.


ok 55 ffe98000 c!

A suffix of @ indicates a read operation. The read operation takes the address off the stack.


ok ffe98000 c@
ok .s
55

A suffix of ? is used to display the value without affecting the stack.


ok ffe98000 c?
55

Be careful when trying to query the device. If the mappings are not set up correctly, trying to read or write could cause errors. Special words are provided to handle these cases. cprobe, wprobe, and lprobe, for example, read from the given address but return zero if the location does not respond, or nonzero if it does.


ok fffa4000 c@
Data Access Error

ok fffa4000 cprobe
ok .s0

ok ffe98000 cprobe
ok .s
0 ffffffffffffffff

A region of memory can be shown with the dump word. This takes an address and a length, and displays the contents of the memory region in bytes.

In the following example, the fill word is used to fill video memory with a pattern. fill takes the address, the number of bytes to fill, and the byte to use. Use wfill and an Lfill for words and longwords. This fill example causes the cgsix to display simple patterns based on the byte passed.


ok " /sbus" select-dev
ok 800000 2 100000 map-in
ok constant fb
ok fb 10000 ff fill
ok fb 20000 0 fill
ok fb 18000 55 fill
ok fb 15000 3 fill
ok fb 10000 5 fillok fb 5000 f9 fill

Appendix B Summary of Oracle Solaris DDI/DKI Services

This appendix discusses the interfaces provided by the Oracle Solaris DDI/DKI. These descriptions should not be considered complete or definitive, nor do they provide a thorough guide to usage. The descriptions are intended to describe what the functions do in general terms. See physio(9F) for more detailed information. The categories are:

Module Functions

The module functions are:

mod_info

Query a loadable module

mod_install

Add a loadable module

mod_remove

Remove a loadable module

Device Information Tree Node (dev_info_t) Functions

The device information tree node functions are:

ddi_binding_name()

Return driver binding name

ddi_dev_is_sid()

Tell whether a device is self-identifying

ddi_driver_major()

Return driver major device number

ddi_driver_name()

Return normalized driver name

ddi_node_name()

Return the devinfo node name

ddi_get_devstate()

Check device state

ddi_get_instance()

Get device instance number

ddi_get_name()

Return driver binding name

ddi_get_parent()

Find the parent of a device information structure

ddi_root_node()

Get the root of the dev_info tree

Device (dev_t) Functions

The device functions are:

ddi_create_minor_node()

Create a minor node for a device

ddi_getiminor()

Get kernel internal minor number from an external dev_t

ddi_remove_minor_node()

Remove a minor mode for a device

getmajor()

Get major device number

getminor()

Get minor device number

makedevice()

Make device number from major and minor numbers

Property Functions

The property functions are:

ddi_prop_exists()

Check for the existence of a property

ddi_prop_free()

Free resources consumed by property lookup

ddi_prop_get_int()

Look up integer property

ddi_prop_get_int64()

Look up 64-bit integer property

ddi_prop_lookup_byte_array()

Look up byte array property

ddi_prop_lookup_int_array()

Look up integer array property

ddi_prop_lookup_int64_array()

Look up 64-bit integer array property

ddi_prop_lookup_string()

Look up string property

ddi_prop_lookup_string_array()

Look up string array property

ddi_prop_remove()

Remove a property of a device

ddi_prop_remove_all()

Remove all properties of a device

ddi_prop_undefine()

Hide a property of a device

ddi_prop_update_byte_array()

Create or update byte array property

ddi_prop_update_int()

Create or update integer property

ddi_prop_update_int64()

Create or update 64-bit integer property

ddi_prop_update_int_array()

Create or update integer array property

ddi_prop_update_int64_array()

Create or update 64-bit integer array property

ddi_prop_update_string()

Create or update string property

ddi_prop_update_string_array()

Create or update string array property

Table B–1 Deprecated Property Functions

Deprecated Functions 

Replacements 

ddi_getlongprop()

see ddi_prop_lookup()

ddi_getlongprop_buf()

ddi_prop_lookup()

ddi_getprop()

ddi_prop_get_int()

ddi_getproplen()

ddi_prop_lookup()

ddi_prop_create()

ddi_prop_lookup()

ddi_prop_modify()

ddi_prop_lookup()

ddi_prop_op()

ddi_prop_lookup()

Device Software State Functions

The device software state functions are:

ddi_get_driver_private()

Get the address of the device's private data area

ddi_get_soft_state()

Get pointer to instance soft-state structure

ddi_set_driver_private()

Set the address of the device's private data area

ddi_soft_state_fini()

Destroy driver soft-state structure

ddi_soft_state_free()

Free instance soft-state structure

ddi_soft_state_init()

Initialize driver soft-state structure

ddi_soft_state_zalloc()

Allocate instance soft-state structure

Memory Allocation and Deallocation Functions

The memory allocation and deallocation functions are:

kmem_alloc()

Allocate kernel memory

kmem_free()

Free kernel memory

kmem_zalloc()

Allocate zero-filled kernel memory

The following functions allocate and free memory intended to be used for DMA. See Direct Memory Access (DMA) Functions.

ddi_dma_mem_alloc()

Allocate memory for DMA transfer

ddi_dma_mem_free()

Free previously allocated DMA memory

The following functions allocate and free memory intended to be exported to user space. See User Space Access Functions.

ddi_umem_alloc()

Allocate page-aligned kernel memory

ddi_umem_free()

Free page-aligned kernel memory

Table B–2 Deprecated Memory Allocation and Deallocation Functions

Deprecated Function 

Replacement 

ddi_iopb_alloc()

ddi_dma_mem_alloc()

ddi_iopb_free()

ddi_dma_mem_free()

ddi_mem_alloc()

ddi_dma_mem_alloc()

ddi_mem_free()

ddi_dma_mem_free()

Kernel Thread Control and Synchronization Functions

The kernel thread control and synchronization functions are:

cv_broadcast()

Wake up all waiting threads

cv_destroy()

Free an allocated condition variable

cv_init()

Allocate a condition variable

cv_signal()

Wake up one waiting thread

cv_timedwait()

Await an event with timeout

cv_timedwait_sig()

Await an event or signal with timeout

cv_wait()

Await an event

cv_wait_sig()

Await an event or signal

ddi_can_receive_sig()

Determine whether the current thread can receive a signal

ddi_enter_critical()

Enter a critical region of control

ddi_exit_critical()

Exit a critical region of control

mutex_destroy()

Destroy mutual exclusion lock

mutex_enter()

Acquire mutual exclusion lock

mutex_exit()

Release mutual exclusion lock

mutex_init()

Initialize mutual exclusion lock

mutex_owned()

Determine whether current thread is holding mutual exclusion lock

mutex_tryenter()

Attempt to acquire mutual exclusion lock without waiting

rw_destroy()

Destroy a readers/writer lock

rw_downgrade()

Downgrade a readers/writer lock holding from writer to reader

rw_enter()

Acquire a readers/writer lock

rw_exit()

Release a readers/writer lock

rw_init()

Initialize a readers/writer lock

rw_read_locked()

Determine whether readers/writer lock is held for read or write

rw_tryenter()

Attempt to acquire a readers/writer lock without waiting

rw_tryupgrade()

Attempt to upgrade readers/writer lock holding from reader to writer

sema_destroy()

Destroy a semaphore

sema_init()

Initialize a semaphore

sema_p()

Decrement semaphore and possibly block

sema_p_sig()

Decrement semaphore but do not block if signal is pending

sema_tryp()

Attempt to decrement semaphore but do not block

sema_v()

Increment semaphore and possibly unblock waiter

Task Queue Management Functions

The task queue management functions are listed below. See the taskq(9F) man page for more information about these interfaces.

ddi_taskq_create()

Create a task queue

ddi_taskq_destroy()

Destroy a task queue

ddi_taskq_dispatch()

Add a task to a task queue

ddi_taskq_wait()

Wait for pending tasks to complete

ddi_taskq_suspend()

Suspend a task queue

ddi_taskq_suspended()

Check whether a task queue is suspended

ddi_taskq_resume()

Resume a suspended task queue

Interrupt Functions

The interrupt functions are:

ddi_intr_add_handler(9F)

Adds an interrupt handler.

ddi_intr_add_softint(9F)

Adds a soft interrupt handler.

ddi_intr_alloc(9F)

Allocates system resources and interrupt vectors for the specified type of interrupt.

ddi_intr_block_disable(9F)

Disables the specified range of interrupts. For MSI only.

ddi_intr_block_enable(9F)

Enables the specified range of interrupts. For MSI only.

ddi_intr_clr_mask(9F)

Clears an interrupt mask if the specified interrupt is enabled.

ddi_intr_disable(9F)

Disables the specified interrupt.

ddi_intr_dup_handler(9F)

Use with MSI-X only. Copies an address and data pair for an allocated interrupt vector to an unused interrupt vector on the same device.

ddi_intr_enable(9F)

Enables the specified interrupt.

ddi_intr_free(9F)

Releases the system resources and interrupt vectors for a specified interrupt handle.

ddi_intr_get_cap(9F)

Returns interrupt capability flags for the specified interrupt.

ddi_intr_get_hilevel_pri(9F)

Returns the minimum priority level for a high-level interrupt.

ddi_intr_get_navail(9F)

Returns the number of interrupts available for a particular hardware device and given interrupt type.

ddi_intr_get_nintrs(9F)

Get the number of interrupts that the device supports for the given interrupt type.

ddi_intr_get_pending(9F)

Read the interrupt pending bit if one is supported by either the host bridge or the device.

ddi_intr_get_pri(9F)

Returns the current software priority setting for the specified interrupt.

ddi_intr_get_softint_pri(9F)

Returns the soft interrupt priority for the specified interrupt.

ddi_intr_get_supported_types(9F)

Returns the hardware interrupt types that are supported by both the device and the host.

ddi_intr_remove_handler(9F)

Removes the specified interrupt handler.

ddi_intr_remove_softint(9F)

Remove the specified soft interrupt handler.

ddi_intr_set_cap(9F)

Sets the DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE flag for the specified interrupt.

ddi_intr_set_mask(9F)

Sets an interrupt mask if the specified interrupt is enabled.

ddi_intr_set_pri(9F)

Sets the interrupt priority level for the specified interrupt.

ddi_intr_set_softint_pri(9F)

Changes the relative soft interrupt priority for the specified soft interrupt.

ddi_intr_trigger_softint(9F)

Trigger the specified soft interrupt.

To take advantage of the features of the new framework, use the above interfaces. Do not use the deprecated interfaces that are listed in the following table. These deprecated interfaces are retained for compatibility purposes only.

Table B–3 Deprecated Interrupt Functions

Deprecated Interrupt Functions 

Replacements 

ddi_add_intr(9F)

Three-step process: 

  1. ddi_intr_alloc(9F)

  2. ddi_intr_add_handler(9F)

  3. ddi_intr_enable(9F)

ddi_add_softintr(9F)

ddi_intr_add_softint(9F)

ddi_dev_nintrs(9F)

ddi_intr_get_nintrs(9F)

ddi_get_iblock_cookie(9F)

Three-step process: 

  1. ddi_intr_alloc(9F)

  2. ddi_intr_get_pri(9F)

  3. ddi_intr_free(9F)

ddi_get_soft_iblock_cookie(9F)

Three-step process: 

  1. ddi_intr_add_softint(9F)

  2. ddi_intr_get_softint_pri(9F)

  3. ddi_intr_remove_softint(9F)

ddi_intr_hilevel(9F)

Three-step process: 

  1. ddi_intr_alloc(9F)

  2. ddi_intr_get_hilevel_pri(9F)

  3. ddi_intr_free(9F)

ddi_remove_intr(9F)

Three-step process: 

  1. ddi_intr_disable(9F)

  2. ddi_intr_remove_handler(9F)

  3. ddi_intr_free(9F)

ddi_remove_softintr(9F)

ddi_intr_remove_softint(9F)

ddi_trigger_softintr(9F)

ddi_intr_trigger_softint(9F)

Programmed I/O Functions

The programmed I/O functions are:

ddi_dev_nregs()

Return the number of register sets a device has

ddi_dev_regsize()

Return the size of a device's register

ddi_regs_map_setup()

Set up a mapping for a register address space

ddi_regs_map_free()

Free a previously mapped register address space

ddi_device_copy()

Copy data from one device register to another device register

ddi_device_zero()

Zero fill the device

ddi_check_acc_handle()

Check data access handle

ddi_get8()

Read 8-bit data from mapped memory, device register, or DMA memory

ddi_get16()

Read 16-bit data from mapped memory, device register, or DMA memory

ddi_get32()

Read 32-bit data from mapped memory, device register, or DMA memory

ddi_get64()

Read 64-bit data from mapped memory, device register, or DMA memory

ddi_put8()

Write 8-bit data to mapped memory, device register, or DMA memory

ddi_put16()

Write 16-bit data to mapped memory, device register, or DMA memory

ddi_put32()

Write 32-bit data to mapped memory, device register, or DMA memory

ddi_put64()

Write 64-bit data to mapped memory, device register, or DMA memory

ddi_rep_get8()

Read multiple 8-bit data from mapped memory, device register, or DMA memory

ddi_rep_get16()

Read multiple 16-bit data from mapped memory, device register, or DMA memory

ddi_rep_get32()

Read multiple 32-bit data from mapped memory, device register, or DMA memory

ddi_rep_get64()

Read multiple 64-bit data from mapped memory, device register, or DMA memory

ddi_rep_put8()

Write multiple 8-bit data to mapped memory, device register, or DMA memory

ddi_rep_put16()

Write multiple 16-bit data to mapped memory, device register, or DMA memory

ddi_rep_put32()

Write multiple 32-bit data to mapped memory, device register, or DMA memory

ddi_rep_put64()

Write multiple 64-bit data to mapped memory, device register, or DMA memory

ddi_peek8()

Cautiously read an 8-bit value from a location

ddi_peek16()

Cautiously read a 16-bit value from a location

ddi_peek32()

Cautiously read a 32-bit value from a location

ddi_peek64()

Cautiously read a 64-bit value from a location

ddi_poke8()

Cautiously write an 8-bit value to a location

ddi_poke16()

Cautiously write a 16-bit value to a location

ddi_poke32()

Cautiously write a 32-bit value to a location

ddi_poke64()

Cautiously write a 64-bit value to a location

The general programmed I/O functions listed above can always be used rather than the mem, io, and pci_config functions that follow. However, the following functions can be used as alternatives in cases where the type of access is known at compile time.

ddi_io_get8()

Read 8-bit data from a mapped device register in I/O space

ddi_io_get16()

Read 16-bit data from a mapped device register in I/O space

ddi_io_get32()

Read 32-bit data from a mapped device register in I/O space

ddi_io_put8()

Write 8-bit data to a mapped device register in I/O space

ddi_io_put16()

Write 16-bit data to a mapped device register in I/O space

ddi_io_put32()

Write 32-bit data to a mapped device register in I/O space

ddi_io_rep_get8()

Read multiple 8-bit data from a mapped device register in I/O space

ddi_io_rep_get16()

Read multiple 16-bit data from a mapped device register in I/O space

ddi_io_rep_get32()

Read multiple 32-bit data from a mapped device register in I/O space

ddi_io_rep_put8()

Write multiple 8-bit data to a mapped device register in I/O space

ddi_io_rep_put16()

Write multiple 16-bit data to a mapped device register in I/O space

ddi_io_rep_put32()

Write multiple 32-bit data to a mapped device register in I/O space

ddi_mem_get8()

Read 8-bit data from a mapped device in memory space or DMA memory

ddi_mem_get16()

Read 16-bit data from a mapped device in memory space or DMA memory

ddi_mem_get32()

Read 32-bit data from a mapped device in memory space or DMA memory

ddi_mem_get64()

Read 64-bit data from a mapped device in memory space or DMA memory

ddi_mem_put8()

Write 8-bit data to a mapped device in memory space or DMA memory

ddi_mem_put16()

Write 16-bit data to a mapped device in memory space or DMA memory

ddi_mem_put32()

Write 32-bit data to a mapped device in memory space or DMA memory

ddi_mem_put64()

Write 64-bit data to a mapped device in memory space or DMA memory

ddi_mem_rep_get8()

Read multiple 8-bit data from a mapped device in memory space or DMA memory

ddi_mem_rep_get16()

Read multiple 16-bit data from a mapped device in memory space or DMA memory

ddi_mem_rep_get32()

Read multiple 32-bit data from a mapped device in memory space or DMA memory

ddi_mem_rep_get64()

Read multiple 64-bit data from a mapped device in memory space or DMA memory

ddi_mem_rep_put8()

Write multiple 8-bit data to a mapped device in memory space or DMA memory

ddi_mem_rep_put16()

Write multiple 16-bit data to a mapped device in memory space or DMA memory

ddi_mem_rep_put32()

Write multiple 32-bit data to a mapped device in memory space or DMA memory

ddi_mem_rep_put64()

Write multiple 64-bit data to a mapped device in memory space or DMA memory

pci_config_setup()

Set up access to PCI Local Bus Configuration space

pci_config_teardown()

Tear down access to PCI Local Bus Configuration space

pci_config_get8()

Read 8-bit data from the PCI Local Bus Configuration space

pci_config_get16()

Read 16-bit data from the PCI Local Bus Configuration space

pci_config_get32()

Read 32-bit data from the PCI Local Bus Configuration space

pci_config_get64()

Read 64-bit data from the PCI Local Bus Configuration space

pci_config_put8()

Write 8-bit data to the PCI Local Bus Configuration space

pci_config_put16()

Write 16-bit data to the PCI Local Bus Configuration space

pci_config_put32()

Write 32-bit data to the PCI Local Bus Configuration space

pci_config_put64()

Write 64-bit data to the PCI Local Bus Configuration space

Table B–4 Deprecated Programmed I/O Functions

Deprecated Function 

Replacement 

ddi_getb()

ddi_get8()

ddi_getl()

ddi_get32()

ddi_getll()

ddi_get64()

ddi_getw()

ddi_get16()

ddi_io_getb()

ddi_io_get8()

ddi_io_getl()

ddi_io_get32()

ddi_io_getw()

ddi_io_get16()

ddi_io_putb()

ddi_io_put8()

ddi_io_putl()

ddi_io_put32()

ddi_io_putw()

ddi_io_put16()

ddi_io_rep_getb()

ddi_io_rep_get8()

ddi_io_rep_getl()

ddi_io_rep_get32()

ddi_io_rep_getw()

ddi_io_rep_get16()

ddi_io_rep_putb()

ddi_io_rep_put8()

ddi_io_rep_putl()

ddi_io_rep_put32()

ddi_io_rep_putw()

ddi_io_rep_put16()

ddi_map_regs()

ddi_regs_map_setup()

ddi_mem_getb()

ddi_mem_get8()

ddi_mem_getl()

ddi_mem_get32()

ddi_mem_getll()

ddi_mem_get64()

ddi_mem_getw()

ddi_mem_get16()

ddi_mem_putb()

ddi_mem_put8()

ddi_mem_putl()

ddi_mem_put32()

ddi_mem_putll()

ddi_mem_put64()

ddi_mem_putw()

ddi_mem_put16()

ddi_mem_rep_getb()

ddi_mem_rep_get8()

ddi_mem_rep_getl()

ddi_mem_rep_get32()

ddi_mem_rep_getll()

ddi_mem_rep_get64()

ddi_mem_rep_getw()

ddi_mem_rep_get16()

ddi_mem_rep_putb()

ddi_mem_rep_put8()

ddi_mem_rep_putl()

ddi_mem_rep_put32()

ddi_mem_rep_putll()

ddi_mem_rep_put64()

ddi_mem_rep_putw()

ddi_mem_rep_put16()

ddi_peekc()

ddi_peek8()

ddi_peekd()

ddi_peek64()

ddi_peekl()

ddi_peek32()

ddi_peeks()

ddi_peek16()

ddi_pokec()

ddi_poke8()

ddi_poked()

ddi_poke64()

ddi_pokel()

ddi_poke32()

ddi_pokes()

ddi_poke16()

ddi_putb()

ddi_put8()

ddi_putl()

ddi_put32()

ddi_putll()

ddi_put64()

ddi_putw()

ddi_put16()

ddi_rep_getb()

ddi_rep_get8()

ddi_rep_getl()

ddi_rep_get32()

ddi_rep_getll()

ddi_rep_get64()

ddi_rep_getw()

ddi_rep_get16()

ddi_rep_putb()

ddi_rep_put8()

ddi_rep_putl()

ddi_rep_put32()

ddi_rep_putll()

ddi_rep_put64()

ddi_rep_putw()

ddi_rep_put16()

ddi_unmap_regs()

ddi_regs_map_free()

inb()

ddi_io_get8()

inl()

ddi_io_get32()

inw()

ddi_io_get16()

outb()

ddi_io_put8()

outl()

ddi_io_put32()

outw()

ddi_io_put16()

pci_config_getb()

pci_config_get8()

pci_config_getl()

pci_config_get32()

pci_config_getll()

pci_config_get64()

pci_config_getw()

pci_config_get16()

pci_config_putb()

pci_config_put8()

pci_config_putl()

pci_config_put32()

pci_config_putll()

pci_config_put64()

pci_config_putw()

pci_config_put16()

repinsb()

ddi_io_rep_get8()

repinsd()

ddi_io_rep_get32()

repinsw()

ddi_io_rep_get16()

repoutsb()

ddi_io_rep_put8()

repoutsd()

ddi_io_rep_put32()

repoutsw()

ddi_io_rep_put16()

Direct Memory Access (DMA) Functions

The DMA functions are:

ddi_dma_alloc_handle()

Allocate a DMA handle

ddi_dma_free_handle()

Free a DMA handle

ddi_dma_mem_alloc()

Allocate memory for a DMA transfer

ddi_dma_mem_free()

Free previously allocated DMA memory

ddi_dma_addr_bind_handle()

Bind an address to a DMA handle

ddi_dma_buf_bind_handle()

Bind a system buffer to a DMA handle

ddi_dma_unbind_handle()

Unbind the address in a DMA handle

ddi_dma_nextcookie()

Retrieve the subsequent DMA cookie

ddi_dma_getwin()

Activate a new DMA window

ddi_dma_numwin()

Retrieve number of DMA windows

ddi_dma_sync()

Synchronize CPU and I/O views of memory

ddi_check_dma_handle()

Check a DMA handle

ddi_dma_set_sbus64()

Allow 64-bit transfers on SBus

ddi_slaveonly()

Report whether a device is installed in a slave access-only location

ddi_iomin()

Find the minimum alignment and transfer size for DMA

ddi_dma_burstsizes()

Find out the allowed burst sizes for a DMA mapping

ddi_dma_devalign()

Find DMA mapping alignment and minimum transfer size

ddi_dmae_alloc()

Acquire a DMA channel

ddi_dmae_release()

Release a DMA channel

ddi_dmae_getattr()

Get the DMA engine attributes

ddi_dmae_prog()

Program a DMA channel

ddi_dmae_stop()

Terminate a DMA engine operation

ddi_dmae_disable()

Disable a DMA channel

ddi_dmae_enable()

Enable a DMA channel

ddi_dmae_getcnt()

Get the remaining DMA engine count

ddi_dmae_1stparty()

Configure the DMA channel cascade mode

ddi_dma_coff()

Convert a DMA cookie to an offset within a DMA handle

Table B–5 Deprecated Direct Memory Access (DMA) Functions

Deprecated Function 

Replacement 

ddi_dma_addr_setup()

ddi_dma_alloc_handle(), ddi_dma_addr_bind_handle()

ddi_dma_buf_setup()

ddi_dma_alloc_handle(), ddi_dma_buf_bind_handle()

ddi_dma_curwin()

ddi_dma_getwin()

ddi_dma_free()

ddi_dma_free_handle()

ddi_dma_htoc()

ddi_dma_addr_bind_handle(), ddi_dma_buf_bind_handle()

ddi_dma_movwin()

ddi_dma_getwin()

ddi_dma_nextseg()

ddi_dma_nextcookie()

ddi_dma_segtocookie()

ddi_dma_nextcookie()

ddi_dma_setup()

ddi_dma_alloc_handle(), ddi_dma_addr_bind_handle(), ddi_dma_buf_bind_handle()

ddi_dmae_getlim()

ddi_dmae_getattr()

ddi_iopb_alloc()

ddi_dma_mem_alloc()

ddi_iopb_free()

ddi_dma_mem_free()

ddi_mem_alloc()

ddi_dma_mem_alloc()

ddi_mem_free()

ddi_dma_mem_free()

hat_getkpfnum()

ddi_dma_addr_bind_handle(), ddi_dma_buf_bind_handle(), ddi_dma_nextcookie()

User Space Access Functions

The user space access functions are:

ddi_copyin()

Copy data to a driver buffer

ddi_copyout()

Copy data from a driver

uiomove()

Copy kernel data using a uio structure

ureadc()

Add character to a uio structure

uwritec()

Remove a character from a uio structure

getminor()

Get minor device number.

ddi_model_convert_from()

Determine a data model type mismatch

IOC_CONVERT_FROM()

Determine whether there is a need to translate M_IOCTL contents

STRUCT_DECL()

Establish the handle to application data in a possibly differing data model

STRUCT_HANDLE()

Establish the handle to application data in a possibly differing data model

STRUCT_INIT()

Establish the handle to application data in a possibly differing data model

STRUCT_SET_HANDLE()

Establish the handle to application data in a possibly differing data model

SIZEOF_PTR()

Return the size of pointer in specified data model

SIZEOF_STRUCT()

Return the size of a structure in the specified data model

STRUCT_SIZE()

Return the size of a structure in the application data model

STRUCT_BUF()

Return a pointer to the native mode instance of the structure

STRUCT_FADDR()

Return a pointer to the specified field of a structure

STRUCT_FGET()

Return the specified field of a structure in the application data model

STRUCT_FGETP()

Return the specified pointer field of a structure in the application data model

STRUCT_FSET()

Set a specified field of a structure in the application data model

STRUCT_FSETP()

Set a specified pointer field of a structure in the application data model

Table B–6 Deprecated User Space Access Functions

Deprecated Function 

Replacement 

copyin()

ddi_copyin()

copyout()

ddi_copyout()

ddi_getminor()

getminor()

User Process Event Functions

The user process event functions are:

pollwakeup()

Inform a process that an event has occurred

proc_ref()

Get a handle on a process to signal

proc_unref()

Release a handle on a process to signal

proc_signal()

Send a signal to a process

User Process Information Functions

The user process information functions are:

ddi_get_cred()

Return a pointer to the credential structure of the caller

drv_priv()

Determine process credentials privilege

ddi_get_pid()

Return the process ID

Table B–7 Deprecated User Process Information Functions

Deprecated Functions 

Replacement 

drv_getparm()

ddi_get_pid(), ddi_get_cred()

User Application Kernel and Device Access Functions

The user application kernel and device access functions are:

ddi_dev_nregs()

Return the number of register sets a device has

ddi_dev_regsize()

Return the size of a device's register

ddi_devmap_segmap(), devmap_setup()

Set up a user mapping to device memory using the devmap framework

devmap_devmem_setup()

Export device memory to user space

devmap_load()

Validate memory address translations

devmap_unload()

Invalidate memory address translations

devmap_do_ctxmgt()

Perform device context switching on a mapping

devmap_set_ctx_timeout()

Set the timeout value for the context management callback

devmap_default_access()

Default driver memory access function

ddi_umem_alloc()

Allocate page-aligned kernel memory

ddi_umem_free()

Free page-aligned kernel memory

ddi_umem_lock()

Lock memory pages

ddi_umem_unlock()

Unlock memory pages

ddi_umem_iosetup()

Setup I/O requests to application memory

devmap_umem_setup()

Export kernel memory to user space

ddi_model_convert_from()

Determine data model type mismatch

Table B–8 Deprecated User Application Kernel and Device Access Functions

Deprecated Function 

Replacement 

ddi_mapdev()

devmap_setup()

ddi_mapdev_intercept()

devmap_load()

ddi_mapdev_nointercept()

devmap_unload()

ddi_mapdev_set_device_acc_attr()

devmap()

ddi_segmap()

devmap()

ddi_segmap_setup()

devmap_setup()

hat_getkpfnum()

devmap()

ddi_mmap_get_model()

devmap()

Time-Related Functions

The time-related functions are:

ddi_get_lbolt()

Return the number of clock ticks since reboot

ddi_get_time()

Return the current time in seconds

ddi_periodic_add()

Issue nanosecond periodic timeout requests

ddi_periodic_delete()

Cancel nanosecond periodic timeout requests

delay()

Delay execution for a specified number of clock ticks

drv_hztousec()

Convert clock ticks to microseconds

drv_usectohz()

Convert microseconds to clock ticks

drv_usecwait()

Busy-wait for specified interval

gethrtime()

Get high-resolution time

gethrvtime()

Get high-resolution LWP virtual time

timeout()

Execute a function after a specified length of time

untimeout()

Cancel the previous time out function call

drv_getparm()

ddi_get_lbolt(), ddi_get_time()

Table B–9 Deprecated Time-Related Functions

Deprecated Function 

Replacement 

drv_getparm()

ddi_get_lbolt(), ddi_get_time()

Power Management Functions

The power management functions are:

ddi_removing_power()

Check if device loses power with DDI_SUSPEND

pci_report_pmcap()

Report the power management capability of a PCI device

pm_busy_component()

Mark a component as busy

pm_idle_component()

Mark a component as idle

pm_raise_power()

Raise the power level of a component

pm_lower_power()

Lower the power level of a component

pm_power_has_changed()

Notify the power management framework of an autonomous power level change

pm_trans_check()

Device power cycle advisory check

Table B–10 Deprecated Power Management Functions

Function Name 

Description 

ddi_dev_is_needed()

Inform the system that a device's component is required 

pm_create_components()

Create power-manageable components 

pm_destroy_components()

Destroy power-manageable components 

pm_get_normal_power()

Get the normal power level of a device component 

pm_set_normal_power()

Set the normal power level of a device component 

Fault Management Functions

The fault management functions are:

ddi_fm_init()

Allocates and initializes resources based on declared fault management capabilities

ddi_fm_fini()

Cleans up resources that were allocated for this device instance to support fault management capabilities declared to ddi_fm_init()

ddi_fm_capable()

Returns the capability bit mask currently set for this device instance

ddi_fm_handler_register()

Registers an error handler callback routine with the IO Fault Management framework

ddi_fm_handler_unregister()

Removes an error handler callback routine that was registered with ddi_fm_handler_register()

ddi_fm_acc_err_get()

Returns the error status for an access handle

ddi_fm_dma_err_get()

Returns the error status for a DMA handle

ddi_fm_acc_err_clear()

Clears the error status for an access handle

ddi_fm_dma_err_clear()

Clears the error status for a DMA handle

ddi_fm_ereport_post()

Queues an encoded fault management error report name-value pair list for delivery to the Fault Manager daemon, fmd(1M)

ddi_fm_service_impact()

Reports the impact of an error

pci_ereport_setup()

Initializes support for error report generation and sets up the resources for subsequent accesses to PCI, PCI/X, or PCI Express configuration space

pci_ereport_teardown()

Releases any resources allocated and setup by pci_ereport_setup() for this device instance

pci_ereport_post()

Scans for and posts any PCI, PCI/X, or PCI Express bus errors

Kernel Statistics Functions

The kernel statistics (kstats) functions are:

kstat_create()

Create and initialize a new kstat

kstat_delete()

Remove a kstat from the system

kstat_install()

Add a fully initialized kstat to the system

kstat_named_init()

Initialize a named kstat

kstat_runq_back_to_waitq()

Record a transaction migration from run queue to the wait queue

kstat_runq_enter()

Record a transaction addition to the run queue

kstat_runq_exit()

Record a transaction removal from the run queue

kstat_waitq_enter()

Record a transaction addition to the wait queue

kstat_waitq_exit()

Record a transaction removal from the wait queue

kstat_waitq_to_runq()

Record a transaction migration from the wait queue to the run queue

Kernel Logging and Printing Functions

The kernel logging and printing functions are:

cmn_err(), vcmn_err()

Display an error message

ddi_report_dev()

Announce a device

strlog()

Submit messages to the log driver

ddi_dev_report_fault()

Report a hardware failure

scsi_errmsg()

Display a SCSI request sense message

scsi_log()

Display a SCSI-device-related message

scsi_vu_errmsg()

Display a SCSI request sense message

Buffered I/O Functions

The buffered I/O functions are:

physio()

Perform physical I/O

aphysio()

Perform asynchronous physical I/O

anocancel()

Prevent cancellation of an asynchronous I/O request

minphys()

Limit the physio() buffer size

biowait()

Suspend processes pending completion of block I/O

biodone()

Release the buffer after buffer I/O transfer and notify blocked threads

bioerror()

Indicate the error in a buffer header

geterror()

Return an I/O error

bp_mapin()

Allocate virtual address space

bp_mapout()

Deallocate virtual address space

disksort()

Use a single-direction elevator seek strategy to sort for buffers

getrbuf()

Get a raw buffer header

freerbuf()

Free a raw buffer header

biosize()

Return the size of a buffer structure

bioinit()

Initialize a buffer structure

biofini()

Uninitialize a buffer structure

bioreset()

Reuse a private buffer header after I/O is complete

bioclone()

Clone another buffer

biomodified()

Check whether a buffer is modified

clrbuf()

Erase the contents of a buffer

Virtual Memory Functions

The virtual memory functions are:

ddi_btop()

Convert device bytes to pages (round down)

ddi_btopr()

Convert device bytes to pages (round up)

ddi_ptob()

Convert device pages to bytes

btop()

Convert size in bytes to size in pages (round down)

btopr()

Convert size in bytes to size in pages (round up)

ptob()

Convert size in pages to size in bytes

Table B–11 Deprecated Virtual Memory Functions

Deprecated Functions 

Replacement 

hat_getkpfnum()

devmap(), ddi_dma_*_bind_handle(), ddi_dma_nextcookie()

Device ID Functions

The device ID functions are:

ddi_devid_init()

Allocate a device ID structure

ddi_devid_free()

Free a device ID structure

ddi_devid_register()

Register a device ID

ddi_devid_unregister()

Unregister a device ID

ddi_devid_compare()

Compare two device IDs

ddi_devid_sizeof()

Return the size of a device ID

ddi_devid_valid()

Validate a device ID

ddi_devid_str_encode()

Encode a device ID and minor_name into a null-terminated ASCII string; return a pointer to that string

ddi_devid_str_decode()

Decode the device ID and minor_name from a previously encoded string; allocate and return pointers to the extracted parts

ddi_devid_str_free()

Free all strings returned by the ddi_devid_* functions

SCSI Functions

The SCSI functions are:

scsi_probe()

Probe a SCSI device

scsi_unprobe()

Free resources allocated during initial probing

scsi_alloc_consistent_buf()

Allocate an I/O buffer for SCSI DMA

scsi_free_consistent_buf()

Free a previously allocated SCSI DMA I/O buffer

scsi_init_pkt()

Prepare a complete SCSI packet

scsi_destroy_pkt()

Free an allocated SCSI packet and its DMA resource

scsi_setup_cdb()

Set up SCSI command descriptor block (CDB)

scsi_transport()

Start a SCSI command

scsi_poll()

Run a polled SCSI command

scsi_ifgetcap()

Get SCSI transport capability

scsi_ifsetcap()

Set SCSI transport capability

scsi_sync_pkt()

Synchronize CPU and I/O views of memory

scsi_abort()

Abort a SCSI command

scsi_reset()

Reset a SCSI bus or target

scsi_reset_notify()

Notify the target driver of bus resets

scsi_cname()

Decode a SCSI command

scsi_dname()

Decode a SCSI peripheral device type

scsi_mname()

Decode a SCSI message

scsi_rname()

Decode a SCSI packet completion reason

scsi_sname()

Decode a SCSI sense key

scsi_errmsg()

Display a SCSI request sense message

scsi_log()

Display a SCSI-device-related message

scsi_vu_errmsg()

Display a SCSI request sense message

scsi_hba_init()

SCSI HBA system initialization routine

scsi_hba_fini()

SCSI HBA system completion routine

scsi_hba_attach_setup()

SCSI HBA attach routine

scsi_hba_detach()

SCSI HBA detach routine

scsi_hba_probe()

Default SCSI HBA probe function

scsi_hba_tran_alloc()

Allocate a transport structure

scsi_hba_tran_free()

Free a transport structure

scsi_hba_pkt_alloc()

Allocate a scsi_pkt structure

scsi_hba_pkt_free()

Free a scsi_pkt structure

scsi_hba_lookup_capstr()

Return an index matching capability string

Table B–12 Deprecated SCSI Functions

Deprecated Function 

Replacement 

free_pktiopb()

scsi_free_consistent_buf()

get_pktiopb()

scsi_alloc_consistent_buf()

makecom_g0()

scsi_setup_cdb()

makecom_g0_s()

scsi_setup_cdb()

makecom_g1()

scsi_setup_cdb()

makecom_g5()

scsi_setup_cdb()

scsi_dmafree()

scsi_destroy_pkt()

scsi_dmaget()

scsi_init_pkt()

scsi_hba_attach()

scsi_hba_attach_setup()

scsi_pktalloc()

scsi_init_pkt()

scsi_pktfree()

scsi_destroy_pkt()

scsi_resalloc()

scsi_init_pkt()

scsi_resfree()

scsi_destroy_pkt()

scsi_slave()

scsi_probe()

scsi_unslave()

scsi_unprobe()

Resource Map Management Functions

The resource map management functions are:

rmallocmap()

Allocate a resource map

rmallocmap_wait()

Allocate a resource map, wait if necessary

rmfreemap()

Free a resource map

rmalloc()

Allocate space from a resource map

rmalloc_wait()

Allocate space from a resource map, wait if necessary

rmfree()

Free space back into a resource map

System Global State

ddi_in_panic()

Determine whether the system is in panic state

Utility Functions

The utility functions are:

nulldev()

Zero return function

nodev()

Error return function

nochpoll()

Error return function for non-pollable devices

ASSERT()

Expression verification

bcopy()

Copy data between address locations in the kernel

bzero()

Clear memory for a given number of bytes

bcmp()

Compare two byte arrays

ddi_ffs()

Find the first bit set in a long integer

ddi_fls()

Find the last bit set in a long integer

swab()

Swap bytes in 16-bit halfwords

strcmp()

Compare two null-terminated strings

strncmp()

Compare two null-terminated strings, with length limit

strlen()

Determine the number of non-null bytes in a string

strnlen()

(Available starting with SXCE build 88) Determine the number of non-null bytes in a string, with length limit

strcpy()

Copy a string from one location to another

strncpy()

Copy a string from one location to another, with length limit

strchr()

Find a character in a string

sprintf(), vsprintf()

Format characters in memory

numtos()

Convert an integer to a decimal string

stoi()

Convert a decimal string to an integer

max()

Return the larger of two integers

min()

Return the lesser of two integers

va_arg()

Finds the next value in a variable argument list

va_copy()

Copies the state of a variable argument list

va_end()

Deletes pointer to a variable argument list

va_start()

Finds the pointer to the start of a variable argument list

Appendix C Making a Device Driver 64-Bit Ready

This appendix provides information for device driver writers who are converting their device drivers to support the 64-bit kernel. It presents the differences between 32-bit and 64-bit device drivers and describes the steps to convert 32-bit device drivers to 64-bit. This information is specific to regular character and block device drivers only.

This appendix provides information on the following subjects:

Introduction to 64-Bit Driver Design

For drivers that only need support for the 32-bit kernel, existing 32-bit device drivers will continue to work without recompilation. However, most device drivers require some changes to run correctly in the 64-bit kernel, and all device drivers require recompilation to create a 64-bit driver module. The information in this appendix will help you to enable drivers for 32-bit and 64-bit environments to be generated from common source code, thus increasing code portability and reducing the maintenance effort.

Before starting to modify a device driver for the 64-bit environment, you should understand how the 32-bit environment differs from the 64-bit environment. In particular, you must be familiar with the C language data type models ILP32 and LP64. See the following table.

Table C–1 Comparison of ILP32 and LP64 Data Types

C Type 

ILP32 

LP64 

char

short

16 

16 

int

32 

32 

long

32 

64 

long long

64 

64 

float

32 

32 

double

64 

64 

long double

96 

128 

pointer

32 

64 

The driver-specific issues due to the differences between ILP32 and LP64 are the subject of this appendix.

In addition to general code cleanup to support the data model changes for LP64, driver writers have to provide support for both 32-bit and 64-bit applications.

The ioctl(9E), devmap(9E), and mmap(9E) entry points enable data structures to be shared directly between applications and device drivers. If those data structures change size between the 32-bit and 64-bit environments, then the entry points must be modified so that the driver can determine whether the data model of the application is the same as that of the kernel. When the data models differ, data structures can be adjusted. See I/O Control Support for 64-Bit Capable Device Drivers, 32-bit and 64-bit Data Structure Macros, and Associating Kernel Memory With User Mappings.

In many drivers, only a few ioctls need this kind of handling. The other ioctls should work without change as long as these ioctls pass data structures that do not change in size.

General Conversion Steps

The sections below provide information on converting drivers to run in a 64-bit environment. Driver writers might need to perform one or more of the following tasks:

  1. Use fixed-width types for hardware registers.

  2. Use fixed-width common access functions.

  3. Check and extend use of derived types.

  4. Check changed fields within DDI data structures.

  5. Check changed arguments of DDI functions.

  6. Modify the driver entry points that handle user data, where needed.

  7. Check structures that use 64-bit long types on x86 platforms.

These steps are explained in detail below.

After each step is complete, fix all compiler warnings, and use lint to look for other problems. The SC5.0 (or newer) version of lint should be used with -Xarch=v9 and -errchk=longptr64 specified to find 64-bit problems.


Note –

Do not ignore compilation warnings during conversion for LP64. Warnings that were safe to ignore previously in the ILP32 environment might now indicate a more serious problem.


After all the steps are complete, compile and test the driver as both a 32-bit and 64-bit module.

Use Fixed-Width Types for Hardware Registers

Many device drivers that manipulate hardware devices use C data structures to describe the layout of the hardware. In the LP64 data model, data structures that use long or unsigned long to define hardware registers are almost certainly incorrect, because long is now a 64-bit quantity. Start by including <sys/inttypes.h>, and update this class of data structure to use int32_t or uint32_t instead of long for 32-bit device data. This approach preserves the binary layout of 32-bit data structures. For example, change:

struct device_regs {
    ulong_t        addr;
    uint_t         count;
};      /* Only works for ILP32 compilation */

to:

struct device_regs {
    uint32_t        addr;
    uint32_t        count;
};      /* Works for any data model */

Use Fixed-Width Common Access Functions

The Oracle Solaris DDI allows device registers to be accessed by access functions for portability to multiple platforms. Previously, the DDI common access functions specified the size of data in terms of bytes, words, and so on. For example, ddi_getl(9F) is used to access 32-bit quantities. This function is not available in the 64-bit DDI environment, and has been replaced by versions of the function that specify the number of bits to be acted on.

These routines were added to the 32-bit kernel in the Solaris 2.6 operating environment, to enable their early adoption by driver writers. For example, to be portable to both 32-bit and 64-bit kernels, the driver must use ddi_get32(9F) to access 32-bit data rather than ddi_getl(9F).

All common access routines are replaced by their fixed-width equivalents. See the ddi_get8(9F), ddi_put8(9F), ddi_rep_get8(9F), and ddi_rep_put8(9F) man pages for details.

Check and Extend Use of Derived Types

System-derived types, such as size_t, should be used where possible so that the resulting variables make sense when passed between functions. The new derived types uintptr_t or intptr_t should be used as the integral type for pointers.

Fixed-width integer types are useful for representing explicit sizes of binary data structures or hardware registers, while fundamental C language data types, such as int, can still be used for loop counters or file descriptors.

Some system-derived types represent 32-bit quantities on a 32-bit system but represent 64-bit quantities on a 64-bit system. Derived types that change size in this way include: clock_t, daddr_t, dev_t, ino_t, intptr_t, off_t, size_t, ssize_t, time_t, uintptr_t, and timeout_id_t.

When designing drivers that use these derived types, pay particular attention to the use of these types, particularly if the drivers are assigning these values to variables of another derived type, such as a fixed-width type.

Check Changed Fields in DDI Data Structures

The data types of some of the fields within DDI data structures, such as buf(9S), have been changed. Drivers that use these data structures should make sure that these fields are being used appropriately. The data structures and the fields that were changed in a significant way are listed below.

buf Structure Changes

The fields listed below pertain to transfer size, which can now exceed more than 4 Gbytes.

size_t        b_bcount;          /* was type unsigned int */
size_t        b_resid;           /* was type unsigned int */
size_t        b_bufsize;         /* was type long */

ddi_dma_attr

The ddi_dma_attr(9S) structure defines attributes of the DMA engine and the device. Because these attributes specify register sizes, fixed-width data types have been used instead of fundamental types.

ddi_dma_cookie Structure Changes

uint32_t     dmac_address;    /* was type unsigned long */
size_t       dmac_size;       /* was type u_int */

The ddi_dma_cookie(9S) structure contains a 32-bit DMA address, so a fixed-width data type has been used to define the address. The size has been redefined as size_t.

csi_arq_status Structure Changes

uint_t    sts_rqpkt_state;         /* was type u_long */
uint_t    sts_rqpkt_statistics;    /* was type u_long */

These fields in the structure do not need to grow and have been redefined as 32-bit quantities.

scsi_pkt Structure Changes

uint_t      pkt_flags;           /* was type u_long */
int     pkt_time;        /* was type long */
ssize_t     pkt_resid;           /* was type long */
uint_t      pkt_state;           /* was type u_long */
uint_t      pkt_statistics;      /* was type u_long */

Because the pkt_flags, pkt_state, and pkt_statistics fields in the scsi_pkt(9S) structure do not need to grow, these fields have been redefined as 32-bit integers. The data transfer size pkt_resid field does grow and has been redefined as ssize_t.

Check Changed Arguments of DDI Functions

This section describes the DDI function argument data types that have been changed.

getrbuf() Argument Changes

struct buf *getrbuf(int sleepflag);

In previous releases, sleepflag was defined as a type long.

drv_getparm() Argument Changes

int drv_getparm(unsigned int parm, void *value_p);

In previous releases, value_p was defined as type unsigned long. In the 64-bit kernel, drv_getparm(9F) can fetch both 32-bit and 64-bit quantities. The interface does not define data types of these quantities, and simple programming errors can occur.

The following new routines offer a safer alternative:

clock_t       ddi_get_lbolt(void);
time_t        ddi_get_time(void);
cred_t        *ddi_get_cred(void);
pid_t         ddi_get_pid(void);

Driver writers are strongly urged to use these routines instead of drv_getparm(9F).

delay() and timeout() Argument Changes

void delay(clock_t ticks);
timeout_id_t timeout(void (*func)(void *), void *arg, clock_t ticks);

The ticks argument to the delay(9F) and timeout(9F) routines has been changed from long to clock_t.

rmallocmap() and rmallocmap_wait() Argument Changes

struct map *rmallocmap(size_t mapsize);
struct map *rmallocmap_wait(size_t mapsize);

The mapsize argument to the rmallocmap(9F) and rmallocmap_wait(9F) routines has been changed from ulong_t to size_t.

scsi_alloc_consistent_buf() Argument Changes

struct buf *scsi_alloc_consistent_buf(struct scsi_address *ap,
    struct buf *bp, size_t datalen, uint_t bflags,
    int (*callback )(caddr_t), caddr_t arg);

In previous releases, datalen was defined as an int and bflags was defined as a ulong.

uiomove() Argument Changes

int uiomove(caddr_t address, size_t nbytes,
    enum uio_rw rwflag, uio_t *uio_p);

The nbytes argument was defined as a type long, but because nbytes represents a size in bytes, size_t is more appropriate.

cv_timedwait() and cv_timedwait_sig() Argument Changes

int cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t timeout);
int cv_timedwait_sig(kcondvar_t *cvp, kmutex_t *mp,    clock_t timeout);

In previous releases, the timeout argument to the cv_timedwait(9F) and cv_timedwait_sig(9F) routines was defined to be of type long. Because these routines represent time in ticks, clock_t is more appropriate.

ddi_device_copy() Argument Changes

int ddi_device_copy(ddi_acc_handle_t src_handle,
    caddr_t src_addr, ssize_t src_advcnt,
    ddi_acc_handle_t dest_handle, caddr_t dest_addr,
    ssize_t dest_advcnt, size_t bytecount, uint_t dev_datasz);

The src_advcnt, dest_advcnt, dev_datasz arguments have changed type. These arguments were previously defined as long, long, and ulong_t respectively.

ddi_device_zero() Argument Changes

int ddi_device_zero(ddi_acc_handle_t handle,
    caddr_t dev_addr, size_t bytecount, ssize_t dev_advcnt,
    uint_t dev_datasz):

In previous releases, dev_advcnt was defined as a type long and dev_datasz as a ulong_t.

ddi_dma_mem_alloc() Argument Changes

int ddi_dma_mem_alloc(ddi_dma_handle_t handle,
    size_t length, ddi_device_acc_attr_t *accattrp,
    uint_t flags, int (*waitfp)(caddr_t), caddr_t arg,
    caddr_t *kaddrp, size_t *real_length,
    ddi_acc_handle_t *handlep);

In previous releases, length, flags, and real_length were defined with types uint_t, ulong_t, and uint_t *.

Modify Routines That Handle Data Sharing

If a device driver shares data structures that contain longs or pointers with a 32-bit application using ioctl(9E), devmap(9E), or mmap(9E), and the driver is recompiled for a 64-bit kernel, the binary layout of data structures will be incompatible. If a field is currently defined in terms of type long and 64-bit data items are not used, change the data structure to use data types that remain as 32-bit quantities (int and unsigned int). Otherwise, the driver needs to be aware of the different structure shapes for ILP32 and LP64 and determine whether a model mismatch between the application and the kernel has occurred.

To handle potential data model differences, the ioctl(), devmap(), and mmap() driver entry points, which interact directly with user applications, need to be written to determine whether the argument came from an application using the same data model as the kernel.

Data Sharing in ioctl()

To determine whether a model mismatch exists between the application and the driver, the driver uses the FMODELS mask to determine the model type from the ioctl() mode argument. The following values are OR-ed into mode to identify the application data model:

The code examples in I/O Control Support for 64-Bit Capable Device Drivers show how this situation can be handled using ddi_model_convert_from(9F).

Data Sharing in devmap()

To enable a 64-bit driver and a 32-bit application to share memory, the binary layout generated by the 64-bit driver must be the same as the layout consumed by the 32-bit application. The mapped memory being exported to the application might need to contain data-model-dependent data structures.

Few memory-mapped devices face this problem because the device registers do not change size when the kernel data model changes. However, some pseudo-devices that export mappings to the user address space might want to export different data structures to ILP32 or LP64 applications. To determine whether a data model mismatch has occurred, devmap(9E) uses the model parameter to describe the data model expected by the application. The model parameter is set to one of the following values:

The model parameter can be passed untranslated to the ddi_model_convert_from(9F) routine or to STRUCT_INIT(). See 32-bit and 64-bit Data Structure Macros.

Data Sharing in mmap()

Because mmap(9E) does not have a parameter that can be used to pass data model information, the driver's mmap(9E) entry point can be written to use the new DDI function ddi_model_convert_from(9F). This function returns one of the following values to indicate the application's data type model:

As with ioctl() and devmap(), the model bits can be passed to ddi_model_convert_from(9F) to determine whether data conversion is necessary, or the model can be handed to STRUCT_INIT().

Alternatively, migrate the device driver to support the devmap(9E) entry point.

Check Structures with 64-bit Long Data Types on x86-Based Platforms

You should carefully check structures that use 64-bit long types, such as uint64_t, on the x86 platforms. The alignment and size can differ between compilation in 32-bit mode versus a 64-bit mode. Consider the following example.

#include &lt;studio>
#include &ltsys>

struct myTestStructure {
        uint32_t        my1stInteger;
        uint64_t        my2ndInteger;
};

main()
{
        struct myTestStructure a;

        printf("sizeof myTestStructure is: %d\n", sizeof(a));
        printf("offset to my2ndInteger is: %d\n", (uintptr_t)&a.bar - (uintptr_t)&a);
}

On a 32-bit system, this example displays the following results:


sizeof myTestStructure is: 12
offset to my2ndInteger is: 4

Conversely, on a 64-bit system, this example displays the following results:


sizeof myTestStructure is: 16
offset to my2ndInteger is: 8

Thus, the 32-bit application and the 64-bit application view the structure differently. As a result, trying to make the same structure work in both a 32-bit and 64-bit environment can cause problems. This situation occurs often, particularly in situations where structures are passed into and out of the kernel through ioctl() calls.

Well Known ioctl Interfaces

Many ioctl(9E) operations are common to a class of device drivers. For example, most disk drivers implement many of the dkio(7I) family of ioctls. Many of these interfaces copy in or copy out data structures from the kernel, and some of these data structures have changed size in the LP64 data model. The following section lists the ioctlsthat now require explicit conversion in 64-bit driver ioctl routines for the dkio, fdio(7I), fbio(7I), cdio(7I), and mtio(7I) families of ioctls.

ioctl command

Affected data structure 

Reference 

DKIOCGAPART

DKIOCSAPART

dk_map

dk_allmap

dkio(7I)

DKIOGVTOC

DKIOSVTOC

partition

vtoc

dkio(7I)

FBIOPUTCMAP

FBIOGETCMAP

fbcmap

fbio(7I)

FBIOPUTCMAPI

FBIOGETCMAPI

fbcmap_i

fbio(7I)

FBIOCCURSOR

FBIOSCURSOR

fbcursor

fbio(7I)

CDROMREADMODE1

CDROMREADMODE2

cdrom_read

cdio(7I)

CDROMCDDA

cdrom_cdda

cdio(7I)

CDROMCDXA

cdrom_cdxa

cdio(7I)

CDROMSUBCODE

cdrom_subcode

cdio(7I)

FDIOCMD

fd_cmd

fdio(7I)

FDRAW

fd_raw

fdio(7I)

MTIOCTOP

mtop

mtio(7I)

MTIOCGET

mtget

mtio(7I)

MTIOCGETDRIVETYPE

mtdrivetype_request

mtio(7I)

USCSICMD

uscsi_cmd

scsi_free_consistent_buf(9F)

Device Sizes

The nblocks property is exported by each slice of a block device driver. This property contains the number of 512-byte blocks that each slice of the device can support. The nblocks property is defined as a signed 32-bit quantity, which limits the maximum size of a slice to 1 Tbyte.

Disk devices that provide more than 1 Tbyte of storage per disk must define the Nblocks property, which should still contain the number of 512 byte blocks that the device can support. However, Nblocks is a signed 64-bit quantity, which removes any practical limit on disk space.

The nblocks property is now deprecated. All disk devices should provide the Nblocks property.

Appendix D Console Frame Buffer Drivers

Drivers for frame buffers that are used for the system console must provide interfaces to enable the system to display text on the console. The Oracle Solaris OS provides enhanced visual I/O interfaces to enable the kernel terminal emulator to display text directly on the console frame buffer. This appendix describes how to add the necessary interfaces to a frame buffer driver to enable the driver to interact with the Oracle Solaris kernel terminal emulator.

Oracle Solaris Consoles and the Kernel Terminal Emulator

The role of the kernel terminal emulator is to render text onto the console frame buffer in the proper position and representation determined by the frame buffer's screen height, width, and pixel depth mode. The terminal emulator also drives scrolling, controls a software cursor, and interprets ANSI terminal escape sequences. The terminal emulator accesses the console frame buffer in either VGA text mode or pixel mode, depending upon the graphics card. To be used as a Oracle Solaris console frame buffer driver, your frame buffer driver must be compatible with the Oracle Solaris kernel terminal emulator. The target platform is the most significant factor that determines whether you need to modify your frame buffer driver to make your driver compatible with the Oracle Solaris kernel terminal emulator.

x86 Platform Console Communication

On x86 platforms, the Oracle Solaris kernel terminal emulator module (tem) uses VGA text mode exclusively to interact with the vgatext module. The vgatext module uses industry standard VGA text mode to interact with x86 compatible frame buffer devices. Because the vgatext module already supports the console frame buffer interfaces, x86 frame buffer drivers are compatible with the kernel tem module. You do not need to add special interfaces to x86 frame buffer drivers.

The remainder of this appendix applies to SPARC platforms only.

SPARC Platform Console Communication

SPARC frame buffer drivers typically do not operate in VGA text mode. SPARC frame buffer drivers typically are required to send pixel patterns that depict the text and images displayed. The kernel tem requires SPARC drivers to support specific interfaces to facilitate rendering data to the screen, perform scrolling, and display a text cursor. How the driver actually renders data sent from the tem onto the screen depends on the device. The driver typically draws the data into video memory according to the hardware and video mode.

The Oracle Solaris OS provides interfaces that enable the kernel terminal emulator to drive compatible console frame buffers directly. The advantages of converting a driver to be compatible with the kernel terminal emulator are:

SPARC console frame buffer drivers are not required to be compatible with the kernel terminal emulator. If the console frame buffer driver is not compatible with the kernel terminal emulator, the system uses the FCode terminal emulator in the OpenBoot PROM.

The console frame buffer is identified through the EEPROM screen environment variable. The system determines whether the console frame buffer is compatible with the kernel terminal emulator module by checking whether the frame buffer driver exports the tem-support DDI property. If the tem-support property is exported, then the system issues the VIS_DEVINIT I/O control (ioctl) command to the frame buffer driver during system boot, while configuring the console. If the tem-support DDI property is exported and the VIS_DEVINIT ioctl command succeeds and returns a compatible version number to the tem, the system configures the system console to utilize that frame buffer driver through the kernel terminal emulator. See the ioctl(9E) man page for information about the I/O control driver entry point.

SPARC drivers that support the kernel terminal emulator should export the tem-support DDI property. This property indicates that the driver supports the kernel terminal emulator. If a frame buffer driver exports the tem-support DDI property, then that driver will be handled early in the boot process, while the console is being configured. If a frame buffer driver does not export the tem-support property, then that driver might not be handled early enough in the boot process.

tem-support

When set to 1, this DDI property indicates that this driver is compatible with the console kernel frame buffer interface.

The kernel terminal emulator module interacts with the console frame buffer driver through two major interfaces:

The following section provides detailed information.

Console Visual I/O Interfaces

The kernel terminal emulator interacts with the console frame buffer driver through two interfaces. During normal system activity (after a successful boot of the system), communication between the kernel terminal emulator and the console frame buffer driver is through ioctl interfaces. During standalone mode (before system boot or during debugging), communication between the kernel terminal emulator and the console frame buffer driver is through polled I/O interfaces. All activity between the kernel terminal emulator and the console frame buffer driver is initiated by the kernel terminal emulator, with the exception of a callback function used by the console frame buffer driver to notify the kernel terminal emulator of changes in the video mode.

The console visual I/O interfaces are documented in detail in the visual_io(7I) man page. For more information on the video mode change callback function, see Video Mode Change Callback Interface.

I/O Control Interfaces

During normal system activity, the kernel terminal emulator communicates with the console frame buffer driver through the ioctl interfaces listed in the following table:

ioctl Name

Corresponding Data Structure 

Description 

VIS_DEVINIT

vis_devinit

Initializes the session between the terminal emulator module and the frame buffer. See VIS_DEVINIT.

VIS_DEVFINI

Not Applicable 

Terminates the session between the terminal emulator module and the frame buffer. See VIS_DEFINI.

VIS_CONSDISPLAY

vis_consdisplay

Displays pixels as a rectangle. See VIS_CONSDISPLAY.

VIS_CONSCOPY

vis_conscopy

Copies a rectangle of pixels (scroll). See VIS_CONSCOPY.

VIS_CONSCURSOR

vis_conscursor

Displays or hides a text cursor. See VIS_CONSCURSOR.

VIS_PUTCMAP

vis_cmap

Sends the terminal emulator module color map to the frame buffer driver. See VIS_PUTCMAP.

VIS_GETCMAP

vis_cmap

Reads the terminal emulator module color map from the frame buffer. See VIS_GETCMAP.

Polled I/O Interfaces

The polled I/O interfaces provide the same functionality as the VIS_CONSDISPLAY, VIS_CONSCOPY, and VIS_CONSCURSOR ioctl interfaces. The polled I/O interfaces are called only when the operating system is quiesced and in standalone mode. See Implementing Polled I/O in Console Frame Buffer Drivers for more information.

While in standalone mode, the kernel terminal emulator communicates with the console frame buffer driver through the polled I/O interfaces listed in the following table:

Polled I/O Function 

Corresponding Data Structure 

Description 

(*display)()

vis_consdisplay

Displays pixels as a rectangle. 

(*copy)()

vis_conscopy

Copies a rectangle of pixels (scroll). 

(*cursor)()

vis_conscursor

Displays or hides a text cursor. 

Video Mode Change Callback Interface

The console frame buffer driver and the kernel terminal emulator must be in agreement about the video mode at all times. Video mode includes the console screen height, width, and depth in pixels. Video mode also includes whether communication between the kernel terminal emulator and the console frame buffer is in VGA text mode or pixel mode.

In order for the console frame buffer driver to notify the kernel terminal emulator of changes in the video mode, the console frame buffer driver is initialized with the address of the (*modechg_cb)() kernel terminal emulator callback function described in the following table:

Callback Function 

Corresponding Data Structures 

Description 

(*modechg_cb)()

vis_modechg_arg

vis_devinit

Keep the terminal emulator module synchronized with the driver video mode (screen height, width, and pixel depth). 

Implementing the Visual I/O Interfaces in Console Frame Buffer Drivers

Except for the video mode change callback, all activity between the driver and the kernel terminal emulator is initiated by the tem (terminal emulator module). This means that the tem issues all of the ioctl commands described in this document. The following sections provide implementation details for each ioctl command. For more information, see the visual_io(7I) man page and the /usr/include/sys/visual_io.h include file. See Video Mode Change Callback Interface for detailed information about the video mode change callback function.


Note –

Each ioctl command should determine whether the FKIOCTL is set in the ioctl flag argument and return EPERM if that bit is not set.


VIS_DEVINIT

The VIS_DEVINIT ioctl command initializes the frame buffer driver as the system console device. This ioctl passes the address of a vis_devinit structure.

The tem first loads the address of its video mode change callback function into the modechg_cb field of the vis_devinit structure and loads its soft state into the modechg_arg field. The tem then issues the VIS_DEVINIT ioctl command. The frame buffer driver then initializes itself and returns a summary of its configuration back to the tem by setting the version, width, height, linebytes, depth, mode, and polledio fields in the vis_devinit structure. The vis_devinit structure is shown in the following code.

struct vis_devinit {
      /*
       * This set of fields are used as parameters passed from the
       * layered frame buffer driver to the terminal emulator.
       */
      int             version;        /* Console IO interface rev */
      screen_size_t   width;          /* Width of the device */
      screen_size_t   height;         /* Height of the device */
      screen_size_t   linebytes;      /* Bytes per scan line */
      int             depth;          /* Device depth */
      short           mode;           /* Display mode Mode */
      struct vis_polledio *polledio;  /* Polled output routines */
      /*
       * The following fields are used as parameters passed from the
       * terminal emulator to the underlying frame buffer driver.
       */
      vis_modechg_cb_t modechg_cb;   /* Video mode change callback */
      struct vis_modechg_arg *modechg_arg;  /* Mode change cb arg */
};

To implement the VIS_DEVINIT ioctl command in the console frame buffer driver, follow these general steps:

  1. Define a struct to contain the console-specific state. This structure is private to the console frame buffer driver. This structure is referred to as consinfo in this appendix. The consinfo structure contains information such as:

    • Current size of the blit buffer

    • Pointer to the blit buffer

    • Color map information

    • Driver rendering mode information such as line pitch

    • Background color

    • Video memory address

    • Terminal emulator callback address

  2. Allocate memory:

    1. Allocate a blit buffer large enough to store a reasonable default sized rectangle of pixels at the highest video depth. Additional memory can be allocated if an incoming request exceeds the size of the buffer. The frame buffer driver's largest font is 12×22. Assuming DEFAULT_HEIGHT is 12, DEFAULT_WIDTH is 22, and the maximum video depth is 32, the buffer size should be 8448 bytes (DEFAULT_HEIGHT × DEFAULT_WIDTH × 32).

    2. Allocate a vis_polledio structure.

    3. Allocate a buffer to hold a cursor. This buffer should be the size of the largest character. This buffer will not change size.

  3. Obtain the video change callback address and callback context of the tem from modechg_cb and modechg_ctx and store this information in the consinfo structure.

  4. Populate the vis_polledio structure with entry point addresses for the polled display, copy, and cursor functions.

  5. Provide the appropriate information in the fields of the vis_devinit structure that was passed to the driver by the tem:

    1. Set the version field to VIS_CONS_REV, which is a constant defined in the /usr/include/sys/visual_io.h header file.

    2. Set the mode field to VIS_PIXEL.

    3. Set the polledio field to the address of the vis_polledio structure.

    4. Set the height field to the video mode height in pixels.

    5. Set the width field to the video mode width in pixels.

    6. Set the depth field to the frame buffer pixel depth in bytes (for example, a 32-bit pixel depth would be 4 bytes).

    7. Set the linebytes field to the value of height × width × depth.

      This information is sent from the driver to the tem by using the vis_devinit structure. This information tells the terminal emulator how to render information and pass it to the graphics driver.

    Whenever the console frame buffer driver changes its video mode (specifically height, width, or depth), the driver must call the video mode change callback function of the tem to update the vis_devinit structure and to pass this structure back to the terminal emulator. The terminal emulator passes its mode change callback function address in the modechg_cb field of the vis_devinit structure. The mode change callback function has the following function signature:

    typedef void (*vis_modechg_cb_t)
          (struct vis_modechg_arg *, struct vis_devinit *);

    As shown in the preceding typedef, the mode change callback function takes two arguments. The first argument is the modechg_arg and the second argument is the vis_devinit structure. The modechg_arg is sent from the tem to the driver during the VIS_DEVINIT ioctl command initialization. The driver must send the modechg_arg back to the tem with each video mode change callback.

  6. Initialize the context of the kernel console. Specific requirements vary depending upon the capability of the graphics device. This initialization might include such steps as setting the draw engine state, initializing the palette, or locating and mapping video memory or the rendering engine so that data can be blitted onto the screen.

  7. Return the vis_devinit structure to the caller.

VIS_DEFINI

The VIS_DEFINI ioctl command releases the driver's console resources and finishes the session.

To implement the VIS_DEVFINI ioctl command in the console frame buffer driver, follow these general steps:

  1. Reset the console frame buffer driver state.

  2. Clear the polled I/O entry points and the kernel terminal emulator video change function callback address.

  3. Release memory.

VIS_CONSDISPLAY

The VIS_CONSDISPLAY ioctl command displays a rectangle of pixels at a specified location. This display is also referred to as blitting a rectangle. The vis_consdisplay structure contains the information necessary to render a rectangle at the video depth that both the driver and the tem are using. The vis_consdisplay structure is shown in the following code.

struct vis_consdisplay {
      screen_pos_t    row;      /* Row (in pixels) to display data at */
      screen_pos_t    col;      /* Col (in pixels) to display data at */
      screen_size_t   width;    /* Width of data (in pixels) */
      screen_size_t   height;   /* Height of data (in pixels) */
      unsigned char   *data;    /* Address of pixels to display */
      unsigned char   fg_color; /* Foreground color */
      unsigned char   bg_color; /* Background color */
};

To implement the VIS_CONSDISPLAY ioctl command in the console frame buffer driver, follow these general steps:

  1. Copy the vis_consdisplay structure.

  2. Validate the display parameters. Return an error if any of the display parameters is out of range.

  3. Calculate the size of the rectangle to be blitted into video memory. Validate this size against the size of the blit buffer created during VIS_DEVINIT. Allocate additional memory for the blit buffer if necessary.

  4. Retrieve the blit data. This data has been prepared by the kernel terminal emulator at the agreed upon pixel depth. That depth is the same pixel depth that was conveyed by the tem during VIS_DEVINIT. The pixel depth is updated whenever the device driver changes video modes through callback to the tem. Typical pixel depths are 8-bit color map indexed, and 32-bit TrueColor.

  5. Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.

  6. Establish the driver-specific console rendering context.

  7. If the frame buffer is running in 8-bit color indexed mode, restore the kernel console color map that the tem set up through a previous VIS_PUTCMAP ioctl. A lazy color map loading scheme is recommended to optimize performance. In a lazy scheme, the console frame buffer only restores colors it has actually used since the VIS_DEVINIT ioctl was issued.

  8. Display the data passed from the tem at the pixel coordinates sent by the tem. You might need to transform the RGB pixel data byte order.

VIS_CONSCOPY

The VIS_CONSCOPY ioctl command copies a rectangular region of pixels from one location to another location. One use for this ioctl is to scroll.

To implement the VIS_CONSCOPY ioctl command in the console frame buffer driver, follow these general steps:

  1. Copy the vis_conscopy structure. The vis_conscopy structure describes the source and target rectangle sizes and locations.

  2. Validate the display parameters. Return an error if any of the display parameters is out of range.

  3. Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.

  4. Call the function to copy the rectangle.


    Note –

    For optimal performance, use the rendering engine of the graphic device to implement the copy function. You need to decide how to do the context management within the driver to set up the rendering engine for best performance.


VIS_CONSCURSOR

The VIS_CONSCURSOR ioctl command displays or hides a cursor. The vis_conscursor structure is shown in the following code.

struct vis_conscursor {
      screen_pos_t    row;      /* Row to display cursor (in pixels) */
      screen_pos_t    col;      /* Col to display cursor (in pixels) */
      screen_size_t   width;    /* Width of cursor (in pixels) */
      screen_size_t   height;   /* Height of cursor (in pixels) */
      color_t         fg_color; /* Foreground color */
      color_t         bg_color; /* Background color */
      short           action;   /* Show or Hide cursor */
};

To implement the VIS_CONSCOPY ioctl command in the console frame buffer driver, follow these general steps:

  1. Copy the vis_conscursor structure from the kernel terminal emulator.

  2. Validate the display parameters. Return an error if any of the display parameters are out of range.

  3. Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.

  4. The terminal emulator can call the VIS_CONSCOPY ioctl with one of the following two actions: SHOW_CURSOR and HIDE_CURSOR. The following steps describe how to implement this functionality by reading and writing video memory. You might also be able to use the rendering engine to do this work. Whether you can use the rendering engine depends on the frame buffer hardware.

    Take these steps to implement the SHOW_CURSOR functionality:

    1. Save the pixels within the rectangle where the cursor will be drawn. These saved pixels will be needed to hide the cursor.

    2. Scan all the pixels on the screen bounded by the rectangle where the cursor will be drawn. Within this rectangle, replace the pixels that match the specified cursor foreground color (fg_color) with white pixels. Replace the pixels that match the specified cursor background color (bg_color) with black pixels. The visual effect is of a black cursor over white text. This method works with any foreground and background color of text. Attempting to invert colors based upon color map position is not feasible. More sophisticated strategies, such as attempting color inversion using HSB coloring (Hue, Saturation, Brightness), are not necessary.

    To implement the HIDE_CURSOR functionality, replace the pixels beneath the cursor rectangle with the pixels saved from the previous SHOW_CURSOR action.

VIS_PUTCMAP

The VIS_PUTCMAP ioctl command establishes the console color map. The terminal emulator calls this function to set up the color map of the kernel. The vis_cmap structure is shown in the following code. This structure only applies to 8-bit color indexed mode.

struct vis_cmap {
      int             index;  /* Index into colormap to start updating */
      int             count;  /* Number of entries to update */
      unsigned char   *red;   /* List of red values */
      unsigned char   *green; /* List of green values */
      unsigned char   *blue;  /* List of blue values */
};

The VIS_PUTCMAP ioctl command is similar to the FBIOPUTCMAP command. The VIS_PUTCMAP command is specific to the frame buffer terminal-emulator compatible console code.

VIS_GETCMAP

The terminal emulator calls the VIS_GETCMAP ioctl command to retrieve the console color map.

Implementing Polled I/O in Console Frame Buffer Drivers

The polled I/O interfaces are implemented as functions in the driver and are called directly by the kernel terminal emulator. The driver passes the address of its polled I/O entry points to the terminal emulator during the execution of the VIS_DEVINIT ioctl command. The VIS_DEVINIT command is initiated by the terminal emulator.

The vis_polledio structure is shown in the following code.

typedef void * vis_opaque_arg_t;

struct vis_polledio {
      struct vis_polledio_arg *arg;
      void    (*display)(vis_opaque_arg_t, struct vis_consdisplay *);
      void    (*copy)(vis_opaque_arg_t, struct vis_conscopy *);
      void    (*cursor)(vis_opaque_arg_t, struct vis_conscursor *);
};

The polled I/O interfaces provide the same functionality as the VIS_CONSDISPLAY, VIS_CONSCOPY, and VIS_CONSCURSOR ioctl interfaces. The polled I/O interfaces should follow the same steps that are described above for the respective ioctl commands. The polled I/O interfaces must very strictly adhere to the additional restrictions that are described in the remainder of this section.

The polled I/O interfaces are called only when the operating system is quiesced and in standalone mode. The system enters standalone mode whenever the user enters OpenBoot PROM or enters the kmdb debugger, or when the system panics. Only one CPU and one thread are active. All other CPUs and threads are stopped. Timesharing, DDI interrupts, and system services are turned off.

Standalone mode severely restricts driver functionality but simplifies driver synchronization requirements. For example, a user application cannot access the console frame buffer driver by way of the driver's memory mappings from within a polled I/O routine.

In standalone mode, the console frame buffer driver must not perform any of the following actions:

These restrictions are not difficult to obey since the polled I/O functions are relatively simple operations. For example, when working with the rendering engine, the console frame buffer driver can poll a bit in the device rather than wait for an interrupt. The driver can use pre-allocated memory to render blit data. DDI or LDI interfaces should not be needed.

Frame Buffer Specific Configuration Module

When the driver-specific fbconfig() module causes a change in resolution or color depth, that fbconfig() module must send an ioctl to the frame buffer driver. This ioctl triggers the frame buffer driver to call the terminal emulator's mode change callback function with the new screen size and depth. The frame buffer driver and the terminal emulator must agree about the video mode at all times. When the frame buffer driver and the terminal emulator do not agree about the video mode, the information on the screen is illegible and meaningless.

The X Window System Frame Buffer Specific DDX Module

When the X Window System exits to the command line, the frame buffer's DDX module must send an ioctl to the frame buffer driver. This ioctl triggers the frame buffer driver to call the terminal emulator's mode change callback function. This communication keeps the frame buffer driver and the terminal emulator in agreement about the video mode if the X Window System starts and then changes the video resolution before exiting. The frame buffer driver and the terminal emulator must agree about the video mode at all times. When the frame buffer driver and the terminal emulator do not agree about the video mode, the information on the screen is illegible and meaningless.

Developing, Testing, and Debugging Console Frame Buffer Drivers

Debugging a console frame buffer driver on an active system can be problematic.

This section offers some suggestions to help you develop, test, and debug console frame buffer drivers.

Testing the I/O Control Interfaces

To test the ioctl commands, create additional ioctl entry points that are callable from a user application. Be sure to copy in the arguments appropriately. Use the ddi_copyin(9F) and ddi_copyout(9F) routines to transfer data to and from user address space. Then write an application to validate rendering, scrolling, and cursor behavior. This way, these ioctl commands do not affect your console while you develop and test the commands.

To ensure that the ioctl commands are working correctly, boot the system and log in. Check whether you get expected behavior when you execute commands such as prstat(1M), ls(1), vi(1), and man(1).

Execute the following script to validate that ANSI color is working correctly:

#!/bin/bash
printf "\n\n\n\e[37;40m             Color List       \e[m\n\n"
printf "\e[30m Color 30 black\e[m\n"
printf "\e[31m Color 31 red\e[m\n"
printf "\e[32m Color 32 green\e[m\n"
printf "\e[33m Color 33 yellow\e[m\n"
printf "\e[34m Color 34 blue\e[m\n"
printf "\e[35m Color 35 purple\e[m\n"
printf "\e[36m Color 36 cyan\e[m\n"
printf "\e[37m Color 37 white\e[m\n\n"
printf "\e[40m Backlight 40 black \e[m\n"
printf "\e[41m Backlight 41 red   \e[m\n"
printf "\e[34;42m Backlight 42 green \e[m\n"
printf "\e[43m Backlight 43 yellow\e[m\n"
printf "\e[37;44m Backlight 44 blue  \e[m\n"
printf "\e[45m Backlight 45 purple\e[m\n"
printf "\e[30;46m Backlight 46 cyan  \e[m\n"
printf "\e[30;47m Backlight 47 white \e[m\n\n"

Testing the Polled I/O Interfaces

The polled I/O interfaces are only available under the following circumstances:

The polled I/O interfaces only become available at a certain point in the boot process. Polled I/O requests issued from the OpenBoot PROM before the system is running are not rendered. Similarly, kmdb prompts issued before the console is configured are not rendered.

To test the polled I/O interfaces, enter the OpenBoot PROM by using the L1+A keystroke sequence. To validate that the polled I/O interfaces are being used, type the following command at the OpenBoot PROM ok prompt:


ok 1b emit ." [32m This is a test" 1b emit ." [m"

The polled I/O interfaces are working properly if the following statements are true:

Testing the Video Mode Change Callback Function

To determine whether the video mode change callback function is working properly, log in to the system and use fbconfig(1M) to change the resolution and depth of the frame buffer several times. If the console continues to display text properly, the video mode change callback function is working correctly. The kernel terminal emulator might adjust the font size to accommodate different screen sizes, but that is not significant to the console frame buffer driver.

To determine whether the X Window System and the console frame buffer driver interact correctly, switch between the X Window System and the command line several times while modifying the X Window System's video resolution and the command line resolution in different ways. If the X Window System exits and the console characters are not displayed correctly, either the X Window System did not notify the driver console code that the video mode changed or the driver did not call the kernel terminal emulator's video mode change callback function.

Additional Suggestions for Testing Console Frame Buffer Drivers

During boot, the system sends messages to /var/adm/messages if the system fails to locate or successfully load a kernel terminal emulator compatible frame buffer driver. To monitor these messages, type the following command in a separate window:


% tail -f /var/adm/messages

To avoid problems with USB while debugging the driver, change the EEPROM input-device NVRAM configuration parameter to use a serial port instead of the keyboard. See the eeprom(1M) man page for more information about this parameter.