This chapter describes:
The following sections describe:
The descriptions of the kernel interfaces are presented in alphabetical order and in reference-page style. The descriptions can include the following sections.
This section lists the name of the kernel interface, along with a summary description of its purpose. In general, there is one interface described for each reference page. However, in some cases it makes sense to describe more than one interface on the same page if the interfaces are related. When this occurs, this section lists the names of all the interfaces it describes.
This section shows the kernel interface function definition. The style used is that of the function definition, not the function call. This book assumes that you understand how to interpret the function definition and how to write an appropriate call for a specific interface. The following example shows these conventions:
int copyin
(user_src,
kernel_dest,
bcount)
caddr_t
user_src;
caddr_t
kernel_dest;
u_int
bcount;
The kernel interface function definition gives you the following information:
The data type of the return value, if the kernel interface returns data. If the kernel interface does not return a value, the void data type is used.
The kernel interface name, for example, copyin. Some kernel interfaces are in uppercase.
The name of each kernel interface argument name. In the example, the argument names are user_src, kernel_dest, and bcount.
The types for each of the arguments. In the example, these types are caddr_t and u_int.
This book uses the word kernel ``interface'' instead of kernel ``routine'' or kernel ``macro.''
This section provides descriptions for the arguments associated with a given kernel interface. In most cases, argument descriptions begin with the word specifies to indicate that you pass the argument (with some specified value) to the kernel interface. If the type of the argument appears as a void *, the argument description states that you must define the type.
This section
contains explanations of the tasks performed by the kernel interface.
This section discusses information that falls into the following categories:
Some kernel interfaces behave differently depending on the architecture of the hardware.
Some kernel interfaces behave differently depending on the implementation of the operating system.
Some kernel interfaces require specific information important to the device driver writer.
This section provides information of particular importance when you use the kernel interface. In many cases, the text in this section alerts you to anything that might cause a panic.
This section provides the section where you can find an example of the kernel interface.
This section provides the possible error constants a given kernel interface can return, along with a short description of the error.
This section describes special processing performed by the kernel interface. For example, the Side Effects section for the uiomove kernel interface describes the members of the uio structure that it can update.
This section describes the return values that a given kernel interface can return. In most cases, if the kernel interface returns an error value, this value is described in the Return Values section.
This section lists related kernel interfaces, structures, system calls, and so forth.
The description of ioctl commands and interfaces (macros) are presented in reference-page style. The description can include the following sections.
This section lists the name of the ioctl command or interface, along with a summary description of its purpose.
For ioctl commands, this section lists the associated header files. For ioctl interfaces, this section shows the interface definition. For example: #include <sys/ioctl.h>
_IO
(g,
n)
The interface definition shows the header file where the
ioctl
interface is defined.
The definition also shows the arguments passed to the interface.
This section provides descriptions for the arguments associated with a given ioctl interface. In most cases, argument descriptions begin with the word specifies to indicate that you pass the argument (with some specified value) to the ioctl interface.
This section describes ioctl commands or interfaces. The description could include information on associated structures and members.
This section provides an example showing how to construct a driver-specific interface that uses the specified ioctl interface.
This section lists related kernel interfaces, structures, system calls, and so forth.
The descriptions of the global variables are presented in alphabetical order and in reference-page style. The descriptions can include the following sections.
This section lists the name of the global variable, along with a short description of its purpose.
This section provides the declaration of the global variable.
This section presents the pathname for the header file that defines the global variable.
This section describes the purpose of the global variable.
This section discusses information about the global variable pertinent
to the device driver writer.
Table 2-1
summarizes the kernel interfaces that nonSTREAMS device drivers can use.
Note
Device drivers use the following header files most frequently:
#include <sys/types.h> #include <sys/errno.h> #include <io/common/devdriver.h> #include <sys/uio.h> #include <machine/cpu.h> #include <sys/conf.h> #include <sys/sysconfig.h>
Kernel Interface | Summary Description |
assert_wait_mesg | Asserts that the current kernel thread is about to block (sleep). |
atoi | Converts an ASCII string to an integer. |
BADADDR | Probes the address during device autoconfiguration. |
bcmp | Compares two byte strings. |
bcopy | Copies a series of bytes with a specified limit. |
biodone | Indicates that block I/O is complete. |
blkclr | Zeros a block of memory. |
brelse | Deallocates a buf structure. |
btop | Converts bytes to number of pages. |
BUF_LOCK | Locks the specified I/O buffer. |
BUF_UNLOCK | Unlocks the specified I/O buffer. |
busphys_to_iohandle | Converts a valid bus physical address to an I/O handle base. |
bzero | Zeros a block of memory. |
cfgmgr_get_state | Determines the configuration state. |
cfgmgr_set_status | Reports failure to the cfgmgr framework. |
clear_wait | Clears the wait condition. |
configure_driver | Configures a device driver that is in the dynamic state. |
contig_free | Frees a block of memory previously allocated by contig_malloc. |
contig_malloc | Allocates physically contiguous memory. |
copyin | Copies data from a user address space to a kernel address space. |
copyinstr | Copies a null-terminated string from a user address space to a kernel address space. |
copyout | Copies data from a kernel address space to a user address space. |
copyoutstr | Copies a null-terminated string from a kernel address space to a user address space. |
copystr | Copies a null-terminated character string with a specified limit. |
copy_to_phys | Copies data from a virtual address to a physical address. |
create_controller_struct | Creates a controller structure for the system (hardware) configuration tree. |
create_device_struct | Creates a device structure for the system (hardware) configuration tree. |
current_task | Returns a pointer to the task structure associated with the currently running kernel thread. |
current_thread | Is a pointer to the currently running kernel thread. |
decl_simple_lock_data | Declares a simple lock structure. |
DELAY | Delays the calling interface a specified number of microseconds. |
devsw_add | Registers driver entry points and reserves a major number. |
devsw_del | Clears entry points from the device switch table. |
devsw_get | Obtains a previously reserved major number. |
dma_get_curr_sgentry | Returns a pointer to the current sg_entry. |
dma_get_next_sgentry | Returns a pointer to the next sg_entry. |
dma_get_private | Gets a data element from the DMA private storage space. |
dma_kmap_buffer | Returns a kernel segment (kseg) address of a DMA buffer. |
dma_map_alloc | Allocates resources for DMA data transfers. |
dma_map_dealloc | Releases and deallocates the DMA resources previously allocated for DMA data transfers. |
dma_map_load | Loads and sets allocated DMA resources and sets up a DMA data path for DMA data transfers. |
dma_map_unload | Unloads the system DMA resources. |
dma_min_boundary | Returns system-level information. |
dma_put_curr_sgentry | Puts a new bus address/byte count pair in the linked list of sg_entry structures. |
dma_put_prev_sgentry | Updates an internal pointer index to the linked list of sg_entry structures. |
dma_put_private | Stores a data element in the DMA private storage space. |
do_config | Initializes the device to its assigned configuration. |
drvr_register_flush | Registers or deregisters a flush interface. |
drvr_register_shutdown | Registers or deregisters a shutdown interface. |
ffs | Finds the first set bit in a mask. |
FREE | Deallocates (frees) the allocated kernel virtual memory. |
fubyte | Returns a byte from user address space. |
fuibyte | Returns a byte from user instruction address space. |
fuiword | Returns a word from user instruction address space. |
fuword | Returns a word from user instruction address space. |
get_config | Returns assigned configuration data for a device. |
get_def_partitionmap | Calculates a default partition map. |
get_info | Returns system-specific information. |
getnewbuf | Allocates a buf structure. |
gsignal | Sends a signal to a process group. |
handler_add | Registers a device driver's interrupt handler. |
handler_del | Deregisters a device driver's interrupt handler. |
handler_disable | Disables a previously registered interrupt handler. |
handler_enable | Enables a previously registered interrupt handler. |
htonl | Converts longword values from host-to-network byte order. |
htons | Converts word values from host-to-network byte order. |
insque | Adds an element to the queue. |
io_copyin | Copies data from bus address space to system memory. |
io_copyio | Copies data from bus address space to bus address space. |
io_copyout | Copies data from system memory to bus address space. |
iodone | Indicates that I/O is complete. |
iohandle_to_phys | Converts an I/O handle to a valid system physical address. |
io_zero | Zeros a block of memory in bus address space. |
IS_KSEG_VA | Determines if the specified address is located in the kernel-unmapped address space. |
IS_SEG0_VA | Determines if the specified address is located in the user-mapped address space. |
IS_SEG1_VA | Determines if the specified address is located in the kernel-mapped address space. |
kernel_isrthread | Starts a fixed priority kernel thread dedicated to interrupt service. |
kernel_thread_w_arg | Starts a kernel thread with a calling argument passed in. |
KSEG_TO_PHYS | Converts a kernel-unmapped virtual address to a physical address. |
lock_done | Releases a complex lock. |
lock_init | Initializes a complex lock. |
lock_read | Asserts a complex lock with read-only access. |
lock_terminate | Terminates, using a complex lock. |
lock_try_read | Tries to assert a complex lock with read-only access. |
lock_try_write | Tries to assert a complex lock with write access. |
lock_write | Asserts a complex lock with write access. |
major | Returns the device major number. |
makedev | Returns a dev_t. |
MALLOC | Allocates a variable-size section of kernel virtual memory. |
mb | Performs a memory barrier. |
minor | Returns the device minor number. |
minphys | Bounds the data transfer size. |
mpsleep | Blocks (puts to sleep) the current kernel thread. |
ntohl | Converts longword values from network-to-host byte order. |
ntohs | Converts word values from network-to-host byte order. |
ovbcopy | Copies a byte string with a specified limit. |
panic | Causes a system crash. |
physio | Implements raw I/O. |
PHYS_TO_KSEG | Converts a physical address to a kernel-unmapped virtual address. |
pmap_extract | Extracts a physical page address. |
pmap_kernel | Returns the physical map handle for the kernel. |
pmap_set_modify | Sets the modify bits of the specified physical page. |
printf | Prints text to the console and the error logger. |
privileged | Checks for proper privileges. |
psignal | Sends a signal to a process. |
queue_init | Initializes the specified queue. |
READ_BUS_D8 | Reads a byte (8 bits) from a device register. |
READ_BUS_D16 | Reads a word (16 bits) from a device register. |
READ_BUS_D32 | Reads a longword (32 bits) from a device register. |
READ_BUS_D64 | Reads a quadword (64 bits) from a device register. |
readdisklabel | Reads a disk label from a device. |
read_io_port | Reads data from a device register. |
register_callback | Registers a callback request (interface). |
remque | Removes an element from the queue. |
rmalloc | Allocates size units from the given resource map. |
rmfree | Frees space previously allocated into the specified resource map. |
rmget | Allocates size units from the given resource map. |
rminit | Initializes a resource map. |
round_page | Rounds the specified address. |
rt_post_callout | Posts an interface to be called at a lower IPL. |
rt_post_callout_ipl | Posts an interface to be called at a specified IPL. |
select_dequeue | Removes the last kernel thread waiting for an event. |
select_dequeue_all | Removes all kernel threads waiting for an event. |
select_enqueue | Adds the current kernel thread. |
select_wakeup | Wakes up a kernel thread. |
setdisklabel | Sets a disk label. |
simple_lock | Asserts a simple lock. |
simple_lock_init | Initializes a simple lock structure. |
simple_lock_terminate | Terminates, using a simple lock. |
simple_lock_try | Tries to assert a simple lock. |
simple_unlock | Releases a simple lock. |
sleep | Puts a calling process to sleep. |
spl | Sets the processor priority to mask different levels of interrupts. |
strcmp | Compares two null-terminated character strings. |
strcpy | Copies a null-terminated character string. |
strlen | Returns the number of characters in a null-terminated string. |
strncmp | Compares two strings, using a specified number of characters. |
strncpy | Copies a null-terminated character string with a specified limit. |
subyte | Writes a byte into user address space. |
suibyte | Writes a byte into user instruction address space. |
suiword | Writes a word into user instruction address space. |
suser | Checks whether the current user is the superuser. |
suword | Writes a word into user address space. |
svatophys | Converts a system virtual address to a physical address. |
swap_lw_bytes | Performs a longword byte swap. |
swap_word_bytes | Performs a short word byte swap. |
swap_words | Performs a word byte swap. |
thread_block | Blocks (puts to sleep) the current kernel thread. |
thread_halt_self | Handles asynchronous traps for self-terminating kernel threads. |
thread_set_timeout | Sets a timer for the current kernel thread. |
thread_terminate | Prepares to stop or stops execution of the specified kernel thread. |
thread_wakeup | Wakes up all kernel threads waiting for the specified event. |
thread_wakeup_one | Wakes up the first kernel thread waiting on a channel. |
timeout | Initializes a callout queue element. |
trunc_page | Truncates the specified address. |
uiomove | Moves data between user and system virtual space. |
unconfigure_driver | Unconfigures a device driver that was dynamically configured. |
unix_master | Forces execution onto the master CPU. |
unix_release | Releases binding of the kernel thread. |
unregister_callback | Deregisters a callback request (interface). |
untimeout | Removes the scheduled interface from the callout queues. |
uprintf | Is a nonsleeping kernel printf function. |
vm_map_pageable | Sets pageability of the specified address range. |
vtop | Converts any virtual address to a physical address. |
wakeup | Wakes up all processes sleeping on a specified address. |
WRITE_BUS_D8 | Writes a byte (8 bits) to a device register. |
WRITE_BUS_D16 | Writes a word (16 bits) to a device register. |
WRITE_BUS_D32 | Writes a longword (32 bits) to a device register. |
WRITE_BUS_D64 | Writes a quadword (64 bits) to a device register. |
writedisklabel | Writes a disk label. |
write_io_port | Writes data to a device register. |
Table 2-2 summarizes the kernel interfaces that STREAMS device drivers can use.
Kernel Interface | Summary Description |
adjmsg | Removes the specified number of bytes from a message. |
allocb | Allocates a message block. |
backq | Gets a pointer to the previous queue. |
bcanput | Tests for flow control in a specified priority band. |
bufcall | Gets a buffer when allocb fails. |
canput | Tests for room in a message queue. |
copyb | Copies a message block. |
copymsg | Copies a message to a new message. |
datamsg | Tests whether a message is a data message. |
dupb | Duplicates a message block descriptor. |
dupmsg | Duplicates a message. |
enableok | Enables a queue for service. |
esballoc | Allocates a message block with a shared buffer. |
flushband | Flushes messages for a specified priority band. |
flushq | Removes a message from a queue. |
freeb | Frees a message block. |
freemsg | Frees all message blocks in a message. |
getq | Gets a message from the front of the queue. |
insq | Inserts a STREAMS message into a queue. |
linkb | Concatenates two message blocks. |
msgdsize | Returns the number of bytes in a message. |
noenable | Prevents a queue from being scheduled. |
OTHERQ | Gets a pointer to a module's other queue. |
pullupmsg | Concatenates bytes in a message. |
putbq | Places a message at the head of a queue. |
putctl | Puts a control message on a queue. |
putctl1 | Puts a control message on a queue. |
putnext | Sends a message to the next module in the stream. |
putq | Puts a message on a queue. |
qenable | Enables a queue. |
qreply | Sends a message in the reverse direction. |
qsize | Finds the number of messages on a queue. |
RD | Gets a pointer to a module's read queue. |
rmvb | Removes a message block from a message block. |
rmvq | Removes a message block from a queue. |
strlog | Submits messages for logging. |
strqget | Gets information about a queue. |
strqset | Changes information about a queue. |
testb | Checks for an available buffer. |
unbufcall | Cancels a pending bufcall request. |
unlinkb | Removes a message block from the head of a message. |
WR | Gets a pointer to this module's write queue. |
Note
The kernel interface descriptions in this section do not discuss how a kernel interface acquires and releases the appropriate symmetric multiprocessing (SMP) lock. The reasons for this approach are that some kernel interfaces such as read_io_port and write_io_port do not need to acquire an SMP lock; some kernel interfaces need to acquire an SMP lock but the device driver writer does not need to know the intricacies of how the kernel interface acquires the SMP lock.
Unless otherwise noted, the kernel interfaces that need to acquire an SMP lock acquire the lock at the start of execution and release the lock when they complete execution.
Removes the specified number of bytes from a message
#include <sys/stream.h>
int adjmsg
(message_ptr,
len_to_trim)
MBLKP
message_ptr;
int
len_to_trim;
The adjmsg interface removes a specified number of bytes from a message. The absolute value of the len_to_trim argument specifies how many bytes that adjmsg removes. If the number of bytes passed to len_to_trim is greater than zero (0), adjmsg removes bytes from the head of the message. If the number of bytes passed to len_to_trim is less than zero (0), adjmsg removes bytes from the tail of the message.
The adjmsg interface fails if the value you pass to the len_to_trim argument is greater than the number of bytes stored in the message pointer argument, message_ptr.
If the adjmsg interface successfully trims the message, it returns the value 1. Otherwise, it returns the value zero (0).
Allocates a message block
#include <sys/stream.h>
MBLKP allocb
(size,
pri)
int
size;
uint
pri;
The allocb interface attempts to allocate a STREAMS message block. Buffer allocation fails only when the system is out of memory. If no buffer is available, you can call the bufcall interface, which helps a module recover from a memory allocation failure.
Upon successful completion, the allocb interface returns a pointer to the allocated message block. This message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
If allocb cannot allocate a message block, it returns a NULL pointer.
Asserts that the current kernel thread is about to block (sleep)
void assert_wait_mesg
(event,
interruptible,
message)
vm_offset_t
event;
boolean_t
interruptible;
char
*message;
Value | Meaning |
TRUE | The current kernel thread is interruptible. This value means that a signal can awaken the current kernel thread. |
FALSE | The current kernel thread is not interruptible. This value means that only the specified event can awaken the current kernel thread. |
The assert_wait_mesg interface asserts that the current kernel thread is about to block (sleep) until the specified event occurs. This interface sets a thread wait bit in the pointer to the thread structure associated with the current kernel thread. This bit signifies that this kernel thread is on the appropriate wait hash queue, waiting for a wakeup call.
To actually block (put to sleep) the current kernel thread, call thread_block.
To issue a wakeup call on the specified event, call the thread_wakeup_prim or clear_wait interface.
You must not call assert_wait_mesg from a device driver's interrupt handler. The reason for this is that at interrupt context there is no process to be put to sleep.
See Writing Device Drivers: Advanced Topics for a code example of the assert_wait_mesg interface.
None
clear_wait, current_thread, thread, thread_block
Converts an ASCII string to an integer
The atoi interface converts the specified ASCII string to an integer. The string can contain decimal integer constants. Specifically, the atoi interface converts the specified ASCII character string up to the first character inconsistent with the format of a decimal integer to an integer data type. Leading white-space characters are ignored.
The atoi interface is a modified version of the atoi C library interface that applications call. The kernel version of atoi is intended for use in those few cases in the kernel where the conversion of an ASCII string to an integer is required (for example, generic boot code).
The atoi interface returns the converted value of an integer if the specified ASCII string is in the expected form. Otherwise, it returns a negative integer.
Gets a pointer to the previous queue
#include <sys/stream.h>
queue_t * backq
(current_queue)
queue_t
*current_queue;
The backq interface returns a pointer to the queue preceding the current queue (the current_queue argument). If the current queue is a read queue, backq returns a pointer to the queue downstream from the current queue, unless the current queue is the stream end. If the current queue is a write queue, backq returns a pointer to the queue upstream from the current queue, unless the current queue is the stream head.
Upon successful completion, the backq interface returns a pointer to the queue preceding the current queue. Otherwise, it returns NULL.
Probes the address during device autoconfiguration
int BADADDR
(addr,
length,
ptr)
caddr_t
addr;
int
length;
struct bus_ctlr_common
*ptr;
The BADADDR interface generates a call to a machine-dependent interface that does a read access check of the data at the supplied address and dismisses any machine check exception that may result from the attempted access. You call this interface to probe for memory or I/O devices at a specified address during device autoconfiguration.
You can use BADADDR in device drivers that are statically configured into the kernel on Digital UNIX. However, you cannot use BADADDR if the driver is dynamically configured into the kernel.
If you implement the driver to be both statically and dynamically configured, you can declare a variable and use it to control the call to BADADDR.
The following code fragment shows the use of such a variable used in the probe interface for the /dev/none driver:
.
.
.
if (none_is_dynamic) { /* Code to handle tasks associated with a dynamically * * configured driver */
.
.
.
} else { /* Code to handle tasks (including the call to BADADDR) * * associated with a statically configured driver */ /* including call to BADADDR */ }
.
.
.
The EISA and ISA buses do not generate a machine check when BADADDR performs a read access to a nonexistent location. These buses always return success when BADADDR performs a read access to their address space.
For the PCI bus and the VMEbus, you must do the following before calling BADADDR:
The BADADDR interface returns the value zero (0) if the data is accessible and nonzero if the data is not accessible.
Tests for flow control in a specified priority band
#include <sys/stream.h>
int bcanput
(message_queue,
pri)
queue_t
*message_queue;
unsigned char
pri;
The bcanput interface, like the canput interface, searches through the stream (starting at the message queue identified by the message_queue argument) until it finds a queue that contains a service interface where the message can be enqueued or until it reaches the end of the stream. If bcanput finds a service interface in a queue, it tests the queue to determine if there is space in the queue to accommodate a message. If the queue is full, bcanput sets the q_flag member of the queue_entry structure pointer (the message queue) to the constant QWANTW to back-enable the caller's service interface.
If the pri argument is zero (0), bcanput calls the canput interface, which performs the task of checking for space in the message queue.
You are responsible for both testing a queue with bcanput and not placing a message on the queue if bcanput fails.
The bcanput interface returns a value of 1 if a message of priority pri can be placed on the message queue, or if the band does not yet exist on the queue. The interface returns a value of zero (0) if the priority band is flow-controlled.
Compares two byte strings
int bcmp
(b1,
b2,
n)
char
*b1;
char
*b2;
int
n;
The bcmp interface compares byte string b1 to byte string b2. The interface compares exactly n bytes. No check is made for null bytes.
If the first n bytes of b1 and b2 are equal, the bcmp interface returns the value zero (0). Otherwise, it returns a nonzero integer.
Copies a series of bytes with a specified limit
void bcopy
(b1,
b2,
n)
char
*b1;
char
*b2;
int
n;
The
bcopy
interface copies
n
bytes from string
b1
to
buffer
b2.
No check is made for null bytes.
The copy is nondestructive, that is, the address ranges of
b1
and
b2
can overlap.
The following code fragment shows a call to bcopy:
.
.
.
struct tc_slot tc_slot[TC_IOSLOTS];
.
.
.
char *cp;
.
.
.
bcopy(tc_slot[index].modulename, cp, TC_ROMNAMLEN + 1);
.
.
.
None
blkclr, copystr, ovbcopy, strcpy, strncpy
Indicates that block I/O is complete
void biodone
(bp)
struct buf
*bp;
The biodone interface is called after an I/O operation to mark the buf structure as completed. It sets the b_flags member to B_SWAP, B_UBC, or B_ASYNC to indicate the type of I/O operation that has completed. Other members may be set as well, depending on the type of I/O operation.
The interface panics if the buf structure is NULL.
None
Zero a block of memory
void blkclr
(b1,
n)
char
*b1;
unsigned int
n;
void bzero
(b1,
n)
char
*b1;
int
n;
The blkclr and bzero interfaces zero n bytes of memory beginning at the address specified by b1.
See Writing Device Drivers: Tutorial for code examples of the blkclr and bzero interfaces.
None
bcopy, copystr, ovbcopy, strcpy, strncpy
Deallocates a buf structure
void brelse
(bp)
register struct buf
*bp;
The brelse interface deallocates a buf structure that was previously allocated by the getnewbuf interface. The buf structure contains information describing an I/O request. Device drivers call the brelse interface after the I/O request has completed to free the memory used by the buf structure.
None
Converts bytes to number of pages
int btop
(virt_addr)
vm_offset_t
virt_addr;
The btop interface converts the specified byte address to the number of pages.
The btop interface returns the number of kernel pages associated with the specified byte address.
Gets a buffer when allocb fails
#include <sys/stream.h>
int bufcall
(size,
pri,
function,
argument)
int
size;
int
pri;
int
(*function) ();
long
argument;
The bufcall interface serves as a timeout call of indeterminate length. When a buffer allocation request fails, you can use bufcall to schedule the interface passed to the function argument to be called with the argument passed to argument when a buffer becomes available. The interface passed to function can be a routine that calls allocb or it can be implemented to perform something else.
Even when the bufcall interface calls the interface passed to the function argument, the allocb interface can still fail if another module or device driver allocated the memory before the interface in function was able to call allocb.
Upon successful completion, the bufcall interface returns a bufcall id that you can use in a call to the unbufcall interface to cancel the request. If the bufcall scheduling fails (that is, a buffer is not available), the interface passed to function is never called and bufcall returns the value zero (0).
Locks the specified I/O buffer
void BUF_LOCK
(bp)
struct buf
*bp;
The BUF_LOCK interface locks the specified I/O buffer. The interface masks all disk and tape controller interrupts (by calling the splbio interface). It sets the mutual exclusion buffer lock member, b_lock, of the specified buf structure pointer.
The BUF_LOCK interface then sets the b_flags member of the specified buf structure pointer to B_BUSY to indicate that this buffer is being used. Finally, BUF_LOCK resets the CPU priority level (by calling the splx interface).
You should make efforts in your device drivers to hold the I/O buffer lock for as short a period of time as possible to allow maximum concurrency. You should also release the I/O buffer lock by calling the BUF_UNLOCK interface before returning from the driver's entry point.
None
Unlocks the specified I/O buffer
void BUF_UNLOCK
(bp)
struct buf
*bp;
The BUF_UNLOCK interface unlocks the specified I/O buffer that was locked in a previous call to BUF_LOCK. The interface masks all disk and tape controller interrupts (by calling the splbio interface). It resets the mutual exclusion buffer lock member, b_lock, of the specified buf structure pointer.
The BUF_UNLOCK interface then resets the b_flags member of the specified buf structure pointer to indicate that this buffer is not being used. Finally, BUF_UNLOCK resets the CPU priority level (by calling the splx interface).
You must have locked the specified I/O buffer by calling BUF_LOCK prior to calling the BUF_UNLOCK interface.
None
Converts a valid bus physical address to an I/O handle base
io_handle_t busphys_to_iohandle
(addr,
flags,
ctlr_p)
u_long
addr;
int
flags;
struct controller
*ctlr_p;
Value | Meaning |
BUS_MEMORY | The address is from the bus memory. In this case, busphys_to_iohandle converts the bus physical address to a sparse memory I/O handle base. |
BUS_IO | The address is from the bus I/O memory. In this case, busphys_to_iohandle converts the bus physical address to a sparse I/O handle base. |
DENSE_MEMORY | The address is from dense space. In this case, busphys_to_iohandle converts the bus physical address to a dense memory base. |
The busphys_to_iohandle interface converts a valid bus physical address to an I/O handle base that a device driver uses to perform I/O copy operations. The form of this address is CPU specific.
The busphys_to_iohandle interface is a generic interface that maps to a bus-specific interface that actually converts the bus physical address to an I/O handle base. Using this interface to convert the bus physical address makes the device driver more portable across different bus architectures.
Upon successful completion, busphys_to_iohandle returns the I/O handle base associated with the bus physical address. If busphys_to_iohandle cannot convert the bus physical address into an I/O handle base, it returns a value of -1.
io_copyin, io_copyio, io_copyout, iohandle_to_phys
Tests for room in a message queue
#include <sys/stream.h>
int canput
(message_queue)
queue_t
*message_queue;
The canput interface searches through the stream (starting at the message queue identified by the message_queue argument) until it finds a queue that contains a service interface where the message can be enqueued or until it reaches the end of the stream. If canput finds a service interface in a queue, it tests the queue to determine if there is space in the queue to accommodate a message. If the queue is full, canput sets the q_flag member of the queue_entry structure pointer (the message queue) to the constant QWANTW to back-enable the caller's service interface.
You are responsible for both testing a queue with canput and not placing a message on the queue if canput fails.
The canput interface returns a value of 1 if the message queue is not full. It returns the value zero (0) if the message queue is full.
Determines the configuration state
int cfgmgr_get_state
(driver_name,
driver_cfg_state)
char
*driver_name;
int
*driver_cfg_state;
Value | Meaning |
SUBSYSTEM_DYNAMICALLY_CONFIGURED | The specified device driver is in the dynamic configuration state. This means the driver was dynamically configured into the kernel. |
SUBSYSTEM_STATICALLY_CONFIGURED | The specified device driver is in the static configuration state. This means the driver was statically configured into the kernel. |
The cfgmgr_get_state interface obtains the configuration state of the specified device driver. The specified device driver is either in the static configuration state or the dynamic configuration state. The cfgmgr_get_state interface returns the state value in the driver_cfg_state argument. Driver writers should store this state value in an xx_is_dynamic variable or some similarly named variable.
You typically call the cfgmgr_get_state interface in the CFG_OP_CONFIGURE entry point of the device driver's configure interface.
See Writing Device Drivers: Tutorial for a code example of the cfgmgr_get_state interface.
Upon successful completion, cfgmgr_get_state returns the value ESUCCESS. This success value indicates that cfgmgr_get_state returned the configuration state of the specified device driver in the driver_cfg_state argument. Otherwise, cfgmgr_get_state returns one of the following error constants defined in /usr/sys/include/sys/sysconfig.h and /usr/sys/include/sys/errno.h:
Reports failure to the cfgmgr framework
int cfgmgr_set_status
(driver_name)
char
*driver_name;
The cfgmgr_set_status interface reports to the cfgmgr framework that a failure has occurred during static configuration. If the specified device driver is in the static configuration state, it does not know that the configuration operation is complete until all register callback requests (interfaces) have successfully completed. Therefore, a device driver calls cfgmgr_set_status to report a possible failure during static configuration to the cfgmgr framework.
Specifically, cfgmgr_set_status performs the following failure operations:
The cfgmgr_set_status interface calls the device driver's configure interface at its CFG_OP_UNCONFIGURE entry point as part of these failure operations. The code associated with the CFG_OP_UNCONFIGURE entry point is responsible for determining how to deallocate any allocated resources during these failure operations.
The cfgmgr_set_status interface does not notify the operator of a configuration failure. Part of the code associated with the CFG_OP_UNCONFIGURE entry point could include an error logging operation to record the fact that a failure has occurred. The reason for doing this is that the cfgmgr framework's task is to accomplish the unconfigure operation of a statically configured device driver.
You call the cfgmgr_set_status interface in the device driver's callback interface when the static configuration operation fails. You register a callback interface by calling the register_callback interface.
See Writing Device Drivers: Tutorial for a code example of the cfgmgr_set_status interface.
Upon successful completion, cfgmgr_set_status returns the value ESUCCESS. This success value indicates that cfgmgr_set_status adjusted the state of the device driver and caused the cfgmgr framework to unconfigure the driver. Otherwise, cfgmgr_set_status returns one of the following error constants defined in /usr/sys/include/sys/sysconfig.h and /usr/sys/include/sys/errno.h:
cfgmgr_get_state, register_callback
Clears the wait condition
void clear_wait
(thread,
result,
interrupt_only)
thread_t
thread;
int
result;
boolean_t
interrupt_only;
Value | Meaning |
THREAD_AWAKENED | This is a normal wakeup. |
THREAD_TIMED_OUT | The timeout period expired. |
THREAD_INTERRUPTED | The clear_wait interface interrupted the wakeup. |
Value | Meaning |
TRUE | Clears the wait condition only if the kernel thread is waiting in an interruptible state. |
FALSE | Clears the wait condition under any circumstances. |
The clear_wait interface clears the wait condition for the specified kernel thread and starts executing the kernel thread, if appropriate. If the kernel thread is interruptible and is still waiting for the event, clear_wait sets the kernel thread state to TH_RUN and places it on the run queue.
None
assert_wait_mesg, mpsleep, thread_block, thread_wakeup, thread_wakeup_one
Configures a device driver that is in the dynamic state
int configure_driver
(driver_name,
bus_num,
bus_name,
driver_struct)
char
*driver_name;
int
bus_num;
char
*bus_name;
struct driver
*driver_struct;
The configure_driver interface configures a device driver into the system (hardware) configuration tree. The configuration tasks consist of incorporating the bus, controller, and device structures associated with the specific device driver into the system (hardware) configuration tree.
See Writing Device Drivers: Tutorial for a code example of the configure_driver interface.
The configure_driver interface can return the following value defined in /usr/sys/include/io/common/devdriver_loadable.h:
The configure_driver interface can also return one of the following status values defined in /usr/sys/include/sys/errno.h. This value is actually returned from the bus-specific controller_configure interface:
Frees a block of memory previously allocated by contig_malloc
#include <sys/malloc.h>
void contig_free
(addr,
size,
type)
void
*addr;
u_long
size;
int
type;
The contig_free interface frees the memory allocated in a previous call to contig_malloc. If the memory came from the saved memory pool, contig_free puts it back in the saved memory pool; otherwise, the interface frees the memory to the virtual memory subsystem.
None
Allocates physically contiguous memory
#include <sys/malloc.h>
void * contig_malloc
(size,
alignment,
addrlimit,
type,
flag)
u_long
size;
u_long
alignment;
u_long
addrlimit;
int
type;
int
flag;
Value | Meaning |
M_ZERO | Signifies that contig_malloc should zero the allocated memory. |
M_WAITOK | Signifies that contig_malloc can block. |
M_NOWAIT | Signifies that contig_malloc cannot block. |
The contig_malloc interface allocates physically contiguous memory during the boot process (before single-user mode). The interface carves out an area of physically contiguous memory from a contiguous memory buffer and allocates memory from this buffer with proper alignment. The call to contig_malloc is the same for statically or dynamically configured drivers. However, the point or points in time in which the statically or dynamically configured driver requests the memory differ.
A statically configured driver typically needs to call contig_malloc only before single-user mode. In this case, contig_malloc obtains the memory from the contiguous memory buffer. When a statically configured driver frees this physically contiguous memory (by calling the contig_free interface), the memory is returned to the virtual memory subsystem.
A dynamically configured driver typically needs physically contiguous memory after single-user mode. As stated previously, contig_malloc carves out an area of physically contiguous memory from a contiguous memory buffer before single-user mode. Thus, this memory would not be available to the dynamically configured driver after single-user mode. To solve this problem, a dynamically configured driver calls contig_malloc by defining the CMA_Option attribute in the sysconfigtab file fragment.
The cma_dd subsystem that Digital provides calls contig_malloc on behalf of dynamically configured device drivers and obtains the memory allocation size (and other information) from the CMA_Option attribute field. In this case, contig_malloc allocates physically contiguous memory from the contiguous memory buffer and places it in a saved memory pool. When a dynamically configured driver needs to call contig_malloc after single-user mode, the physically contiguous memory comes from this saved memory pool. When a dynamically configured driver frees this physically contiguous memory (by calling the contig_free interface), the memory is returned to the saved memory pool (not the virtual memory subsystem). Thus, this physically contiguous memory is available to the dynamically configured driver upon subsequent reload requests that occur after single-user mode.
Upon successful completion, contig_malloc returns a pointer to the allocated memory. If contig_malloc cannot allocate the requested memory, it returns a null pointer.
Copies a message block
#include <sys/stream.h>
MBLKP copyb
(message_block)
MBLKP
message_block;
The copyb interface allocates a new message block and copies into it the data from the block pointed to by the message block (the message_block argument). The new message block is at least as large as the message block being copied. The copyb interface uses the b_rptr and b_wptr members of the msgb structure pointer to determine how many bytes to copy.
Upon successful completion, the copyb interface returns a pointer to the newly allocated message block that contains the copied data. This newly allocated message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
Otherwise, copyb returns a NULL pointer.
Copies data from a user address space to a kernel address space
int copyin
(user_src,
kernel_dest,
bcount)
caddr_t
user_src;
caddr_t
kernel_dest;
u_int
bcount;
The copyin interface copies a specified amount of data from the unprotected user address space to the protected kernel address space.
See Writing Device Drivers: Tutorial for a code example of the copyin interface.
Upon successful completion, copyin returns a value of zero (0). Otherwise, it can return the following error:
Copies a null-terminated string from a user address space to a kernel address space
int copyinstr
(user_src,
kernel_dest,
maxlength,
lencopied)
char
*user_src;
char
*kernel_dest;
int
maxlength;
int
*lencopied;
The copyinstr interface copies a specified null-terminated string from the unprotected user address space to a specified address in the protected kernel address space.
If the string being copied is not null terminated, copyinstr copies maxlength bytes into the kernel address space.
Upon successful completion, copyinstr returns the value zero (0) and the actual length of the string copied to the lencopied argument. Otherwise, it returns one of the following error constants defined in /usr/sys/include/sys/errno.h:
Copies a message to a new message
#include <sys/stream.h>
MBLKP copymsg
(message_block)
MBLKP
message_block;
The copymsg interface:
Upon successful completion, the copymsg interface returns a pointer to the newly allocated message block that contains the copied data. This newly allocated message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
Otherwise, copymsg returns a NULL pointer.
Copies data from a kernel address space to a user address space
int copyout
(kernel_src,
user_dest,
bcount)
caddr_t
kernel_src;
caddr_t
user_dest;
u_int
bcount;
The copyout interface copies a specified amount of data from the protected kernel address space to the unprotected user address space.
See Writing Device Drivers: Tutorial for a code example of the copyout interface.
Upon successful completion, copyout returns the value zero (0). Otherwise, it returns the following error:
Copies a null-terminated string from a kernel address space to a user address space
int copyoutstr
(kernel_src,
user_dest,
maxlength,
lencopied)
char
*kernel_src;
char
*user_dest;
int
maxlength;
int
*lencopied;
The copyoutstr interface copies a specified null-terminated string from the protected kernel address space to the unprotected user address space.
If the string being copied is not null-terminated,
copyoutstr
copies
maxlength
bytes into the user address space.
Upon successful completion, copyoutstr returns the value zero (0) and the actual length of the string copied in lencopied. Otherwise, it can return the following errors:
Copies a null-terminated character string with a specified limit
int copystr
(s1,
s2,
maxlength,
ncopiedaddr)
char
*s1;
char
*s2;
u_int
maxlength;
u_int
*ncopiedaddr;
The copystr interface copies string s1 to the buffer pointed to by s2. The interface stops after copying a null character or after copying maxlength characters, whichever comes first. The s2 buffer is not padded with null characters to maxlength.
The copystr interface returns the number of characters copied in the location pointed to by ncopiedaddr.
Note that the character size is 1 byte.
Upon successful completion, copystr returns the value zero (0). Otherwise, it can return the following error:
bcopy, blkclr, ovbcopy, strcpy, strncpy
Copies data from a virtual address to a physical address
void copy_to_phys
(virt_src,
phys_dest,
bcount)
vm_offset_t
virt_src;
vm_offset_t
phys_dest;
unsigned int
bcount;
The copy_to_phys interface copies a specified amount of virtually addressed memory to physically addressed memory. The addresses reside only in system memory space and not in the memory space on I/O buses.
If there is any overlap between virt_src and phys_dest, the copy_to_phys interface panics.
None
copyin, copyout, io_copyin, io_copyio, io_copyout
Creates a controller structure for the system (hardware) configuration tree
int create_controller_struct
(ctlr_cnfg)
struct controller_config
*ctlr_cnfg;
The create_controller_struct interface creates a controller structure for each instance of a controller that the device driver supports. The interface integrates the controller structure for a specific controller into the list of controller structures that reside in the system configuration tree.
Typically, you supply the information needed by the create_controller_struct interface by passing to it the address of the filled-in controller_config structure. However, if any of this information needs to be configurable (that is, you want to give a system manager the ability to dynamically change this information), you must:
See Writing Device Drivers: Tutorial for a code example of the create_controller_struct interface.
Upon successful completion, create_controller_struct returns ESUCCESS. On failure, it returns EINVAL.
controller_config, create_device_struct
Creates a device structure for the system (hardware) configuration tree
int create_device_struct
(dev_cnfg)
struct device_config
*dev_cnfg;
The create_device_struct interface creates a device structure for each instance of a device that the device driver supports. The interface integrates the device structure for a specific device into the list of device structures that reside in the system configuration tree.
Typically, you supply the information needed by the create_device_struct interface by passing to it the address of the filled-in device_config structure. However, if any of this information needs to be configurable (that is, you want to give a system manager the ability to dynamically change this information), you must:
See Writing Device Drivers: Tutorial for a code example of the create_device_struct interface.
Upon successful completion, create_device_struct returns ESUCCESS. On failure, it returns EINVAL.
create_controller_struct, device_config
Returns a pointer to the task structure associated with the currently running kernel thread
None
The current_task interface returns a pointer to the task structure associated with the currently running kernel thread. A device driver typically calls this interface in preparation for doing a DMA operation to a user's buffer. The device driver uses the pointer returned by current_task in the call to the vm_map_pageable interface.
You must not call the current_task interface from a driver's interrupt handler.
The current_task interface returns a pointer to the task structure for the currently running kernel thread.
round_page, thread, trunc_page, vm_map_pageable
Is a pointer to the currently running kernel thread
None
The current_thread interface (macro) is a pointer to the currently running kernel thread. Typically, device drivers use this interface to reference the wait_result member of the thread structure pointer associated with the currently running kernel thread. A device driver calls current_thread after calls to assert_wait_mesg and thread_block. If the device driver needs to set a timeout, then it calls current_thread after calls to assert_wait_mesg, thread_set_timeout, and thread_block.
The kernel can set the wait_result member to one of the following values:
Value | Meaning |
THREAD_AWAKENED | The result of the assert wait is a normal wakeup. |
THREAD_TIMED_OUT | The specified timeout has expired. |
THREAD_INTERRUPTED | The wait condition was interrupted by the clear_wait interface. |
THREAD_SHOULD_TERMINATE | The result of the assert wait is that the current kernel thread should terminate. |
THREAD_RESTART | The current kernel thread should be restarted. |
See Writing Device Drivers: Advanced Topics for a code example of the current_thread interface.
None
assert_wait_mesg, clear_wait, thread, thread_block, thread_set_timeout
Tests whether a message is a data message
#include <sys/stream.h>
int datamsg
(type)
unsigned char
type;
mp->b_datap->db_type
The datamsg interface tests the type of message to determine if it is a data message type.
The following list identifies the data message value types:
Value | Meaning |
M_DATA | Ordinary data |
M_DELAY | Requests a real-time delay |
M_PROTO | Internal control information and data |
M_PCPROTO | Same meaning as the M_PROTO except for priority |
If the message is a data message, datamsg returns the value 1. If the message is not a data message, datamsg returns the value zero (0).
Declares a simple lock structure
#include <kern/lock.h>
void decl_simple_lock_data
(class,
name)
char
class;
char
name;
The decl_simple_lock_data interface declares a simple lock structure, slock, of the specified name. You declare a simple lock structure for the purpose of protecting device driver data structures and device register access. You use decl_simple_lock_data to declare a simple lock structure and then pass it to the following simple lock-specific interfaces: simple_lock_init, simple_lock, simple_lock_try, simple_unlock, and simple_lock_terminate.
See Writing Device Drivers: Advanced Topics for a code example of the decl_simple_lock_data interface.
None
lock.h, simple_lock, simple_lock_init, simple_lock_try, simple_unlock, slock
Delays the calling interface a specified number of microseconds
The DELAY interface delays the calling interface a specified number of microseconds. DELAY spins, waiting for the specified number of microseconds to pass before continuing execution. For example, the following code results in a 10000-microsecond (10-millisecond) delay:
. . . DELAY(10000); . . .
The range of delays is system dependent, due to its relation to the granularity of the system clock. The system defines the number of clock ticks per second in the hz variable. Specifying any value smaller than 1/hz to the DELAY interface results in an unpredictable delay. For any delay value, the actual delay may vary by plus or minus one clock tick.
Using the DELAY interface is discouraged because the processor will be consumed for the specified time interval and therefore is unavailable to service other processes. In cases where device drivers need timing mechanisms, you should use the sleep and timeout interfaces instead of the DELAY interface. The most common usage of the DELAY interface is in the system boot path. Using DELAY in the boot path is often acceptable because there are no other processes in contention for the processor.
None
Registers driver entry points and reserves a major number
#include <sys/conf.h>
int devsw_add
(driver_name,
driver_reg_instance,
major_number,
dsent_ptr)
char
*driver_name;
int
driver_reg_instance;
int
major_number;
struct dsent
*dsent_ptr;
This name is a string that matches the string you specified for the entry_name item in the /etc/sysconfigtab database. Typically, third-party driver writers specify the driver name (followed by a colon) in the sysconfigtab file fragment, which gets appended to the /etc/sysconfigtab database during the driver product installation.
The devsw_add interface registers a device driver's I/O services interfaces in and reserves a major number from a free slot in the dsent table. Digital UNIX reserves this major number across boots. The devsw_add interface performs appropriate checks on the values passed to the driver_name, major_number, and dsent_ptr arguments.
You can request that the devsw_add interface reserve a specific major number from a slot in the dsent table by passing a specific number to the major_number argument. If you pass a specific major number, the devsw_add interface determines if a major number has already been assigned to the device driver. If this major number was already assigned to the device driver, the devsw_add interface simply registers the driver's I/O services interfaces in the dsent table and returns the major number as the reserved number. Passing the value -1 to the major_number argument causes the devsw_add interface to reserve a previously reserved major number or the next available major number from a slot in the dsent table.
If the major number you request or if the major number that was previously reserved is too large for the current size of the device switch table, devsw_add returns an error value of -1.
You can make subsequent calls to devsw_add to modify the device switch entry in the device switch table.
The dsent data structure contains entry points for the block and character I/O services interfaces associated with a specific device driver. This data structure also contains other information such as whether the driver is implemented as a symmetric multiprocessing (SMP) driver.
The devsw_add interface is provided for registering these block and character I/O services interfaces and reserving an associated major number.
A device driver writer registers the driver's I/O services interfaces by declaring an instance of the dsent structure in the driver and setting the appropriate members to the names of the driver's I/O services interfaces. You must specify an interface name for each member of this structure. If the driver does not implement a specific interface, there are typically two interfaces you can specify:
You specify nodev when it is an error for the interface to be called. To indicate the error, nodev returns ENODEV.
You specify nulldev when it is not an error for the interface to be called, but the driver has no need to implement the interface. The nulldev interface returns the value zero (0) and performs no tasks. This value indicates a success status.
See Writing Device Drivers: Tutorial for a code example of the devsw_add interface.
Upon successful completion, the devsw_add interface returns the reserved major number. Otherwise, on an error, the devsw_add interface returns the value -1. The following list describes possible error conditions:
Clears entry points from the device switch table
#include <sys/conf.h>
int devsw_del
(driver_name,
driver_reg_instance)
char
*driver_name;
int
driver_reg_instance;
The devsw_del interface clears a device driver's I/O services interfaces (and other information) from the device switch table. The interface continues to reserve the major number associated with the device driver.
The devsw_del interface performs appropriate checks on the value passed to the driver_name argument.
A device driver calls devsw_del to remove access to its associated I/O services interfaces but retain use of its reserved major number. For example, a device driver about to be unloaded might want to call devsw_del.
See Writing Device Drivers: Tutorial for a code example of the devsw_del interface.
Upon successful completion, the devsw_del interface returns the major number associated with the device driver whose I/O services interfaces were cleared from the device switch table. Otherwise, on an error, the devsw_del interface returns the value -1. The following list describes possible error conditions:
Obtains a previously reserved major number
#include <sys/conf.h>
int devsw_get
(driver_name,
driver_reg_instance)
char
*driver_name;
int
driver_reg_instance;
The devsw_get interface returns the major number associated with the specified device driver.
The devsw_get interface performs appropriate checks on the value passed to the driver_name argument.
Upon successful completion, the devsw_get interface returns the major number associated with the specified device driver. Otherwise, on an error, the devsw_get interface returns the value -1. The following list describes possible error conditions:
Returns a pointer to the current sg_entry
#include <io/common/devdriver.h>
sg_entry_t dma_get_curr_sgentry
(dma_handle)
dma_handle_t
dma_handle;
The dma_get_curr_sgentry interface returns a pointer to an sg_entry data structure. A device driver can use this pointer to retrieve the current bus address/byte count pair for the mapping of a block of an in-memory I/O buffer onto the controller's I/O bus. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer.
Specifically, dma_get_curr_sgentry returns a pointer to a bus address/byte count pair from the in-kernel DMA mapping data structures. Unlike dma_get_next_sgentry, a call to dma_get_curr_sgentry does not result in incrementing an internal index variable. Thus, subsequent calls to dma_get_curr_sgentry result in a return of the same sg_entry pointer and the same bus address/byte count pair.
Use of the dma_get_curr_sgentry interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_get_curr_sgentry interface.
Upon successful completion, dma_get_curr_sgentry returns a pointer to the sg_entry structure associated with a mapped region of an I/O buffer on the controller's I/O bus. This sg_entry pointer is associated with the current bus address/byte count pair for this DMA handle. Otherwise, dma_get_curr_sgentry returns the value zero (0) to indicate it reached the end of bus address/byte count pairs for this DMA handle.
dma_get_next_sgentry, sg_entry
Returns a pointer to the next sg_entry
#include <io/common/devdriver.h>
sg_entry_t dma_get_next_sgentry
(dma_handle)
dma_handle_t
dma_handle;
The dma_get_next_sgentry interface returns a pointer to an sg_entry data structure. A device driver can use this pointer to retrieve the next valid bus address/byte count pair for the mapping of a block of an in-memory I/O buffer onto the controller's I/O bus. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer.
Specifically, dma_get_next_sgentry returns a pointer to a bus address/byte count pair from the in-kernel DMA mapping data structures. After each call to dma_get_next_sgentry, the interface increments an internal index variable. This contrasts with the dma_get_curr_sgentry interface, which does not perform an increment operation. This increment operation ensures that the next call to dma_get_next_sgentry gets the next sg_entry pointer and thus the next bus address/byte count pair for this DMA handle.
Use of the dma_get_next_sgentry interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_get_next_sgentry interface.
Upon successful completion, dma_get_next_sgentry returns a pointer to the sg_entry structure associated with a mapped region of an I/O buffer on the controller's I/O bus. This sg_entry pointer is associated with the next bus address/byte count pair for this DMA handle. Otherwise, dma_get_next_sgentry returns the value zero (0) to indicate it reached the end of bus address/byte count pairs for this DMA handle.
dma_get_curr_sgentry, sg_entry
Gets a data element from the DMA private storage space
#include <io/common/devdriver.h>
int dma_get_private
(dma_handle,
index,
data)
dma_handle_t
dma_handle;
int
index;
u_long
*data;
The dma_get_private interface retrieves a 64-bit data element from the DMA private storage area of the internal kernel data structure associated with the specified DMA handle. Device drivers can put a 64-bit data element in the DMA private storage area of this internal kernel data structure by calling dma_put_private. Currently, the internal kernel data structure provides only one member to store a 64-bit data element.
DMA engines that make multiple calls to dma_map_alloc and dma_map_load can use the dma_get_private and dma_put_private pair of interfaces. Each call to dma_map_alloc and dma_map_load returns the address of the allocated DMA handle. The device driver calls dma_put_private to store each returned address as the 64-bit data element in each internal kernel data structure's DMA private storage area. By storing each address as the 64-bit data element in each internal kernel data structure's DMA private storage area, the device driver creates a linked list of DMA handles that it can pass to some lower software layer. This lower software layer then calls dma_get_private to retrieve the resources contained in the linked list of DMA handles.
Use of the dma_get_private interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
Upon successful completion, dma_get_private returns the value zero (0). If the index to the private storage area is greater than zero (0), dma_get_private returns the value 1, indicating a failure status.
Returns a kernel segment (kseg) address of a DMA buffer
#include <io/common/devdriver.h>
vm_offset_t dma_kmap_buffer
(dma_handle,
offset)
dma_handle_t
dma_handle;
u_long
offset;
The dma_kmap_buffer interface takes an offset variable and returns a kseg address. The device driver can use this kseg address to copy and save the data at the offset in the buffer. A device driver calls dma_kmap_buffer when the following occurs:
For example, a SCSI device performs a disconnect in the second byte of a longword on a TURBOchannel-based SCSI subsystem. The device driver uses the kseg address to read the valid data transferred so far, saves it in a dma_min_boundary sized buffer, and merges it back into the DMA buffer once the rest of the transfer has completed.
See Writing Device Drivers: Tutorial for a code example of the dma_kmap_buffer interface.
Upon successful completion, dma_kmap_buffer returns a kseg address of the byte offset pointed to by the addition of the following two values:
virt_addr + offset
where:
The dma_kmap_buffer interface returns the value zero (0) to indicate failure to retrieve the kseg address.
dma_map_load, dma_min_boundary, sg_entry
Allocates resources for DMA data transfers
#include <io/common/devdriver.h>
u_long dma_map_alloc
(byte_count,
ctlr_p,
dma_handle_p,
flags)
u_long
byte_count;
struct controller
*ctlr_p;
dma_handle_t
*dma_handle_p;
int
flags;
Typically, the device driver passes an argument of type dma_handle_t *. The dma_map_alloc interface returns to this variable the address of the DMA handle. The device driver uses this address in a call to dma_map_load.
Value | Meaning |
DMA_GUARD_UPPER | Allocates additional resources so that contiguous data overruns are captured by the system map error functions. This bit is probably most useful during device driver development and debugging. |
DMA_GUARD_LOWER | Allocates additional resources so that contiguous data underruns are captured by the system map error functions. This bit is probably most useful during device driver development and debugging. |
DMA_SLEEP | Puts the process to sleep if the system cannot allocate the necessary resources to perform a data transfer of size byte_count at the time the driver calls the interface. |
DMA_IN | Sets up a DMA write into main core memory. |
DMA_OUT | Sets up a DMA read from main core memory. |
DMA_ALL | Returns a nonzero value, only if the system can satisfy a DMA transfer of size byte_count. |
DMA_CONTIG | Signifies a request to the DMA mapping interface to provide a single sg_entry structure mapping of a buffer to which DMA access will be made (on an I/O bus by a DMA engine). |
The dma_map_alloc interface allocates the resources (mapping registers, I/O channels, and other hardware and software resources) for DMA data transfers. You specify the size of the DMA data transfer in the byte_count argument.
The dma_map_alloc interface returns to the dma_handle_p argument a handle to DMA resources associated with the mapping of an in-memory I/O buffer onto a controller's I/O bus. Device driver writers can view the DMA handle as the tag to the allocated system resources needed to perform a DMA operation.
The
dma_map_alloc
interface allocates only the necessary resources for a device driver to
perform a maximum transfer of size
byte_count.
However, the maximum transfer size is the size of the returned
byte count
if the returned byte count is not equal to
byte_count.
All drivers must be prepared for a returned byte count that is less than
byte_count.
The reason for this is that system resources can have physical limits
that may never satisfy an allocation request of size
byte_count.
To actually initialize and set up the resources,
the driver must make a call to
dma_map_load.
The dma_map_alloc interface does not put the process to sleep and returns the value zero (0) if both the following are true:
The DMA_CONTIG flag is a request for contiguous memory space on an I/O bus for a virtually mapped buffer in system memory space that may be physically discontiguous. The call to the dma_map_alloc or dma_map_load interface with the DMA_CONTIG flag will not fail if a contiguous I/O address space cannot be used to map the memory buffer, for example, if more than one sg_entry structure is returned. The device driver can determine if the DMA_CONTIG satisfied the request by comparing the byte count value of the bc member in the first returned sg_entry structure to the requested byte count in the byte_count argument of the dma_map_alloc or dma_map_load interface.
This flag is useful for I/O devices whose DMA typically crosses one or more (8 Kbyte) pages. This is because system hardware scatter-gather resources can be set up and used to do scatter-gather mapping of a virtually contiguous, physically discontiguous I/O buffer during the calls to dma_map_alloc or dma_map_load. This DMA mapping makes a physically discontiguous memory buffer appear physically contiguous to an I/O device on an I/O bus.
Even if an I/O device's DMA engine has scatter-gather resources or support, DMA is typically faster if the system scatter-gather resources are used. This is due to the system's lower overhead to set up scatter-gather resources relative to an I/O device reading and processing multiple scatter-gather data structures.
Use of the dma_map_alloc interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_map_alloc interface.
Upon successful completion, dma_map_alloc returns a byte count (in bytes) that indicates the DMA transfer size it can map. It returns the value zero (0) to indicate a failure.
If the device driver sets flags to DMA_ALL, then dma_map_alloc returns a nonzero value only if the system can satisfy a transfer size of byte_count. This means that if the system cannot support a transfer of size byte_count (even if all DMA resources were made available), dma_map_alloc refuses to allocate any portion of the resources associated with the specified byte_count and returns a byte count of zero (0). This behavior no allocation of resources unless dma_map_alloc can allocate the resources needed to do an uninterruptible transfer of the requested size avoids extra calls to dma_map_dealloc.
If the returned byte count equals byte_count, then dma_map_alloc has allocated all of the resources necessary to allow the DMA transfer, without additional system resource allocation.
If the returned byte count does not equal byte_count, the device driver can perform one of the following tasks:
A device driver can partition the DMA data transfer into a byte_count that is less than or equal to the returned byte count and then perform a sequence of DMA data transfer operations until the transfer has completed.
If the device driver needs more resources associated with the specified byte_count than dma_map_alloc can allocate, the driver calls dma_map_dealloc to release and deallocate these resources. The driver then recalls dma_map_alloc (possibly with the DMA_SLEEP flag set) until the necessary resources are available.
A system can have the necessary resources to perform a DMA transfer of size byte_count. However, the resources might not be available at the time of the driver's call to dma_map_alloc. In this case, you set the DMA_SLEEP bit in the flags argument. This causes dma_map_alloc to block (sleep) until all the resources necessary to perform a DMA transfer of size byte_count are available to be allocated.
controller, dma_map_dealloc, dma_map_load, sg_entry
Releases and deallocates the DMA resources previously allocated for DMA data transfers
#include <io/common/devdriver.h>
int dma_map_dealloc
(dma_handle)
dma_handle_t
dma_handle;
The dma_map_dealloc interface releases and deallocates the resources for DMA data transfers that were previously allocated in a call to dma_map_alloc or dma_map_load.
Use of the dma_map_dealloc interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_map_dealloc interface.
Upon successful completion, dma_map_dealloc returns the value 1. Otherwise, it returns the value zero (0).
dma_map_alloc, dma_map_load, dma_map_unload, sg_entry
Loads and sets allocated DMA resources and sets up a DMA data path for DMA data transfers
#include <io/common/devdriver.h>
u_long dma_map_load
(byte_count,
virt_addr,
proc_p,
ctlr_p,
dma_handle_p,
max_byte_count,
flags)
u_long
byte_count;
vm_offset_t
virt_addr;
struct proc
*proc_p;
struct controller
*ctlr_p;
dma_handle_t
*dma_handle_p;
u_long
max_byte_count;
int
flags;
Typically, the device driver passes an argument of type dma_handle_t *. If the device driver called dma_map_alloc prior to calling dma_map_load, then dma_map_alloc returns to this argument the address of the allocated DMA handle. The dma_map_load interface can then use this handle to load the appropriate DMA mapping resources. If the device driver did not call dma_map_alloc prior to calling dma_map_load, then you must set the dma_handle_p argument to the value zero (0). Upon completing execution, dma_map_load returns a valid DMA handle to dma_handle_p.
.po -0.5i | |
Value | Meaning |
DMA_GUARD_UPPER | Allocates additional resources so that contiguous data overruns are captured by the system map error functions. This bit is probably most useful during device driver development and debugging. |
DMA_GUARD_LOWER | Allocates additional resources so that contiguous data underruns are captured by the system map error functions. This bit is probably most useful during device driver development and debugging. |
DMA_SLEEP | Puts the process to sleep if the system cannot allocate the necessary resources to perform a data transfer of size byte_count at the time the driver calls the interface. |
DMA_IN | Sets up a DMA write into main core memory. |
DMA_OUT | Sets up a DMA read from main core memory. |
DMA_ALL | Returns a nonzero value, only if the system can satisfy a DMA transfer of size byte_count. |
DMA_CONTIG | Signifies a request to the DMA mapping interface to provide a single sg_entry structure mapping of a buffer to which DMA access will be made (on an I/O bus by a DMA engine). |
The dma_map_load interface loads and sets the system resources necessary to perform a DMA transfer of size byte_count to the virtual address specified in the virt_addr argument. This virtual address must be valid in the context of the process's proc structure, specified in the proc_p argument.
If the device driver calls dma_map_alloc prior to calling dma_map_load, then dma_map_alloc returns to dma_handle_p the address of the allocated DMA handle. The dma_map_load interface uses this handle to load and set the appropriate DMA mapping resources. If the device driver did not call dma_map_alloc prior to calling dma_map_load, then you must set dma_handle_p to the value zero (0). In this case, dma_map_load allocates the appropriate DMA mapping resources (just as if the allocation were done in a previous call to the dma_map_alloc interface) and loads and sets the resources as necessary.
The DMA_CONTIG flag is a request for contiguous memory space on an I/O bus for a virtually mapped buffer in system memory space that may be physically discontiguous. The call to the dma_map_alloc or dma_map_load interface with the DMA_CONTIG flag will not fail if a contiguous I/O address space cannot be used to map the memory buffer, for example, if more than one sg_entry structure is returned. The device driver can determine if the DMA_CONTIG satisfied the request by comparing the byte count value of the bc member in the first returned sg_entry structure to the requested byte count in the byte_count argument of the dma_map_alloc or dma_map_load interface.
This flag is useful for I/O devices whose DMA typically crosses one or more (8 Kbyte) pages. This is because system hardware scatter-gather resources can be set up and used to do scatter-gather mapping of a virtually contiguous, physically discontiguous I/O buffer during the calls to dma_map_alloc or dma_map_load. This DMA mapping makes a physically discontiguous memory buffer appear physically contiguous to an I/O device on an I/O bus.
Even if an I/O device's DMA engine has scatter-gather resources or support, DMA is typically faster if the system scatter-gather resources are used. This is due to the system's lower overhead to set up scatter-gather resources relative to an I/O device reading and processing multiple scatter-gather data structures.
Use of the dma_map_load interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_map_load interface.
Upon successful completion, dma_map_load returns a byte count (in bytes) that indicates the DMA transfer size it can support. It returns the value zero (0) to indicate a failure.
If the device driver sets flags to DMA_ALL, then dma_map_load returns a nonzero value only if the system can satisfy a transfer size of byte_count. This means that if the system cannot support a transfer of size byte_count (even if all DMA resources were made available), dma_map_load refuses to allocate any portion of the resources associated with the specified byte_count and returns a byte count of zero (0). This behavior no allocation of resources unless dma_map_load can allocate the resources needed to do an uninterruptible transfer of the requested size avoids extra calls to dma_map_dealloc.
If the returned byte count equals byte_count, then dma_map_load has allocated, loaded, and set all of the resources necessary to allow the DMA transfer, without additional system resource allocation.
If the returned byte count does not equal byte_count, the device driver can perform one of the following tasks:
If the device driver needs more resources associated with the specified byte_count than dma_map_load can allocate, the driver calls dma_map_unload, dma_map_dealloc, or both to unload, release, and deallocate these resources. The driver then recalls dma_map_alloc (possibly with the DMA_SLEEP flag set) until the necessary resources are available.
The device driver can use the resources already allocated, loaded, and set to perform the DMA data transfers. This data was indicated as mapped by dma_map_alloc in the dma_handle_p argument.
A system can have the necessary resources to perform a DMA transfer of size byte_count. However, the resources might not be available at the time of the driver's call to dma_map_load. In this case, you set the DMA_SLEEP bit in the flags argument. This causes dma_map_load to block (sleep) until all the resources necessary to perform a DMA transfer of size byte_count are available to be allocated, loaded, and set.
dma_map_alloc, dma_map_dealloc, dma_map_unload, sg_entry
Unloads the system DMA resources
#include <io/common/devdriver.h>
int dma_map_unload
(flags,
dma_handle)
int
flags;
dma_handle_t
dma_handle;
This bit setting is analogous to setting the dma_handle_p argument to the value zero (0) for dma_map_load to allocate the DMA mapping resources.
The dma_map_unload interface unloads (invalidates) the resources that were loaded and set up in a previous call to dma_map_load. A call to dma_map_unload does not release or deallocate the resources that were allocated in a previous call to dma_map_alloc unless the driver sets the flags argument to the DMA_DEALLOC bit.
Use of the dma_map_unload interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
See Writing Device Drivers: Tutorial for a code example of the dma_map_unload interface.
Upon successful completion, dma_map_unload returns the value 1. Otherwise, it returns the value zero (0).
dma_map_alloc, dma_map_dealloc, dma_map_load, sg_entry
Returns system-level information
#include <io/common/devdriver.h>
int dma_min_boundary
(ctlr_p)
struct controller
*ctlr_p;
The dma_min_boundary interface returns an integer value that provides the information necessary for a device driver to determine the smallest DMA transfer that can be done atomically on a CPU or bus. The value returned by dma_min_boundary depends on the type of CPU or bus. For example, on a DEC 3000 Model 500 AXP Workstation connected to a TURBOchannel bus, this interface returns a value of 4. This indicates that a TURBOchannel DMA operation is atomic at the longword level (not the byte or word level). On a DEC 2000 Model 300 connected to an EISA bus, for example, this interface returns a value of 1 to indicate byte atomicity. Subbyte atomicity is not supported.
On a DEC 3000 Model 500 AXP Workstation connected to a TURBOchannel bus, this return value of 4 informs a device driver about how to handle a DMA transfer that involves a buffer that begins and/or ends within a nonlongword aligned, nonintegral longword-sized (DMA) transfer. The device driver handles this DMA transfer by redirecting the beginning and/or end of the DMA transfer, respectively, to other buffers that must be merged to the main (aligned longword block) buffer through calls to bcopy.
The dma_min_boundary interface returns the value zero (0) to indicate that the system initialization code did not set up this field. Otherwise, dma_min_boundary returns the byte-size atomicity value that the system supports for DMA operations for all controllers on the respective bus.
Puts a new bus address/byte count pair in the linked list of sg_entry structures
#include <io/common/devdriver.h>
int dma_put_curr_sgentry
(dma_handle,
sg_entryp)
dma_handle_t
dma_handle;
sg_entry_t
sg_entryp;
The dma_put_curr_sgentry interface puts new bus address/byte count values into the ba and bc members for the existing bus address/byte count pair pointed to by the DMA handle passed in by you. This interface enables device drivers to patch an existing bus address/byte count pair due to an unexpected interruption in a DMA transfer. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer.
This interface is useful for DMA engines that do not have total control of a DMA transfer. For example, a SCSI device doing an unexpected disconnect in the middle of a block transfer can interrupt a SCSI controller. Such a disconnect can require the driver to do one or more patches to the list of bus address/byte count pairs in order to restart the DMA transfer (without starting from the beginning of the list). Device drivers call the dma_put_curr_sgentry and dma_put_prev_sgentry interfaces to accomplish these patch operations. Typically, only buses that do not have byte atomicity transfers (for example, the TURBOchannel bus on Alpha CPUs) require these patch operations.
See Writing Device Drivers: Tutorial for a code example of the dma_put_curr_sgentry interface.
Upon successful completion, dma_put_curr_sgentry returns the value 1. Otherwise, dma_put_curr_sgentry returns the value zero (0) to indicate a failure. This failure indicates that the index into the DMA handle points past the end of the last bus address/byte count pair. The DMA handle can point past the end of the valid portion of the list when the previous call to dma_get_next_sgentry returned the pointer to the DMA handle associated with the last byte address/byte count pair.
dma_get_curr_sgentry, dma_get_next_sgentry, dma_put_prev_sgentry, sg_entry
Updates an internal pointer index to the linked list of sg_entry structures
#include <io/common/devdriver.h>
int dma_put_prev_sgentry
(dma_handle,
sg_entryp)
dma_handle_t
dma_handle;
sg_entry_t
sg_entryp;
The dma_put_prev_sgentry interface updates an internal pointer index to the linked list of sg_entry structures, and then puts new bus address/byte count values into the existing bus address/byte count pair pointed to by the DMA handle passed in by you. This interface enables device drivers to patch existing bus address/byte count pairs due to an unexpected interruption in a DMA transfer. A bus address/byte count pair is represented by the ba and bc members of an sg_entry structure pointer.
This interface is useful for DMA engines that do not have total control of a DMA transfer. For example, a SCSI device doing an unexpected disconnect in the middle of a block transfer can interrupt a SCSI controller. Such a disconnect can require the driver to do one or more patches to the list of bus address/byte count pairs in order to restart the DMA transfer (without starting from the beginning of the list). Device drivers call the dma_put_curr_sgentry and dma_put_prev_sgentry interfaces to accomplish these patch operations. Typically, only buses that do not have byte atomicity transfers (for example, the TURBOchannel bus on Alpha CPUs) require these patch operations.
The dma_put_prev_sgentry differs from dma_put_curr_sgentry in that it updates an internal pointer index before inserting the new bus address/byte count values into the existing bus address/byte count pair. The pointer-index retains this updated value after dma_put_prev_sgentry returns.
See Writing Device Drivers: Tutorial for a code example of the dma_put_prev_sgentry interface.
Upon successful completion, dma_put_prev_sgentry returns the value 1. Otherwise, dma_put_prev_sgentry returns the value zero (0) to indicate failure. This failure indicates that the index into the DMA handle points to the first element in the byte address/byte count list. In this case, there is no previous byte address/byte count entry to patch.
dma_put_curr_sgentry, sg_entry
Stores a data element in the DMA private storage space
#include <io/common/devdriver.h>
int dma_put_private
(dma_handle,
index,
data)
dma_handle_t
dma_handle;
int
index;
u_long
data;
The dma_put_private interface stores a 64-bit data element in the DMA private storage area of the internal kernel data structure associated with the specified DMA handle. Device drivers call dma_get_private to retrieve a 64-bit data element from the DMA private storage of this internal kernel data structure. Currently, the internal kernel data structure provides only one member to store a 64-bit data element.
DMA engines that make multiple calls to dma_map_alloc and dma_map_load can use the dma_get_private and dma_put_private pair of interfaces. Each call to dma_map_alloc and dma_map_load returns the address of the allocated DMA handle. The device driver calls dma_put_private to store each returned address as the 64-bit data element in each internal kernel data structure's DMA private storage area. By storing each address as the 64-bit data element in each internal kernel data structure's DMA private storage area, the device driver creates a linked list of DMA handles that it can pass to some lower software layer. This lower software layer then calls dma_get_private to retrieve the resources contained in the linked list of DMA handles.
Use of the dma_put_private interface makes device drivers more portable between DMA hardware-mapping implementations across different hardware platforms because it masks out any future changes in the kernel- and system-level DMA mapping data structures.
Upon successful completion, dma_put_private returns the value 1. If the index to the private storage area is greater than zero (0), dma_put_private returns the value zero (0), indicating a failure status.
Initializes the device to its assigned configuration
void do_config
(ctlr_p)
struct controller
*ctlr_p;
The do_config interface initializes the specified controller based on its power-up resource assignments. If the device uses either an interrupt or a DMA channel, then do_config also performs any setup requirements. For example, for controllers that connect to the EISA bus, do_config initializes the controller according to the parameters specified in the EISA configuration file.
The do_config interface is a generic interface that maps to a bus-specific interface that actually initializes the specified device. Using this interface to initialize a device makes the driver more portable across different bus architectures. Not all buses support the do_config interface.
None
controller, get_config, get_info
Registers or deregisters a flush interface
void drvr_register_flush
(callback,
parameter,
flags)
void
(*callback) ();
caddr_t
parameter;
int
flags;
Value | Meaning |
DRVR_REGISTER | Registers a device driver's flush interface. |
DRVR_UNREGISTER | Deregisters a device driver's flush interface. |
The drvr_register_flush interface registers or deregisters a flush interface to be called when the system performs a system shutdown. The kernel calls this interface just prior to system shutdown. The drvr_register_flush interface allows device drivers to ensure that all I/O to a bus adapter has been completed. Some bus adapters are asynchronous to the system bus (containing buffers) and need to be cleared out (that is, flushed).
Device drivers should call the drvr_register_flush interface if it is necessary to call a flush interface before the kernel performs a dump operation. Device drivers should call the drvr_register_shutdown interface if it is necessary to call a shutdown interface that the kernel calls after it performs the dump operation.
The drvr_register_flush interface causes a system crash and displays an appropriate message on the console terminal if you specify an invalid bit in the flags argument.
The flush interface that you register should never acquire a simple or complex lock. The reason for this is that on a panic, the kernel's call to the flush interface can cause the CPU to double panic, hang, or both if the callback interface tries to access locks that were taken through active code paths at the time of the panic. This double panic or hang of the CPU would result in no crash dump for diagnosis. A hang of the CPU can require operator intervention to restart the CPU.
None
drvr_register_shutdown xxflush
Registers or deregisters a shutdown interface
void drvr_register_shutdown
(callback,
parameter,
flags)
void
(*callback) ();
caddr_t
parameter;
int
flags;
Value | Meaning |
DRVR_REGISTER | Registers a device driver shutdown interface. |
DRVR_UNREGISTER | Deregisters a device driver's shutdown interface. |
The drvr_register_shutdown interface registers or deregisters a shutdown interface for the calling device driver. Device drivers call drvr_register_shutdown to register a device driver shutdown interface that the kernel calls at system shutdown time (or when the user halts the system).
The drvr_register_shutdown interface allows device drivers to register interfaces that turn off the hardware device before the system shuts down. For example, a device driver that operates on a SCSI bus connected to two host CPUs (Available Server Environment) must ensure that the SCSI adapter on the shutdown host CPU is not doing work on the bus.
Device drivers should call the drvr_register_shutdown interface if it is necessary to call a shutdown interface that the kernel calls after it performs the dump operation. Device drivers should call the drvr_register_flush interface if it is necessary to call a flush interface before the kernel performs a dump operation.
The drvr_register_shutdown interface causes a system crash and displays an appropriate message on the console terminal if the device driver writer sets an invalid bit in the flags argument.
The shutdown interface that you register should never acquire a simple or complex lock. The reason for this is that on a panic, the kernel's call to the shutdown interface can cause the CPU to double panic, hang, or both if the callback interface tries to access locks that were taken through active code paths at the time of the panic. This double panic or hang of the CPU would result in no crash dump for diagnosis. A hang of the CPU can require operator intervention to restart the CPU.
After the system has performed the crash dump and a panic occurs, the kernel's call to the shutdown interface can cause the CPU to double panic, hang, or both. This double panic or hang of the CPU can be the result of the following activities:
A hang of the CPU can require operator intervention to restart the CPU.
None
drvr_register_flush xxshutdown
Duplicates a message block descriptor
#include <sys/stream.h>
MBLKP dupb
(message_block_ptr)
MBLKP
message_block_ptr;
The dupb interface creates a new message block structure to reference the message block pointed to by the message_block_ptr argument. Unlike copyb, the dupb interface does not copy the information in the data block, but creates a new structure to point to it. The new message block structure contains the same information as the first message block structure.
Upon successful completion, the dupb interface returns a pointer to the newly allocated message block. This newly allocated message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
Otherwise, dupb returns a NULL pointer.
Duplicates a message
#include <sys/stream.h>
MBLKP dupmsg
(message_block_ptr)
MBLKP
message_block_ptr;
The dupmsg interface forms a new message by copying the message block descriptors pointed to by the message_block_ptr argument and linking them. The dupmsg interface calls dupb for each message block. The data blocks themselves are not duplicated.
Upon successful completion, the dupmsg interface returns a pointer to the newly allocated message block. This newly allocated message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
Otherwise, dupmsg returns a NULL pointer.
Enables a queue for service
#include <sys/stream.h>
void enableok
(queue_pointer)
queue_t
*queue_pointer;
The enableok interface allows the queue associated with the queue_pointer argument to be rescheduled for service. The interface cancels the effect of a previous call to the noenable interface on the queue by turning off the QNOENB flag in the queue.
None
Allocates a message block with a shared buffer
#include <sys/stream.h>
mblk_t * esballoc
(base,
size,
pri,
free_rtnp)
unsigned char
*base;
int
size;
int
pri;
frtn_t
*free_rtnp;
The esballoc interface creates a STREAMS message and attaches a user-supplied data buffer in place of a STREAMS data buffer. The interface calls allocb to obtain a message and data block header. The user-supplied data buffer, pointed to by the base argument, is used as the data buffer for the message.
The free_rtn structure is referenced by the dp_freep member of the datab structure. When the freeb interface is called to free the message, the driver's message free interface (referenced through the free_rtn structure) is called, with arguments, to free the data buffer.
The free_rtn structure is defined as follows:
/* Free return structure for esballoc */ typedef struct free_rtn { void (*free_func)(char *, char *); /* Interface to free buffer */ char * free_arg; /* Parameter to free_func */ } frtn_t;
Instead of requiring a specific number of arguments, the free_arg member is of type char *. This way, the driver can pass a pointer to a structure if more than one argument is needed.
The free_func interface must be defined in kernel space and should be declared void. It has no user context and must not sleep.
Upon successful completion, the esballoc interface returns a pointer to the newly allocated message block. This message block is of type struct msgb *. The msgb data structure is defined in the /usr/sys/include/sys/stream.h file.
On failure, esballoc returns a NULL pointer.
Finds the first set bit in a mask
The ffs interface returns the bit position of the first bit you set in the mask argument. The scan proceeds from the least significant bit to the most significant bit of the mask.
The ffs interface duplicates the behavior of the ffs instruction on the Digital VAX series computers. It is not present in architectures that implement its function in hardware or through compiler inline substitution.
The ffs interface is useful for translating bits in a bit mask into bit positions. For example, the signal handling code uses it to analyze the signal mask.
Upon successful completion, ffs returns the bit position of the first bit set in the mask. If no bits were set in the mask, ffs returns the value zero (0).
Flushes messages for a specified priority band
#include <sys/stream.h>
void flushband
(queue,
pri,
flag)
queue_t
*queue;
unsigned char
pri;
int
flag;
Value | Meaning |
FLUSHDATA | Flush only data messages. These data messages are represented by the M_DATA, M_DELAY, M_PROTO, and M_PCPROTO constants. |
FLUSHALL | Flush all messages. Requests a realtime delay |
The flushband interface flushes messages associated with the priority band specified by the pri argument. If pri is zero (0), flushband flushes only normal and high priority messages. Otherwise, flushband flushes messages from the priority band specified by the pri argument according to the value passed to the flag argument.
None
Removes a message from a queue
#include <sys/stream.h>
void flushq
(queue,
flag)
queue_t
*queue;
int
flag;
Value | Meaning |
FLUSHDATA | Flush only data messages. These data messages are represented by the M_DATA, M_DELAY, M_PROTO, and M_PCPROTO constants. |
FLUSHALL | Flush all messages. Requests a realtime delay |
The flushq interface frees messages and their associated data structures by calling the freemsg interface. If the queue's count falls below the low-water mark and the QWANTW bit is set, flushq enables the nearest upstream service procedure.
None
Deallocates (frees) the allocated kernel virtual memory
#include <sys/malloc.h>
FREE
(addr,
type)
void
*addr;
int
type;
The FREE interface (macro) deallocates (frees) the allocated kernel virtual memory, which you allocated in a previous call to MALLOC.
In previous versions of the operating system, device drivers could call the following memory allocation-related interfaces:
Device drivers called these interfaces to allocate and free a variable-sized section of kernel virtual memory.
Device drivers called this interface to perform nonblocking
allocation of a variable-sized section of kernel virtual memory.
Device drivers called these interfaces to allocate exact-size sections of kernel virtual memory.
Digital UNIX still provides backwards compatibility with the kalloc, kfree, and kget interfaces and the zinit, zchange, zalloc, zfree, and zget interfaces. However, Digital recommends that for new device drivers you use the MALLOC and FREE interfaces to allocate and to free sections of kernel virtual memory.
A memory corruption can occur if a device driver continues to use the memory after freeing it. Digital UNIX provides a built-in mechanism to debug such erroneous use of memory. You can enable this debugging feature at boot time by providing the following boot parameter: kmem_debug=1. When you enable this debugging feature, the free interface stores the following in the last word of freed memory:
The malloc interface checks the checksum of the memory content before reallocating this corrupted memory. If the checksum of the memory content does not match the corrupted memory, malloc stores the debug information and then causes the kernel to panic. The malloc interface stores the address and size of the corrupted memory and the pc of the interface that last freed it in a kmem_corrupt_data structure.
You should consider the following when using this debugging feature:
See Writing Device Drivers: Tutorial for a code example of the FREE interface.
None
Frees a message block
#include <sys/stream.h>
void freeb
(message_block_ptr)
MBLKP
message_block_ptr;
The freeb interface deallocates a message block. If the reference count of the db_ref member of the datab structure is greater than the value 1, freeb decrements the count. If db_ref equals the value 1, freeb deallocates the message block and the corresponding data block and buffer.
If the data buffer to be freed was allocated in a call to the esballoc interface, the buffer may be a non-STREAMS resource. In that case, the driver must be notified that the attached data buffer needs to be freed by calling the driver's freeing interface. To make this process independent of the driver used in the stream, freeb finds the free_rtn structure associated with the buffer. The free_rtn structure contains a pointer to the driver-independent interface that releases the buffer. Once this is accomplished, freeb releases the STREAMS resources associated with the buffer.
None
Frees all message blocks in a message
#include <sys/stream.h>
void freemsg
(message_block_ptr)
MBLKP
message_block_ptr;
The freemsg interface calls the freeb interface to free all message and data blocks associated with the message pointed to by the message_block_ptr argument.
None
Returns a byte from user address space
int fubyte
(user_src)
char
*user_src;
The fubyte interface returns 1 byte from the unprotected user address space to the calling program.
If the size of the return value is larger than 1 byte, the byte actually used for the return value is implementation defined.
Upon successful completion, fubyte returns a value greater than zero (0). Otherwise, it returns -1, indicating that the user address specified in user_src cannot be accessed.
copyinstr, fuword, subyte, suword
Returns a byte from user instruction address space
The fuibyte interface is for machines that have separate address spaces for code and data. Because the Alpha architecture does not have separate address spaces for code and data, fuibyte is mapped to fubyte. The fuibyte interface is provided for compatibility reasons. See the interface description for fubyte for a complete description of fuibyte.
Returns a word from user instruction address space
The fuiword interface is for machines that have separate address spaces for code and data. Because the Alpha architecture does not have separate address spaces for code and data, fuiword is mapped to fuword. The fuiword interface is provided for compatibility reasons. See the interface description for fuword for a complete description of fuiword.
Returns a word from user instruction address space
int fuword
(user_src)
char
*user_src;
The fuword interface returns one word from the unprotected user address space to the calling program.
Upon successful completion, fuword returns a value greater than zero (0). Otherwise, it returns -1, indicating that the user address specified in user_src cannot be accessed.
copyinstr, fubyte, subyte, suword
Returns assigned configuration data for a device
int get_config
(ctlr_p,
config_item,
func_type,
data_p,
handle)
struct controller
*ctlr_p;
uint_t
config_item;
char
*func_type;
void
*data_p;
int
handle;
Value | Meaning |
RES_MEM | Obtains bus memory characteristics. |
RES_IRQ | Obtains interrupt channel characteristics assigned to the device. |
RES_DMA | Obtains the DMA channel assigned to the device. |
RES_PORT | Obtains the I/O port assignments for the device. |
The get_config interface returns configuration data information assigned to the specified device.
The get_config interface is a generic interface that maps to a bus-specific interface that actually returns the assigned configuration data for the specified device. Using this interface to obtain configuration information makes the driver more portable across different bus architectures.
If the bus option has only one resource of the requested config_item, get_config stores its value in the data_p argument and returns the value zero (0).
If the bus option has multiple resources of the requested config_item, get_config stores the value at the top of the list in the data_p argument and returns a handle that points to the next element in the list. To obtain the next element in the list, call get_config again and pass the returned handle to the handle argument. After reading all of the elements in the list, get_config returns the value zero (0).
If the bus option does not have a resource of the requested func_type, get_config returns the value -1.
controller, do_config, get_info
Calculates a default partition map
int get_def_partitionmap
(geom,
pmap)
register struct DEVGEOMST
*geom;
register struct pt_tbl
*pmap;
The get_def_partitionmap interface calculates a default partition map when supplied with a disk geometry. It uses the disk capacity and sector size for the calculation and ignores other aspects of the geometry. The partitions are layed out as follows:
Starts at block 0 and spans the number of blocks specified by the global variable ddr_def_partsz_a.
Starts after partition a and spans the number of blocks specified by the global variable ddr_def_partsz_b.
Spans the entire disk.
Are undefined with lengths of zero.
Starts after partition b and spans half of the remainder of the disk.
Starts after partition g and spans the remainder of the disk.
The get_def_partitionmap interface is called by a disk device driver's open interface and DIOCGDEFPT ioctl command to create a default partition map.
See Writing Device Drivers: Advanced Topics for a code example of the get_def_partitionmap interface.
Upon successful completion, get_def_partitionmap returns the value zero (0). Otherwise, it returns the value 1 to indicate the partition map could not be updated.
Returns system-specific information
#include <devdriver.h>
u_int get_info
(item_list)
struct item_list
*item_list;
The get_info interface returns system-specific data assigned to the hardware platform that the driver operates on. For example, a device driver might request system-specific information for the following Alpha hardware platforms: DEC 3000 Model 300 AXP Workstation, DEC 3000 Model 500 AXP Workstation, and DEC 4000 Model 610 AXP System.
The get_info interface checks the function code that you pass in the function member of the item_list data structure. If the hardware platform that the driver operates on supports the system item associated with this function code, get_info returns the system-specific data for the system item in the output_data member of the item_list structure. For example, get_info returns the TURBOchannel clock speed in the output_data member of the item_list structure if you specify the constant GET_TC_SPEED in the function member of the item_list structure.
The get_info interface then performs the identical checks for the rest of the item_list structures in the linked list.
The get_info interface is a generic interface that maps to a hardware platform-specific interface that actually returns the assigned CPU-specific data for the specified CPU. Using this interface to obtain CPU-specific information makes the driver more portable across different CPU architectures and different CPU types within the same architecture.
The return value from the get_info interface depends on the following issues:
In this case, get_info returns the value TRUE.
In this case, get_info returns the value NOT_SUPPORTED.
If get_info returns the value TRUE, it returns one of the following values in the rtn_status member of the item_list data structure:
Value | Meaning |
INFO_RETURNED | Indicates that the hardware platform that the driver currently operates on supports the requested system item. |
NOT_SUPPORTED | Indicates that the hardware platform that the driver operates on does not support the requested system item. |
do_config, get_config, item_list
Allocates a buf structure
None
The getnewbuf interface allocates a buf structure for performing I/O operations. This interface guarantees that the members of the structure are properly initialized prior to initiating the I/O request. Device drivers call this interface prior to calling physio, which performs the I/O operation.
Upon successful completion, getnewbuf returns a pointer to the allocated buf structure. On failure, it returns a NULL pointer.
Gets a message from the front of the queue
#include <sys/stream.h>
MBLKP getq
(message_queue)
queue_t
*message_queue;
The getq interface is used by a service interface to retrieve its enqueued messages.
A module or driver may include a service interface to process enqueued messages. Once the STREAMS scheduler calls this service it must process all enqueued messages, unless prevented by flow control. The getq interface gets the next available message from the top of the queue pointed to by the message_queue argument. You should call getq in a while loop that should be exited only when there are no more messages.
If there is a message to retrieve, getq returns a pointer to it. If no message is queued, getq returns a NULL pointer.
bcanput, canput, putbq, putq, qenable
Sends a signal to a process group
void gsignal
(pgroup,
signal)
pid_t
pgroup;
int
signal;
The gsignal interface sends a signal to a process group, invoking psignal for each process that is a member of the specified process group.
None
Registers a device driver's interrupt handler
ihandler_id_t * handler_add
(handler)
ihandler_t
*handler;
The handler_add interface registers a device driver's interrupt handler and its associated ihandler_t data structure to the bus-specific interrupt-dispatching algorithm. The ih_bus member of the ihandler_t structure specifies the parent bus structure for the bus controlling the driver being loaded. For controller devices, handler_add sets ih_bus to the address of the bus structure for the bus the controller resides on.
Device drivers call the handler_add interface to register a device driver's interrupt handler.
See Writing Device Drivers: Tutorial for a code example of the handler_add interface.
Upon successful completion, the handler_add interface returns an opaque ihandler_id_t key, which is a unique number that identifies the ISIs to be acted on by subsequent calls to handler_del, handler_disable, and handler_enable. To implement this ihandler_id_t key, each call to handler_add causes the handler_key data structure to be allocated.
The handler_add interface returns the value NULL if it cannot allocate the appropriate resources or if it detected an error.
handler_del, handler_disable, handler_enable, ihandler_t
Deregisters a device driver's interrupt handler
int handler_del
(id)
ihandler_id_t
*id;
The handler_del interface deregisters a device driver's interrupt handler from the bus-specific interrupt-dispatching algorithm. In addition, the interface unlinks the handler_key structure associated with the interrupt handler. Prior to deleting the interrupt handler, the device driver should have disabled it by calling handler_disable. If the interrupt handler was not disabled, handler_del returns an error.
The handler_del interface uses the id argument to call a bus-specific adp_handler_del interface to remove the driver's interrupt handler. Deregistration of an interrupt handler can consist of replacing it with the stray interface to indicate that interrupts are no longer expected from this device. The stray interface is a generic interface used as the interrupt handler when there is no corresponding interrupt handler.
Device drivers that are dynamically configured into the kernel call the handler_del interface to deregister a device driver's interrupt handler. It is not necessary for device drivers that are statically configured into the kernel to call the handler_del interface.
See Writing Device Drivers: Tutorial for a code example of the handler_del interface.
Upon successful completion, handler_del returns the value zero (0). Otherwise, it returns the value -1.
adp_handler_del, handler_add, handler_disable, handler_enable, ihandler_t
Disables a previously registered interrupt handler
int handler_disable
(id)
ihandler_id_t
*id;
The handler_disable interface makes the driver's previously registered interrupt handlers unavailable to the system. You must call handler_disable prior to calling handler_del. The handler_disable interface uses the id argument to call a bus-specific adp_handler_disable interface to perform the bus-specific tasks needed to disable the interrupt handlers.
Device drivers that are dynamically configured into the kernel should call the handler_disable interface to disable a previously registered interrupt handler. It is not necessary for device drivers that are statically configured into the kernel to call handler_disable.
See Writing Device Drivers: Tutorial for a code example of the handler_disable interface.
Upon successful completion, handler_disable returns the value zero (0). Otherwise, it returns the value -1.
handler_add, handler_del, handler_enable, ihandler_t
Enables a previously registered interrupt handler
int handler_enable
(id)
ihandler_id_t
*id;
The handler_enable interface marks that interrupts are enabled and can be dispatched to the driver's interrupt handlers, as registered in a previous call to handler_add. The id argument passed to handler_enable is used to call a bus-specific adp_handler_enable interface to perform the bus-specific tasks needed to enable the interrupt handlers.
Device drivers should call the handler_enable interface to enable a previously registered interrupt handler.
See Writing Device Drivers: Tutorial for a code example of the handler_enable interface.
Upon successful completion, handler_enable returns the value zero (0). Otherwise, it returns the value -1.
adp_handler_enable, handler_add, handler_del, handler_disable, ihandler_t
Convert word and longword values from host-to-network byte order
#include <sys/param.h>
unsigned int htonl
(longword)
unsigned int
longword;
unsigned short htons
(word)
unsigned short
word;
The htonl interface converts the specified longword value from host-to-network byte order. The htons interface converts the specified word value from host-to-network byte order.
The TCP/IP protocols specify the canonical network byte order, which is big endian (meaning that the most significant byte is leftmost in memory).
Upon successful completion, the htonl interface returns the converted longword value in network byte order. Similarly, upon successful completion, the htons interface converts the specified word value in network byte order.
Inserts a STREAMS message into a queue
#include <sys/stream.h>
int insq
(message_queue,
message,
message_to_be_inserted)
queue_t
*message_queue;
MBLKP
message;
MBLKP
message_to_be_inserted;
The insq interface inserts a STREAMS message into a queue. The message to be inserted (the message_to_be_inserted argument) is placed in the queue (the message_queue argument) immediately before the message associated with the message argument. If the message argument is NULL, insq places the new message at the end of the queue. The interface ignores the queue class of the new message and it updates all flow-control parameters. The insq interface also enables the service procedure unless the QNOENB flag bit is set.
If the message argument is non-NULL, it must point to a message on the queue or a system panic could occur.
Upon successful completion, insq returns the value 1. On failure, it returns the value zero (0).
Add or remove an element from the queue
struct generic_qheader { struct generic_qheader *q_forw; struct generic_qheader *q_back; };
int insque
(elem,
pred)
struct generic_qheader
*elem;
struct generic_qheader
*pred;
int remque
(elem)
struct generic_qheader
*elem;
The insque interface adds the element that the elem argument specifies to the queue. The interface inserts elem in the next position after pred in the queue.
The remque interface removes the element that the elem argument specifies from the queue it is currently in.
Queues are built from doubly linked lists. Each element is linked into the queue through a queue header. All queue headers are of the generic form struct generic_qheader. A given element may have multiple queue headers. This allows each element to be simultaneously linked onto multiple queues.
Any driver interface that manipulates these queues must call an appropriate spl interface to ensure that the spl level is high enough to block out any interrupts for other device drivers that may access these queues.
None
Copies data from bus address space to system memory
int io_copyin
(srcaddr,
destaddr,
byte_count)
io_handle_t
srcaddr;
vm_offset_t
destaddr;
u_long
byte_count;
The io_copyin interface copies data from bus address space to system memory. The interface optimizes the copy operation for 32-bit transfers. The I/O handle you pass to srcaddr identifies where the copy originates in bus address space, and the address you pass to destaddr identifies where the copy occurs in system memory. The io_copyin interface assumes no alignment of data associated with srcaddr and destaddr.
The io_copyin interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the copy from bus address space to system memory. Using io_copyin to perform the copy operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.
The I/O handle that you pass to the srcaddr argument of the io_copyin interface must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the srcaddr argument that references addresses residing in some other space (for example, dense space) the results of the copy operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
See Writing Device Drivers: Tutorial for a code example of the io_copyin interface.
Upon successful completion, io_copyin returns IOA_OKAY. It returns the value -1 on failure.
Copies data from bus address space to bus address space
int io_copyio
(srcaddr,
destaddr,
byte_count)
io_handle_t
srcaddr;
io_handle_t
destaddr;
u_long
byte_count;
The io_copyio interface copies data from one location in bus address space to another location in bus address space. The I/O handles you pass to srcaddr and destaddr identify the locations in bus address space where the copy originates and where the copy occurs. The io_copyio interface assumes no alignment of data associated with srcaddr and destaddr.
The io_copyio interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the copy of data from one location in bus address space to another location in bus address space. Using io_copyio to perform the copy operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.
The I/O handles that you pass to the srcaddr and destaddr arguments of the io_copyio interface must be I/O handles that reference addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass I/O handles to the srcaddr and destaddr arguments that reference addresses residing in some other space (for example, dense space) the results of the copy operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
See Writing Device Drivers: Tutorial for a code example of the io_copyio interface.
Upon successful completion, io_copyio returns IOA_OKAY. It returns the value -1 on failure.
Copies data from system memory to bus address space
int io_copyout
(srcaddr,
destaddr,
byte_count)
vm_offset_t
srcaddr;
io_handle_t
destaddr;
u_long
byte_count;
The io_copyout interface copies data from system memory to bus address space. The interface optimizes the copy operation for 32-bit transfers. The address you pass to srcaddr identifies the kernel virtual address where the copy originates in system memory, and the I/O handle you pass to destaddr identifies the address where the copy occurs in bus address space. The io_copyout interface assumes no alignment of data associated with srcaddr and destaddr.
The io_copyout interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the copy to bus address space. Using io_copyout to perform the copy operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.
The I/O handle that you pass to the destaddr argument of the io_copyout interface must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the destaddr argument that references addresses residing in some other space (for example, dense space) the results of the copy operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
See Writing Device Drivers: Tutorial for a code example of the io_copyout interface.
Upon successful completion, io_copyout returns IOA_OKAY. It returns the value -1 on failure.
Zeros a block of memory in bus address space
int io_zero
(destaddr,
byte_count)
io_handle_t
destaddr;
u_long
byte_count;
The io_zero interface zeros byte_count bytes of memory beginning at the bus address specified by destaddr. The I/O handle you pass to destaddr identifies where the zero operation occurs in bus address space. The interface optimizes the copy operation for 32-bit transfers.
The io_zero interface is a generic interface that maps to a machine-specific interface that actually writes zeros to some location in bus address space. Using io_zero to perform the zero operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.
The I/O handle that you pass to the destaddr argument of the io_zero interface must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the destaddr argument that references addresses residing in some other space (for example, dense space) the results of the copy operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
Upon successful completion, io_zero returns IOA_OKAY. It returns the value -1 on failure.
io_copyin, io_copyio, io_copyout
Indicates that I/O is complete
void iodone
(bp)
struct buf
*bp;
The iodone interface indicates that I/O is complete and reschedules the process that initiated the I/O.
None
Converts an I/O handle to a valid system physical address
u_long iohandle_to_phys
(io_handle,
flags)
io_handle_t
io_handle;
long
flags;
The
iohandle_to_phys
interface converts an I/O handle to a valid system
physical address that a device driver uses to perform I/O copy
operations.
You use this physical address in the I/O copy operations associated with
calls to
bcopy,
copyin,
copyout,
or a copy interface that you supply.
Do not use the physical address returned by
iohandle_to_phys
in calls to
io_copyin,
io_copyio,
and
io_copyout.
These interfaces take an I/O handle instead of a physical address and
are to bus address space what
bcopy
is to system memory.
To indicate the conversion type, pass one of the following conversion
type values defined in
/usr/sys/include/io/common/devdriver.h:
Value | Meaning |
HANDLE_DENSE_SPACE | Converts the I/O handle to a dense space physical address. |
HANDLE_SPARSE_SPACE | Converts the I/O handle to a sparse space physical address. |
HANDLE_BUSPHYS_ADDR | Converts the I/O handle to a bus physical address. |
In addition to one of the conversion type values, you OR in one of the following data size values defined in /usr/sys/include/io/common/devdriver.h. This bit is required for compatibility across Alpha CPU architectures. Some Alpha CPUs encode size information in the physical address used in I/O or memory bus accesses.
Value | Meaning |
HANDLE_BYTE | Converts the I/O handle to a system physical address that points to a byte address. This byte address resides in bus address space (either I/O space or memory space). |
HANDLE_WORD | Converts the I/O handle to a system physical address that points to a word address. This word address resides in bus address space (either I/O space or memory space). |
HANDLE_LONGWORD | Converts the I/O handle to a system physical address that points to a longword address. This longword address resides in bus address space (either I/O space or memory space). |
HANDLE_QUADWORD | Converts the I/O handle to a system physical address that points to a quadword address. This quadword address resides in bus address space (either I/O space or memory space). |
HANDLE_TRIBYTE | Converts the I/O handle to a system physical address that points to a tribyte address. This tribyte address resides in bus address space (either I/O space or memory space). |
The iohandle_to_phys interface is a generic interface that maps to a bus- and machine-specific interface that actually converts the I/O handle to a system physical address. Using this interface to convert the I/O handle makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
Upon successful completion, iohandle_to_phys returns the physical address indicated by the conversion type value that you passed to the flags argument. If a failure occurs, iohandle_to_phys returns the value zero (0). A return value of zero (0) can occur as a result of the following conditions:
io_copyin, io_copyio, io_copyout
Determine if the specified address is located in the kernel-unmapped address space, the user-mapped address space, and the kernel-mapped address space.
void IS_KSEG_VA
(addr)
unsigned long
addr;
void IS_SEG0_VA
(addr)
unsigned long
addr;
void IS_SEG1_VA
(addr)
unsigned long
addr;
The IS_KSEG_VA interface determines if the specified address is located in the kernel-unmapped address space. The IS_SEG0_VA interface determines if the specified address is located in the user-mapped address space. The IS_SEG1_VA interface determines if the specified address is located in the kernel-mapped address space.
The following code fragment shows a call to IS_KSEG_VA:
.
.
.
caddr_t virt_addr; [1] unsigned phys_addr; [2]
.
.
.
if(IS_KSEG_VA(virt_addr)) { [3]phys_addr = KSEG_TO_PHYS(virt_addr); [4]
.
.
.
None
Starts a fixed priority kernel thread dedicated to interrupt service
thread_t kernel_isrthread
(task,
start,
pri)
task_t
task;
void
(*start) ();
int
pri;
The following priority usage table describes the possible scheduling priorities. The first column shows a range of priorities. The second column shows an associated scheduling priority constant defined in <src/kernel/kern/sched.h> (if applicable). The third column describes the usage of the priority ranges. To specify a scheduling priority of 38, you pass the constant BASEPRI_SYSTEM as shown in the example. To specify a scheduling priority of 33, you can pass the following: BASEPRI_HIGHEST + 1.
Priority | Constant | Usage |
0
. . . 31 |
N/A | Realtime kernel threads |
32
. . . 38 |
BASEPRI_HIGHEST
. . . BASEPRI_SYSTEM |
Operating system kernel threads |
44
. . . 64 |
BASEPRI_USER
. . . BASEPRI_LOWEST |
User kernel threads |
The kernel_isrthread interface creates and starts a kernel thread at the specified entry point. This kernel thread handles only interrupt service requests in the specified task and at the specified priority level. A device driver should always attach a kernel thread to the ``first task.''
See Writing Device Drivers: Advanced Topics for a code example of the kernel_isrthread interface.
Upon successful completion, kernel_isrthread returns a pointer to the thread structure associated with the kernel thread started at the specified entry point. Device drivers can use this pointer as a handle to a specific kernel thread in calls to other kernel threads-related interfaces.
kernel_thread_w_arg, task, thread
Starts a kernel thread with a calling argument passed in
thread_t kernel_thread_w_arg
(task,
start,
argument)
task_t
task;
void
(*start) ();
void
*argument;
The kernel_thread_w_arg interface creates and starts a kernel thread in the specified task at the specified entry point with a specified argument. The kernel_thread_w_arg interface passes the specified argument to the newly created kernel thread. The kernel_thread_w_arg interface creates and starts a kernel thread with timeshare scheduling. A kernel thread created with timeshare scheduling means that its priority degrades if it consumes an inordinate amount of CPU resources. A device driver should call kernel_thread_w_arg only for long-running tasks. A device driver should always attach a kernel thread to the ``first task.''
This interface is actually a convenience wrapper for the thread_create interface (which creates the kernel thread) and the thread_start interface (which starts the newly created kernel thread).
The kernel_thread_w_arg interface behaves identically to kernel_isrthread except that with kernel_thread_w_arg you can pass an argument to the entry point for the newly created kernel thread.
See Writing Device Drivers: Advanced Topics for a code example of the kernel_thread_w_arg interface.
Upon successful completion, kernel_thread_w_arg returns a pointer to the thread structure associated with the kernel thread started at the specified entry point. Device drivers can use this pointer as a handle to a specific kernel thread in calls to other kernel threads-related interfaces.
kernel_isrthread, task, thread
Converts a kernel-unmapped virtual address to a physical address
vm_offset_t KSEG_TO_PHYS
(addr)
vm_offset_t
addr;
The KSEG_TO_PHYS interface converts a kernel-unmapped virtual address to a kernel physical address. Device drivers can use this physical address in DMA operations. Prior to calling KSEG_TO_PHYS, device driver writers often call one of the following interfaces to determine whether the address passed is a virtual address in the addressed kernel segment:
Determines if the specified address is located in the kernel-unmapped address space
Determines if the specified address is located in the user-mapped address space
Determines if the specified address is located in the kernel-mapped address space
The following code fragment shows a call to KSEG_TO_PHYS:
.
.
.
caddr_t virt_addr; [1] unsigned phys_addr; [2]
.
.
.
if(IS_KSEG_VA(virt_addr)) { [3]phys_addr = KSEG_TO_PHYS(virt_addr); [4]
.
.
.
Upon successful completion, KSEG_TO_PHYS returns the physical address.
Concatenates two message blocks
#include <sys/stream.h>
void linkb
(message,
message_to_be_added)
MBLKP
message;
MBLKP
message_to_be_added;
The linkb interface creates a new message by adding the message specified in the message_to_be_added argument to the tail of the message specified in the message argument. The continuation pointer (the b_cont member of the msgb structure) of the first message is set to point to the second message (the message_to_be_added argument).
None
Releases a complex lock
#include <kern/lock.h>
void lock_done
(lock_structptr)
lock_t
lock_structptr;
The lock_done interface releases a lock that was previously asserted by one of the following complex locking interfaces: lock_read, lock_try_read, lock_try_write, and lock_write.
You must hold the lock on the resource before calling lock_done.
See Writing Device Drivers: Advanced Topics for a code example of the lock_done interface.
None
lock, lock.h, lock_init, lock_read, lock_terminate, lock_try_read, lock_try_write, lock_write
Initializes a complex lock
#include <kern/lock.h>
void lock_init
(lock_structptr,
can_sleep)
lock_t
lock_structptr;
boolean_t
can_sleep;
The lock_init interface initializes a complex lock. You identify this lock by declaring a pointer to a complex lock structure and passing it as the first argument. The complex lock structure pointer must be initialized before you can assert read and write operations on the complex lock.
See Writing Device Drivers: Advanced Topics for a code example of the lock_init interface.
None
lock_done, lock, lock.h, lock_read, lock_terminate, lock_try_read, lock_try_write, lock_write
Asserts a complex lock with read-only access
#include <kern/lock.h>
void lock_read
(lock_structptr)
lock_t
lock_structptr;
The lock structure is an opaque data structure; that is, its associated members are referenced and manipulated by the Digital UNIX operating system and not by the user of the complex lock mechanism.
The lock_read interface asserts a lock with read-only access for the resource associated with the specified lock structure pointer. The lock_read interface allows multiple kernel threads to access the resource read-only at the same time. When a read lock is asserted, the protected resource is guaranteed not to change.
To release a previously asserted read lock, call the lock_done interface.
You must call lock_init (once only) prior to calling lock_read to initialize the lock structure pointer for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the lock_read interface.
None
lock_done, lock, lock.h, lock_terminate, lock_try_read, lock_try_write, lock_write,
Terminates, using a complex lock
#include <kern/lock.h>
void lock_terminate
(lock_structptr)
lock_t
lock_structptr;
The lock_terminate interface determines that the driver is done using the complex lock forever. The complex lock must be free (that is, the driver does not hold the lock) before calling lock_terminate. The device driver must not reference the specified complex lock after calling lock_terminate.
You must call lock_init (once only) prior to calling lock_terminate to initialize the lock structure pointer for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the lock_terminate interface.
None
lock_done, lock_init, lock, lock.h, lock_try_read, lock_try_write, lock_write
Tries to assert a complex lock with read-only access
#include <kern/lock.h>
boolean_t lock_try_read
(lock_structptr)
lock_t
lock_structptr;
The lock structure is an opaque data structure; that is, its associated members are referenced and manipulated by the Digital UNIX operating system and not by the user of the complex lock mechanism.
The lock_try_read interface tries to assert a complex lock (without blocking) with read-only access for the resource associated with the specified lock structure pointer. To release a complex lock with read-only access successfully asserted by lock_try_read, call the lock_done interface.
You must call lock_init (once only) prior to calling lock_try_read to initialize the lock structure pointer for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the lock_try_read interface.
The lock_try_read interface returns one of the following values:
Value | Meaning |
TRUE | The attempt to acquire the read-only complex lock was successful. |
FALSE | The attempt to acquire the read-only complex lock was unsuccessful. |
lock_done, lock, lock.h, lock_try_write, lock_write
Tries to assert a complex lock with write access
#include <kern/lock.h>
boolean_t lock_try_write
(lock_structptr)
lock_t
lock_structptr;
The lock structure is an opaque data structure; that is, its associated members are referenced and manipulated by the Digital UNIX operating system and not by the user of the complex lock mechanism.
The lock_try_write interface tries to assert a complex lock (without blocking) with write access for the resource associated with the specified lock structure pointer. To release a complex lock with write access successfully asserted by lock_try_write, call the lock_done interface.
You must call lock_init (once only) prior to calling lock_try_write to initialize the lock structure pointer for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the lock_try_write interface.
The lock_try_write interface returns one of the following values:
Value | Meaning |
TRUE | The attempt to acquire the write complex lock was successful. |
FALSE | The attempt to acquire the write complex lock was unsuccessful. |
lock_done, lock, lock.h, lock_terminate, lock_try_read, lock_write
Asserts a complex lock with write access
#include <kern/lock.h>
void lock_write
(lock_structptr)
lock_t
lock_structptr;
The lock structure is an opaque data structure; that is, its associated members are referenced and manipulated by the Digital UNIX operating system and not by the user of the complex lock mechanism.
The lock_write interface asserts a lock with exclusive write access for the resource associated with the specified lock structure pointer. This means that once a write lock is asserted, no other kernel thread can gain read or write access to the resource until it is released.
To release a complex write lock successfully asserted by lock_write, call the lock_done interface.
You must call lock_init (once only) prior to calling lock_write to initialize the lock structure pointer for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the lock_write interface.
None
lock_done, lock, lock.h, lock_read, lock_terminate, lock_try_read, lock_try_write
Returns the device major number
#include <sys/types.h>
int major
(device)
dev_t
device;
The major interface returns the device major number associated with the device specified by the device argument. Device driver writers use the dev_t data type to represent a device's major and minor numbers. This data type is an abstraction of the internal representations of the major and minor numbers. Driver writers do not need to know how the system internally represents the major and minor numbers. To ensure maximum portability of the device driver, use the major interface to extract the major number portion of this internal representation.
See Writing Device Drivers: Tutorial for a code example of the major interface.
Upon successful completion, major returns the major number portion of the dev_t passed as the argument.
Returns a dev_t
#include <sys/types.h>
dev_t makedev
(major,
minor)
int
major;
int
minor;
The makedev interface returns a device number of type dev_t based on the numbers specified for the major and minor arguments.
Upon successful completion, makedev returns a dev_t that represents the major and minor device numbers passed as arguments.
Allocates a variable-size section of kernel virtual memory
#include <sys/malloc.h>
MALLOC
(addr,
cast,
size,
type,
flags)
addr;
cast;
u_long
size;
int
type;
int
flags;
Value | Meaning |
M_WAITOK | Allocates memory from the virtual memory subsystem if there is not enough memory in the preallocated pool. This constant signifies that MALLOC can block. |
M_NOWAIT | Does not allocate memory from the virtual memory subsystem if there is not enough memory in the preallocated pool. This constant signifies that MALLOC cannot block. |
M_ZERO | Allocates zero-filled memory. You pass this bit value by ORing it to M_WAITOK or M_NOWAIT. |
The MALLOC interface (macro) allocates at least size bytes from the kernel memory and returns the address of the allocated memory. A device driver can allocate the memory in interrupt and process context.
The MALLOC interface (macro) maintains a pool of preallocated memory for quick allocation. If there is not enough memory in the pool, MALLOC allocates memory from the virtual memory subsystem by calling kmem_alloc, which can potentially block (sleep). There is a kernel thread that allocates and frees memory to and from the preallocated pool.
The MALLOC interface (macro) is actually a wrapper that calls malloc. A device driver should not directly call the malloc interface.
The type argument allows the memory allocator to keep track of memory usage by a subsystem.
If the allocation size is greater than 16K, you must pass M_WAITOK to the flags argument. You cannot allocate more than 16K bytes of memory in interrupt context.
In previous versions of the operating system, device drivers could call the following memory allocation-related interfaces:
Device drivers called these interfaces to allocate and free a variable-sized section of kernel virtual memory.
Device drivers called this interface to perform nonblocking
allocation of a variable-sized section of kernel virtual memory.
Device drivers called these interfaces to allocate exact-size sections of kernel virtual memory.
Digital UNIX still provides backwards compatibility with the kalloc, kfree, and kget interfaces and the zinit, zchange, zalloc, zfree, and zget interfaces. However, Digital recommends that for new device drivers you use the MALLOC and FREE interfaces to allocate and to free sections of kernel virtual memory.
A memory corruption can occur if a device driver continues to use the memory after freeing it. Digital UNIX provides a built-in mechanism to debug such erroneous use of memory. You can enable this debugging feature at boot time by providing the following boot parameter: kmem_debug=1. When you enable this debugging feature, the free interface stores the following in the last word of freed memory:
The malloc interface checks the checksum of the memory content before reallocating this corrupted memory. If the checksum of the memory content does not match the corrupted memory, malloc stores the debug information and then causes the kernel to panic. The malloc interface stores the address and size of the corrupted memory and the pc of the interface that last freed it in a kmem_corrupt_data structure.
You should consider the following when using this debugging feature:
A device driver must not call MALLOC in interrupt context with the flags argument set to M_WAITOK. If flags is set to M_WAITOK, MALLOC checks if the kernel thread is in interrupt context. If so, MALLOC returns a null pointer and displays a message on the console terminal.
The M_WAITOK flag implies that it is valid to allocate memory from the virtual memory subsystem if there is not enough memory in the preallocated pool. To be able to allocate memory from the virtual memory subsystem (which can page fault), the device driver must be in process context.
See Writing Device Drivers: Tutorial for a code example of the MALLOC interface.
Upon successful completion, MALLOC returns the address of the allocated memory. The return type associated with this address is the same as that specified for the addr argument. If the memory allocation request cannot be fulfilled, MALLOC returns a null pointer in the addr argument.
Performs a memory barrier
None
The Alpha architecture does not guarantee read/write ordering. That is, the memory subsystem is free to complete read and write operations in any order that is optimal, without regard for the order in which they were issued. Read/write ordering is not the same as cache coherency, which is handled separately and is not an issue.
The Alpha architecture also contains a write buffer (as do many high-performance RISC CPUs, including the MIPS R3000). This write buffer can coalesce multiple writes to identical or adjacent addresses into a single write, effectively losing earlier write requests. Similarly, multiple reads to the same identical or adjacent addresses can be coalesced into a single read.
This coalescing has implications for multiprocessor systems, as well as systems with off-board I/O or DMA engines that can read or modify memory asynchronously or that can require multiple writes to actually issue multiple data items. The mb (memory barrier) interface guarantees ordering of operations. The mb interface is derived from the MB instruction, which is described in the Alpha Architecture Reference Manual.
The mb interface is a superset of the wbflush interface that ULTRIX drivers use. For compatibility, wbflush is aliased to mb on Digital UNIX Alpha systems.
You call mb in a device driver under the following circumstances:
Device drivers and the Digital UNIX operating system are the primary users of the mb interface. However, some user programs, such as a graphics program that directly maps the frame buffer and manipulates registers, might need to call mb. The Digital UNIX operating system does not provide a C library interface for mb. User programs that require use of mb should use the following asm construct:
#include <c_asm.h> asm ("mb");
In most situations that would require a cache flush on other CPU architectures, you should call the mb interface on Digital UNIX Alpha systems. The reason is not that mb is equivalent to a cache flush (as it is not). Rather, a common reason for doing a cache flush is to make data that the host CPU wrote available in main memory for access by the DMA device or to access from the host CPU data that was put in main memory by a DMA device. In each case, on an Alpha CPU you should use a memory barrier to synchronize with that event.
One example of using mb occurs with an ethernet network controller. Each ethernet network controller has a unique ethernet hardware address that is typically contained in a ROM on the ethernet controller board. The ethernet hardware address is a multibyte sequence typically consisting of at least 10 bytes. Frequently this multibyte ethernet hardware address is read from the controller hardware by the driver's probe interface by issuing a sequence of reads to the same controller register. Each successive read returns the next byte of the ethernet hardware address. In such instances, a call to mb should be inserted between each of these read operations to ensure that successive read operations do not get coalesced into fewer actual reads as seen by the ethernet controller.
See Writing Device Drivers: Tutorial for a code example of the mb interface.
Returns the device minor number
#include <sys/types.h>
int minor
(device)
dev_t
device;
The minor interface returns the device minor number associated with the device specified by the device argument. Device driver writers use the dev_t data type to represent a device's major and minor numbers. This data type is an abstraction of the internal representations of the major and minor numbers. Driver writers do not need to know how the system internally represents the major and minor numbers. To ensure maximum portability of the device driver, use the minor interface to extract the minor number portion of this internal representation.
See Writing Device Drivers: Tutorial for a code example of the minor interface.
Upon successful completion, minor returns the minor number portion of the dev_t passed as the argument.
Bounds the data transfer size
void minphys
(bp)
struct buf
*bp;
The minphys interface bounds the data transfer size by checking the b_bcount member of the buf structure pointed to by the bp argument. If the b_bcount member is greater than 63 * 1024, minphys sets b_bcount to 63 * 1024.
The minphys interface does not return a value. However, it may change the contents of the b_bcount member of the buf structure.
Blocks (puts to sleep) the current kernel thread
int mpsleep
(channel,
pri,
wmesg,
timo,
lockp,
flags)
caddr_t
channel;
long
pri;
char
*wmesg;
long
timo;
void
*lockp;
long
flags;
Value | Meaning |
MS_LOCK_SIMPLE | Calls mpsleep with a simple lock asserted. |
MS_LOCK_READ | Calls mpsleep with a read-only lock asserted on entry. |
MS_LOCK_WRITE | Calls mpsleep with a write lock asserted. |
MS_LOCK_ON_ERROR | Forces mpsleep to relock the lock on failure. |
MS_LOCK_NO_RELOCK | Forces mpsleep not to relock after blocking (sleeping). |
The mpsleep interface blocks (puts to sleep) the current kernel thread until a wakeup is issued on the address you specified in the channel argument. This interface is the symmetric multiprocessor (SMP) sleep call. The kernel thread blocks (sleeps) a maximum of timo divided by hz seconds. The value zero (0) means there is no timeout.
If you pass the PCATCH flag to the pri argument, mpsleep checks signals before and after blocking (sleeping). Otherwise, mpsleep does not check signals.
The mpsleep interface allows you to specify a pointer to a simple or complex lock structure that is associated with some resource. This interface unlocks this resource prior to blocking (sleeping). The flags argument specifies the lock type. The mpsleep interface releases the lock when the current kernel thread successfully performs an assert wait on the specified channel.
The mpsleep interface cannot be called from within a device driver's ISI because it is illegal to block at interrupt context.
The mpsleep interface returns the value zero (0) if awakened (success) and EWOULDBLOCK if the timeout specified in the timo argument expires (failure). On success, mpsleep relocks the lock if you did not set MS_LOCK_NO_RELOCK in flags. On failure, it leaves the lock unlocked. If you set the flags argument to MS_LOCK_ON_ERROR, mpsleep relocks the lock on failures.
assert_wait_mesg, clear_wait, thread_block, thread_wakeup, thread_wakeup_one
Returns the number of bytes in a message
#include <sys/stream.h>
int msgdsize
(message_block_ptr)
MBLKP
message_block_ptr;
The msgdsize interface counts the number of bytes in a data message. Only bytes included in the data blocks with a message type of M_DATA (ordinary data) is included in the count.
Upon successful completion, the msgdsize interface returns the number of data bytes in a message, expressed as an integer.
Prevents a queue from being scheduled
#include <sys/stream.h>
void noenable
(queue)
queue_t
*queue;
The noenable interface prevents the queue associated with the queue argument from being scheduled for service by the insq, putbq, or putq interfaces when enqueuing an ordinary priority message. You can enable the queue again by calling the enableok interface.
None
enableok, insq, putbq, putq, qenable
Convert longword and word values from network-to-host byte order
#include <sys/param.h>
unsigned int ntohl
(longword)
unsigned int
longword;
unsigned short ntohs
(word)
unsigned short
word;
The ntohl interface converts the specified longword value from network-to-host byte order. The ntohs interface converts the specified word value from network-to-host byte order.
The TCP/IP protocols specify the canonical network byte order, which is big endian (meaning that the most significant byte is leftmost in memory).
Upon successful completion, the ntohl interface returns the converted longword value in host byte order. Similarly, upon successful completion, the ntohs interface returns the converted word value in host byte order.
Gets a pointer to a module's other queue
#include <sys/stream.h>
queue_t * OTHERQ
(queue)
queue_t
*queue;
The OTHERQ interface returns a pointer to the other of the two queue structures that make up a STREAMS module or driver. If the queue argument points to the read queue, the OTHERQ interface returns the write queue. If the queue argument points to the write queue, the OTHERQ interface returns the read queue.
Upon successful completion, the OTHERQ interface returns a pointer to the other of the two queue structures that make up a STREAMS module or driver.
Copies a byte string with a specified limit
void ovbcopy
(b1,
b2,
n)
char
*b1;
char
*b2;
int
n;
The ovbcopy interface copies n bytes from string b1 to buffer b2. No check is made for null bytes.
The address ranges of b1 and b2 can overlap.
In most cases, ovbcopy is not as efficient as bcopy.
None
bcopy, blkclr, copystr, strcpy, strncpy
Causes a system crash
void panic
(message)
char
*message;
The panic interface causes a system crash, usually because of fatal errors. It sends to the console terminal and error logger the specified message and, possibly, other system-dependent information (for example, register dumps). It also causes a crash dump to be generated. After displaying the message, panic reboots the system if the console environment variables are set appropriately.
None
Implements raw I/O
int physio
(strategy,
bp,
device,
rwflag,
mincnt,
uio)
int
(*strategy) ();
register struct buf
*bp;
dev_t
device;
int
rwflag;
void
(*mincnt) ();
register struct uio
*uio;
The physio interface implements raw I/O. This interface maps the raw I/O request directly into the user buffer, without using bcopy. The memory pages in the user address space are locked while the transfer is processed.
See Writing Device Drivers: Tutorial for a code example of the physio interface.
The physio interface can return one of the following values:
Converts a physical address to a kernel-unmapped virtual address
vm_offset_t PHYS_TO_KSEG
(addr)
vm_offset_t
addr;
The PHYS_TO_KSEG interface converts a kernel physical address to a kernel-unmapped virtual address.
The following code fragment shows a call to PHYS_TO_KSEG:
.
.
.
caddr_t virt_addr; [1] unsigned phys_addr; [2]
.
.
.
virt_addr = PHYS_TO_KSEG(phys_addr); [3]
.
.
.
Upon successful completion, PHYS_TO_KSEG returns the virtual address associated with the specified physical address.
Extracts a physical page address
vm_offset_t pmap_extract
(pmap,
virt_addr)
pmap_t
pmap;
vm_offset_t
virt_addr;
The pmap_extract interface extracts the physical page address associated with the specified pmap (physical map) and virt_addr (virtual address) arguments. The virtual address includes the offset within a page.
The pmap_extract interface returns the value zero (0) if no valid translation exists for the specified virtual address in the specified physical map.
Returns the physical map handle for the kernel
None
The pmap_kernel interface returns the physical map handle for the kernel.
The pmap_kernel interface returns the physical map handle for the kernel.
Sets the modify bits of the specified physical page
void pmap_set_modify
(phys_page)
vm_offset_t
phys_page;
The pmap_set_modify interface informs the pmap module that the specified physical page was modified through a back-door mechanism.
None
Write formatted text to some output device
void printf
(format,
var_arglist)
char
*format;
va_dcl
var_arglist;
void uprintf
(format,
var_arglist)
char
*format;
va_dcl
var_arglist;
The printf and uprintf interfaces are scaled-down versions of the corresponding C library interfaces. The printf interface prints diagnostic information directly on the console terminal and writes ASCII text to the error logger. Because printf is not interrupt driven, all system activities are suspended when you call it.
The
uprintf
interface prints to the current user's terminal.
Interrupt service interfaces should never call
uprintf.
It does not perform any space checking, so you should not use this
interface to print verbose messages.
The
uprintf
interface does not log messages to the error logger.
You introduce conversion specifications by using the percent sign
(%).
Following the %,
you can include:
A field width or precision can be an asterisk (*) instead of a digit string. If you use an asterisk, you can include an argument that supplies the field width or precision.
The flag characters and their meanings are as follows:
For c, d, s, and u conversions, this flag has no effect.
For x or X conversions, the printf and uprintf interfaces pad a nonzero result on the left with 0x or 0X.
These interfaces support the following formats that device driver
writers find particularly useful:
The following line shows the format of the
printf
interface with the
%
b
conversion character:
printf("reg=%b\n", regval, "<base><arg>*");
In this case, base and arg are defined as:
The following shows a call to printf:
printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
This example would produce the following output:
reg=2<BITTWO,BITONE>
The following shows the format of the
printf
interface with the
%
r
and
%
R
conversion characters:
printf("%r R", val, reg_desc);
"<bit field descriptions>"
"0x%x<bit field descriptions>"
You use a reg_desc structure to describe the individual bit fields. To describe multiple bit fields within a single word, you can declare multiple reg_desc structures. The reg_desc structure is defined as follows:
struct reg_desc { unsigned rd_mask; /* mask to extract field */ int rd_shift; /* shift for extracted */ /* value, - >>, + << */ char *rd_name; /* field name */ char *rd_format; /* format to print field */ struct reg_values *rd_values; /* symbolic names of */ /* values */ };
The following is a sample reg_desc entry:
struct reg_desc dsc[] = { /* mask shift name format values */ { VPNMASK, 0, "VA", "0x%x", NULL }, { PIDMASK, PIDSHIFT, "PID", "%d", NULL }, { 0, 0, NULL, NULL, NULL }, };
The printf and uprintf interfaces also accept a field number, zero filling to length. For example:
printf(" %8x\n",regval);
The maximum field size is 11.
Some device drivers might have used the tprintf interface to print on a specified terminal or on the system console if no terminal was provided. Digital recommends that you discontinue use of tprintf because it will not be supported in future releases of the Digital UNIX operating system.
Checks for proper privileges
int privileged
(privilege,
error_code)
int
privilege;
int
error_code;
The privileged interface checks for an appropriate privilege when the security feature is enabled. Use privileged with a privilege number and with the error_code argument set to the constant EPERM to emulate the traditional behavior of the suser interface. Set error_code to a value of zero (0) if you want to check the privilege but not fail the operation if the user does not have the proper privilege. A value of -1 turns off all auditing as well.
The
privileged
interface returns the value zero (0) if the process does not have
privilege.
It returns the value 1 if the process does have privilege.
Sends a signal to a process
void psignal
(process,
signal)
struct proc
*process;
int
signal;
The psignal interface posts a signal to the specified process. The posting of a signal causes that signal to be added to the set of pending signals for the specified process. Depending on the state of the process and the state of the process's signals, the specified signal may be ignored, masked, caught by a tracing parent, or caught by the actual target process. If the signal is to be delivered to the target process, psignal examines and modifies the process state to prepare the execution of the appropriate signal handler.
None
Concatenates bytes in a message
#include <sys/stream.h>
int pullupmsg
(message_block_ptr,
len)
MBLKP
message_block_ptr;
int
len;
The pullupmsg interface tries to combine multiple data blocks into a single block. The interface concatenates and aligns the first len data bytes of the message pointed to by the message_block_ptr argument. If len equals the value -1, pullupmsg concatenates all data. If len bytes of the same message type cannot be found, pullupmsg fails and returns the value zero (0).
Upon successful completion, the pullupmsg interface returns the value 1. On failure, it returns the value zero (0).
Places a message at the head of a queue
#include <sys/stream.h>
int putbq
(queue_pointer,
message_block_ptr)
queue_t
*queue_pointer;
MBLKP
message_block_ptr;
The putbq interface places a message at the beginning of the appropriate section of the message queue. There are always sections for high priority and ordinary messages. If other priority bands are used, each will have its own section of the queue, in priority band order, after high priority messages and before ordinary messages. You can use putbq only for ordinary and priority band messages. High priority messages are not subject to flow-control and thus cannot be put back on the queue.
Typically, you call putbq when the bcanput or canput interface determines that the message cannot be passed on to the next stream component. The flow-control parameters are updated to reflect the change in the queue's status. If the QNOENB flag bit is not set, putbq enables the service interface.
Upon successful completion, putbq returns the value 1. On failure, it returns the value zero (0).
Puts a control message on a queue
#include <sys/stream.h>
int putctl
(queue_pointer,
type)
queue_t
*queue_pointer;
int
type;
The putctl interface tests the type argument to make sure a data type was not specified. It then attempts to allocate a message block. The putctl interface fails if a message block cannot be allocated or if the type argument is M_DELAY, M_PROTO, or M_PCPROTO.
The putctl interface calls the putctl_comm interface, which actually performs the work of sending the control message.
Upon successful completion, putctl returns the value 1. The putctl interface fails if a message block cannot be allocated or if the type argument is M_DELAY, M_PROTO, or M_PCPROTO.
Puts a control message with a 1-byte parameter on a queue
#include <sys/stream.h>
int putctl1
(queue_pointer,
type,
parameter)
queue_t
*queue_pointer;
int
type;
int
parameter;
Like the putctl interface, the putctl1 interface tests the type argument to make sure a data type was not specified. It then attempts to allocate a message block. In addition, the putctl1 interface can send a 1-byte parameter in the parameter argument. You can use the parameter for any purpose. For example, you can use this argument to specify how long the delay will be when sending an M_DELAY message. The putctl1 interface fails if a message block cannot be allocated or if the type argument is M_DELAY, M_PROTO, or M_PCPROTO.
The putctl1 interface calls the putctl_comm interface, which actually performs the work of sending the control message.
Upon successful completion, putctl1 returns the value 1. The putctl1 interface fails if a message block cannot be allocated or if the type argument is M_DELAY, M_PROTO, or M_PCPROTO.
Sends a message to the next module in the stream
#include <sys/stream.h>
void putnext
(queue_pointer,
message_ptr)
queue_t
*queue_pointer;
MBLKP
message_ptr;
The putnext interface passes a message to the puthere interface of the next queue in the stream.
None
Puts a message on a queue
#include <sys/stream.h>
int putq
(queue_pointer,
message_ptr)
queue_t
*queue_pointer;
MBLKP
message_ptr;
The putq interface puts a message on a driver's queue after the module's put interface has finished processing the message. The putq interface places the message after any other messages of the same priority and then updates any flow-control parameters. If the QNOENB flag bit is not set, putq enables the service interface. If no processing is done, the putq interface can be used as the module's put interface.
Upon successful completion, putq returns the value 1. On failure, it returns the value zero (0).
Enables a queue
#include <sys/stream.h>
void qenable
(queue_pointer)
queue_t
*queue_pointer;
The qenable interface puts the queue associated with the queue_pointer argument on the linked list of those whose service interfaces are ready to be called by the STREAMS scheduler.
None
Sends a message in the reverse direction
#include <sys/stream.h>
void qreply
(queue_pointer,
message_ptr)
queue_t
*queue_pointer;
MBLKP
message_ptr;
The qreply interface sends a message on a stream in the opposite direction from the queue specified in the queue_pointer argument. It calls the OTHERQ interface to find the queue's module partner.
None
Finds the number of messages on a queue
#include <sys/stream.h>
int qsize
(queue_pointer)
queue_t
*queue_pointer;
The qsize interface evaluates the queue associated with the queue_pointer argument and returns the number of messages it contains.
If there are no messages in the queue, qsize returns the value zero (0). Otherwise, it returns the integer representing the number of messages on the queue.
Initializes the specified queue
void queue_init
(queue_pointer)
queue_t
queue_pointer;
The queue_init interface initializes the specified queue. Device drivers call this interface prior to calling select_enqueue to initialize the links member of the sel_queue data structure. This member specifies a queue_entry structure. This structure contains a generic doubly linked list (queue).
None
select_enqueue, select.h, sel_queue
Gets a pointer to a module's read queue
#include <sys/stream.h>
queue_t * RD
(queue_pointer)
queue_t
*queue_pointer;
The RD interface accepts a write queue pointer as an argument and returns a pointer to the read queue of the same module.
Make sure the queue_pointer argument is a pointer to a write queue. The RD interface does not check for queue type. A system panic could occur if queue_pointer is not a write queue.
Upon successful completion, RD returns the pointer to the read queue.
Perform byte, word, longword, and quadword bus I/O read operations
unsigned char READ_BUS_D8
(dev_addr)
io_handle_t
dev_addr;
unsigned short READ_BUS_D16
(dev_addr)
io_handle_t
dev_addr;
unsigned int READ_BUS_D32
(dev_addr)
io_handle_t
dev_addr;
unsigned long READ_BUS_D64
(dev_addr)
io_handle_t
dev_addr;
The READ_BUS_D8 interface (macro) reads a byte (8 bits) from a device register located in the bus I/O address space. The READ_BUS_D16 interface (macro) reads a word (16 bits) from a device register located in the bus I/O address space. The READ_BUS_D32 interface (macro) reads a longword (32 bits) from a device register located in the bus I/O address space. The READ_BUS_D64 interface (macro) reads a quadword (64 bits) from a device register located in the bus I/O address space. These are convenience interfaces that call read_io_port, which is a generic interface that maps to a bus- and machine-specific interface that actually performs the task of reading the byte, word, longword, or quadword. Use of these interfaces to read data from a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
In addition to dev_addr, the READ_BUS_D8, READ_BUS_D16, READ_BUS_D32, and READ_BUS_D64 interfaces automatically pass values to the width and flags arguments of read_io_port. The following list identifies these values:
The I/O handle that you pass to the dev_addr argument of the READ_BUS_D8, READ_BUS_D16, READ_BUS_D32, and READ_BUS_D64 interfaces must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the dev_addr argument that references addresses residing in some other space (for example, dense space) the results of the read operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
The read_io_port and write_io_port interfaces (and by extension, the Digital-supplied macros built from these interfaces) do not support unaligned data accesses that cross longword boundaries. You can access unaligned data by providing an interface (macro) that checks the lower bits of an I/O address to determine the byte boundary of the I/O read or write operation to be performed and the width of the data to be read or written. If an alignment problem exists, you can break up the read or write operation into separate byte-size reads or writes.
The READ_DEVICECSR_USHORT example interface is a macro that reads an unsigned word of data from a device register. The READ_DEVICECSR_USHORT interface (macro) is called instead of directly calling the Digital-supplied READ_BUS_D16 interface (macro). The READ_DEVICECSR_USHORT interface (macro) first masks out the lower 2 bits of the base address. If the lower 2 bits are both high (indicating an address on a tribyte boundary), the driver must break up the read operation into 2-byte read operations. The driver must also perform appropriate bit-shifting operations to read high and low bytes that are then ORed together.
#define READ_DEVICECSR_USHORT(a) ( (u_short)( (((u_short)(a)&3) == 3) /* (((u_short)(a)&1) == 1) This can be used in drivers with 16-bit CSRs to check if a word read operation crosses a 16-bit register boundary. */ ? ( (READ_BUS_D8( (io_handle_t)sc->regbase + (a)+1) << 8) | READ_BUS_D8( (io_handle_t)sc->regbase + (a) ) ) : ( READ_BUS_D16((io_handle_t)sc->regbase + (a)) ); )
Upon successful completion, these interfaces return the requested data from the device register located in the bus address space: READ_BUS_D8 returns a byte (8 bits), READ_BUS_D16 returns a word (16 bits), READ_BUS_D32 returns a longword (32 bits), and READ_BUS_D64 returns a quadword (64 bits).
Reads a disk label from a device
char * readdisklabel
(dev,
strategy,
lp)
dev_t
dev;
int
(*strategy) ();
register struct disklabel
*lp;
The readdisklabel interface reads a disklabel structure for a given device, using the device driver's strategy interface to perform the read.
The device driver's open interface calls readdisklabel to read the disk label from the disk into memory.
See Writing Device Drivers: Advanced Topics for a code example of the readdisklabel interface.
Upon successful completion, readdisklabel returns the NULL string. Otherwise, it returns one of the following messages:
disklabel, setdisklabel, writedisklabel
Reads data from a device register
#include <io/common/devdriver.h>
long read_io_port
(dev_addr,
width,
flags)
io_handle_t
dev_addr;
int
width;
int
flags;
The read_io_port interface reads data of the specified width from a device register located in bus address space. The I/O handle you pass to dev_addr identifies where the read operation originates.
The read_io_port interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the read operation. Using this interface to read data from a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
You must call the mb interface immediately after calling the read_io_port interface under certain circumstances. For discussions and examples of these circumstances, see the Memory Barrier Issues section in Writing Device Drivers: Tutorial.
The I/O handle that you pass to the dev_addr argument of the read_io_port interface must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the dev_addr argument that references addresses residing in some other space (for example, dense space) the results of the read operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
The read_io_port and write_io_port interfaces (and by extension, the Digital-supplied macros built from these interfaces) do not support unaligned data accesses that cross longword boundaries. You can access unaligned data by providing an interface (macro) that checks the lower bits of an I/O address to determine the byte boundary of the I/O read or write operation to be performed and the width of the data to be read or written. If an alignment problem exists, you can break up the read or write operation into separate byte-size reads or writes.
The READ_DEVICECSR_USHORT example interface is a macro that reads an unsigned word of data from a device register. The READ_DEVICECSR_USHORT interface (macro) is called instead of directly calling the Digital-supplied READ_BUS_D16 interface (macro). The READ_DEVICECSR_USHORT interface (macro) first masks out the lower 2 bits of the base address. If the lower 2 bits are both high (indicating an address on a tribyte boundary), the driver must break up the read operation into 2-byte read operations. The driver must also perform appropriate bit-shifting operations to read high and low bytes that are then ORed together.
#define READ_DEVICECSR_USHORT(a) ( (u_short)( (((u_short)(a)&3) == 3) /* (((u_short)(a)&1) == 1) This can be used in drivers with 16-bit CSRs to check if a word read operation crosses a 16-bit register boundary. */ ? ( (READ_BUS_D8( (io_handle_t)sc->regbase + (a)+1) << 8) | READ_BUS_D8( (io_handle_t)sc->regbase + (a) ) ) : ( READ_BUS_D16((io_handle_t)sc->regbase + (a)) ); )
See Writing Device Drivers: Tutorial for a code example of the read_io_port interface.
Upon successful completion, read_io_port returns the requested data from the device register located in the bus address space: a byte (8 bits), a word (16 bits), a longword (32 bits), or a quadword (64 bits). This interface returns data justified to the low-order byte lane. For example, a byte (8 bits) is always returned in byte lane 0 and a word (16 bits) is always returned in byte lanes 0 and 1.
Registers a callback request (interface)
int register_callback
(function,
point,
order,
argument)
void
(*function) ();
int
point;
int
order;
ulong
argument;
The register_callback interface registers a device driver's xxcallback interface. The kernel calls a driver's xxcallback interface when execution reaches the point specified in the point and order arguments. The kernel passes the values specified in the point and argument arguments to the driver's xxcallback interface. Device drivers implement one or more xxcallback interfaces to handle the different dispatch points in the boot path.
The dispatch point constants you can pass to the point argument are defined in the /usr/sys/include/sys/sysconfig.h file. The following table lists the dispatch point constants that device drivers can use:
Value | Meaning |
CFG_PT_PRECONFIG | The dispatch point is hardware preconfiguration. Driver tasks that do not require completion of hardware configuration can be performed at this dispatch point. |
CFG_PT_POSTCONFIG | The dispatch point is hardware post configuration. Driver tasks that require completion of hardware configuration can be performed at this dispatch point. |
CFG_PT_ROOTFS_AVAIL | The dispatch point is root file system available. Driver tasks that require completion of the root file system mount operation can be performed at this dispatch point. |
The order constants you can pass to the order argument are defined in the /usr/sys/include/sys/sysconfig.h file. The following table lists the order constants that device drivers can use:
Value | Meaning |
CFG_ORD_NOMINAL | This callback request (interface) is registered at the lowest priority. Typically, you pass this constant with an appropriate offset. The kernel executes callback requests (interfaces) registered at this priority last. |
CFG_ORD_MAXIMUM | This callback request (interface) is registered at the highest priority. Typically, you pass this constant with an appropriate offset. The kernel executes callback requests (interfaces) registered at this priority first. |
The kernel maintains an internal callback list that stores the values you pass to the register_callback interface. These callbacks remain registered until the user removes them.
Only statically configured device drivers need to implement xxcallback interfaces. Thus, only statically configured device drivers need to call the register_callback interface.
You typically call the cfgmgr_get_state interface to determine if the device driver is in the dynamic configuration state or the static configuration state.
See Writing Device Drivers: Tutorial for a code example of the register_callback interface.
Upon successful completion, the register_callback interface returns the value ESUCCESS. Otherwise, register_callback returns one of the following error constants defined in /usr/sys/include/sys/errno.h:
cfgmgr_get_state, sysconfig.h, unregister_callback, xxcallback, xxconfigure
Allocates size units from the given resource map
long rmalloc
(map_struct,
size)
struct map
*map_struct;
long
size;
The rmalloc interface allocates size units from the given resource map. In a map, the addresses are increasing, and the list is terminated by a zero size. The actual units managed by the map are arbitrary and can be map registers, bytes, blocks, and so forth.
The caller is responsible for providing any locking necessary for the map structure that the system passes to this interface.
The rmalloc interface returns the base of the allocated space. The interface returns an error if no space could be allocated.
Frees space previously allocated into the specified resource map
void rmfree
(map_struct,
size,
addr)
struct map
*map_struct;
long
size;
long
addr;
The rmfree interface frees the space previously allocated with a call to rmalloc. The rmfree interface frees a space of the size specified by the size argument at the address specified by the addr argument.
The caller is responsible for providing any locking necessary for the map structure that the system passes to this interface.
None
Allocates size units from the given resource map
int rmget
(map_struct,
size,
addr)
struct map
*map_struct;
long
size;
long
addr;
The rmget interface allocates the number of units specified in size starting at the address specified in addr.
The caller is responsible for providing any locking necessary for the map structure that the system passes to this interface.
Upon successful completion, rmget returns the starting address, addr. Otherwise, it returns the value zero (0).
Initializes a resource map
void rminit
(map_struct,
size,
addr,
name,
mapsize)
struct map
*map_struct;
long
size;
long
addr;
char
*name;
int
mapsize;
The rminit interface initializes the specified resource map to have mapsize - 2 segments. The interface also identifies this resource map with the string passed to the name argument. It prints this name if the slots become so fragmented that space is lost.
The resource map itself is initialized with size elements free starting at the address specified in addr.
The caller is responsible for providing any locking necessary for the map structure that the system passes to this interface.
None
Removes a message block from a message block
#include <sys/stream.h>
MBLKP rmvb
(message_block_ptr,
message_to_be_removed)
MBLKP
message_block_ptr;
MBLKP
message_to_be_removed;
The rmvb interface removes a message block (the message_to_be_removed argument) from a message block (the message_block_ptr argument) and returns a pointer to the altered message block. The rmvb interface does not free the message block; it merely removes the message block. It is the module's or driver's responsibility to free the message block.
Upon successful completion, rmvb returns a pointer to the message block (minus the removed message block). The pointer is NULL if the message_to_be_removed argument was the only message block before the call to the rmvb interface. If the message block passed to message_to_be_removed does not exist, rmvb returns the value -1.
Removes a message block from a queue
#include <sys/stream.h>
void rmvq
(queue_pointer,
message_to_be_removed)
queue_t
*queue_pointer;
MBLKP
message_to_be_removed;
The rmvq interface removes a message block from a queue. You can remove a message block from anywhere on a queue. To prevent modules and drivers from having to deal with the internals of message linking on a queue, you can call either rmvq or getq to remove a message block from a queue.
Make sure the message block pointer you pass to the message_to_be_removed argument exists to avoid a possible system panic.
None
Rounds the specified address
#include <mach/vm_param.h>
vm_offset_t round_page
(address)
vm_offset_t
address;
The round_page interface rounds the specified address (or byte count) to a multiple of the page size. For example, round_page would round a 1-byte count to be equal to the size of one page. This interface shields the driver writer from having to know the page size of the system, which could vary in different CPU architectures and on different CPU types within the same architecture. Typically, a device driver calls round_page in preparation for doing a DMA operation to a user's buffer. The value returned by this interface is used in the call to the vm_map_pageable interface. To use this interface, the driver writer must include the <mach/vm_param.h> header file in the driver.
The round_page interface returns the rounded address or byte count.
current_task, trunc_page, vm_map_pageable
Removes the last kernel thread waiting for an event
void select_dequeue
(selq)
sel_queue_t
*selq;
The select_dequeue interface removes the last kernel thread waiting for an event to occur on the specified device. This interface is called to terminate a select call. Typically, a driver's xxselect interface calls select_dequeue when the kernel sets the scanning argument (for the driver's xxselect interface) to the value zero (0). This value causes the kernel to unblock any kernel threads suspended when selecting events for this device.
None
poll.h, select.h, select_dequeue_all, select_enqueue, sel_queue, xxselect
Reference Pages Section 2: select
Removes all kernel threads waiting for an event
void select_dequeue_all
(selq)
sel_queue_t
*selq;
The select_dequeue_all interface is similar in functionality to the select_dequeue interface. The difference is that select_dequeue_all removes all kernel threads (not just the last one) while waiting for an event on the specified device.
None
poll.h, select.h, select_dequeue, select_enqueue, sel_queue, xxselect
Reference Pages Section 2: select
Adds the current kernel thread
void select_enqueue
(selq)
sel_queue_t
*selq;
The select_enqueue interface adds the current kernel thread to the list of kernel threads waiting for a select event on the specified device. This interface is called when a driver's xxselect interface has been called and the requested event cannot be immediately satisfied. For example, the requested event cannot be immediately satisfied when xxselect is called for the following reasons:
By calling select_enqueue, the driver's xxselect interface ensures that the kernel thread issuing the select call will be blocked until the requested event can be satisfied or the select call terminates.
You must call the queue_init interface to initialize the sel_queue structure pointer prior to calling select_enqueue. Failure to do so causes the kernel to panic.
None
poll.h, queue_init, select.h, select_dequeue, select_dequeue_all, sel_queue, xxselect
Reference Pages Section 2: select
Wakes up a kernel thread
void select_wakeup
(selq)
sel_queue_t
*selq;
The select_wakeup interface wakes up a kernel thread that is suspended while waiting for an event on the specified device. A user-level process can use the select system call to cause the process to be suspended while waiting for an event to happen on a device. For example, a graphics application may issue a select call while waiting for mouse or keyboard input to arrive. In this case the process would issue the select system call, which would indirectly call the graphics driver's select interface (through the driver's select entry point in the dsent table) to determine if any input is available. If input is available, the select call may return immediately. If no input is currently available, the graphics driver would suspend the process until input arrived.
For this example, when the graphics driver has received input (typically through its interrupt handler), it causes any processes suspended from calling select to continue by calling the select_wakeup interface. This causes any process currently suspended on the select channel (as specified by the selq argument) to resume.
None
select_dequeue, select_dequeue_all, select_enqueue, xxselect
Reference Pages Section 2: select
Sets a disk label
int setdisklabel
(old_lp,
new_lp,
openmask)
register struct disklabel
*old_lp;
register struct disklabel
*new_lp;
u_int
openmask;
The setdisklabel interface sets the in-memory copy of the disklabel structure to the new values specified by the user.
The DIOCWDINFO and DIOCSDINFO ioctl commands call setdisklabel to set the in-memory disk label and to check the validity of the new settings.
See Writing Device Drivers: Advanced Topics for a code example of the setdisklabel interface.
Upon successful completion, setdisklabel returns the value zero (0). Otherwise, it returns EBUSY if:
disklabel, readdisklabel, writedisklabel
Asserts a simple lock
#include <kern/lock.h>
void simple_lock
(slock_ptr)
simple_lock_t
slock_ptr;
The simple_lock interface asserts a lock with exclusive access for the resource associated with the specified slock structure pointer. This means that no other kernel thread can gain access to the locked resource until you call simple_unlock to release it. Because simple locks are spin locks, simple_lock does not return until the lock has been obtained.
You must call simple_lock_init (once only) prior to calling simple_lock to initialize the simple lock structure for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the simple_lock interface.
None
decl_simple_lock_data, lock.h, simple_lock_init, simple_lock_terminate, simple_lock_try, simple_unlock, slock
Initializes a simple lock structure
#include <kern/lock.h>
void simple_lock_init
(slock_ptr)
simple_lock_t
slock_ptr;
The simple_lock_init interface initializes the simple lock structure that you previously declared with the decl_simple_lock_data interface. You need to initialize the simple lock structure only once. After you initialize the simple lock structure, you can call simple_lock to assert exclusive access on the associated resource.
See Writing Device Drivers: Advanced Topics for a code example of the simple_lock_init interface.
None
decl_simple_lock_data, lock.h, simple_lock, simple_lock_terminate, simple_lock_try, simple_unlock, slock
Terminates, using a simple lock
#include <kern/lock.h>
void simple_lock_terminate
(slock_ptr)
simple_lock_t
slock_ptr;
The simple_lock_terminate interface determines that the driver is done using the simple lock permanently. The simple lock must be free (that is, the driver does not hold the lock) before calling simple_lock_terminate. The device driver must not reference the specified simple lock after calling simple_lock_terminate.
You must call simple_lock_init (once only) prior to calling simple_lock_terminate to initialize the simple lock structure for the resource. A resource, from the device driver's standpoint, is data that more than one kernel thread can manipulate. You can store the resource in variables (global) and data structure members.
See Writing Device Drivers: Advanced Topics for a code example of the simple_lock_terminate interface.
None
decl_simple_lock_data, simple_lock_init, lock.h, simple_lock_try, simple_unlock, slock
Tries to assert a simple lock
#include <kern/lock.h>
boolean_t simple_lock_try
(slock_ptr)
simple_lock_t
slock_ptr;
The simple_lock_try interface tries to assert a lock with read and write access for the resource associated with the specified simple lock. The main difference between this interface and simple_lock is that simple_lock_try returns immediately if the resource is already locked while simple_lock spins until the lock has been obtained. Thus, call simple_lock_try when you need a simple lock but the code cannot spin until the lock is obtained.
To release a simple lock successfully asserted by simple_lock_try, call the simple_unlock interface.
See
Writing Device Drivers: Advanced Topics
for a code example of the
simple_lock_try
interface.
The simple_lock_try interface returns one of the following values:
Value | Meaning |
TRUE | The simple_lock_try interface successfully asserted the simple lock. |
FALSE | The simple_lock_try interface failed to assert the simple lock. |
decl_simple_lock_data, lock.h, simple_lock, simple_lock_init, simple_lock_terminate, simple_unlock, slock
Releases a simple lock
#include <kern/lock.h>
void simple_unlock
(slock_ptr)
simple_lock_t
slock_ptr;
The simple_unlock interface releases a simple lock for the resource associated with the specified simple lock structure pointer. This simple lock was previously asserted by calling the simple_lock or simple_lock_try interface.
See Writing Device Drivers: Advanced Topics for a code example of the simple_unlock interface.
None
decl_simple_lock_data, lock.h, simple_lock, simple_lock_terminate, simple_lock_try, slock
Puts a calling process to sleep
void sleep
(channel,
pri)
caddr_t
channel;
long
pri;
The sleep interface puts a calling process to sleep on the address specified by the channel argument. Some common addresses are the lbolt argument, a buf structure, and a proc structure. This address should be unique to prevent unexpected wake/sleep cycles, which can occur if different processes are sleeping on the same address accidentally. If you set the PCATCH flag in the pri argument, the sleep interface puts signals on the queue and does not wake up the sleeping process.
The sleep and wakeup interfaces block and then wake up a process. Generally, device drivers call these interfaces to wait for the transfer to complete an interrupt from the device. That is, the write interface of the device driver sleeps on the address of a known location, and the device's interrupt service interface wakes the process when the device interrupts. It is the responsibility of the wakened process to check if the condition for which it was sleeping has been removed.
Digital UNIX provides two ways to put a process to sleep: interruptible and uninterruptible. The sleep interface performs an uninterruptible sleep operation if you do not set the PCATCH flag and an interruptible sleep operation if you set the PCATCH flag. This means that device drivers cannot call sleep at interrupt context because at interrupt context there is no calling process to be put to sleep. Thus, a device driver's Interrupt Service Interface (ISI) and those interfaces called from within the ISI must not call the sleep interface.
On the Digital UNIX operating system you cannot use pri to set the scheduling priority of the calling process.
See Writing Device Drivers: Tutorial for a code example of the sleep interface.
None
Sets the processor priority to mask different levels of interrupts
#include <machine/cpu.h>
int getspl
();
int splbio
();
int splclock
();
int spldevhigh
();
int splextreme
();
int splhigh
();
int splimp
();
int splnet
();
int splnone
();
int splsched
();
int splsoftclock
();
int spltty
();
int splvm
();
int splx
(x)
int
x;
The spl interfaces set the CPU priority to various interrupt levels. The current CPU priority level determines which types of interrupts are masked (disabled) and which are unmasked (enabled). Historically, seven levels of interrupts were supported, with eight different spl interfaces to handle the possible cases. For example, calling spl0 would unmask all interrupts and calling spl7 would mask all interrupts. Calling an spl interface between 0 and 7 would mask all interrupts at that level and at all lower levels.
Specific interrupt levels were assigned for different device types. For example, before handling a given interrupt, a device driver would set the CPU priority level to mask all other interrupts of the same level or lower. This setting meant that the device driver could be interrupted only by interrupt requests from devices of a higher priority.
Digital UNIX currently supports the naming of spl interfaces to indicate the associated device types. Named spl interfaces make it easier to determine which interface you should use to set the priority level for a given device type. The following table summarizes the uses for the different spl interfaces:
spl Interface | Meaning |
getspl | Gets the spl value. |
splbio | Masks all disk and tape controller interrupts. |
splclock | Masks all hardware clock interrupts. |
spldevhigh | Masks all device and software interrupts. |
splextreme | Blocks against all but halt interrupts. |
splhigh | Masks all interrupts except for realtime devices, machine checks, and halt interrupts. |
splimp | Masks all Ethernet hardware interrupts. |
splnet | Masks all network software interrupts. |
splnone | Unmasks (enables) all interrupts. |
splsched | Masks all scheduling interrupts (usually the hardware clock). |
splsoftclock | Masks all software clock interrupts. |
spltty | Masks all tty (terminal device) interrupts. |
splvm | Masks all virtual memory clock interrupts. |
splx | Resets the CPU priority to the level specified by the argument. |
The binding of any spl interface with a specific CPU priority level is highly machine dependent. With the exceptions of the splhigh and splnone interfaces, knowledge of the explicit bindings is not required to create new device drivers. You always use splhigh to mask (disable) all interrupts and splnone to unmask (enable) all interrupts.
The following code fragment shows the use of spl interfaces as part of a disk strategy interface:
int s; . . . s = splbio(); /* Mask (disable) all disk interrupts */ . . . [Code to deal with data that can be modified by the disk interrupt code] . . . splx(s); /* Restore CPU priority to what it was */
Upon successful completion, each spl interface returns an integer value that represents the CPU priority level that existed before it was changed by a call to the specified spl interface.
Compares two null-terminated character strings
int strcmp
(s1,
s2)
char
*s1;
char
*s2;
The strcmp interface lexicographically compares string s1 to string s2. The interface does not continue the comparison beyond the first null character it finds. A fatal error occurs if you call strcmp with strings that are not null terminated.
See Writing Device Drivers: Tutorial for a code example of the strcmp interface.
If string s1 is less than string s2, strcmp returns an integer less than zero. If s1 and s2 are equal, the interface returns the value zero (0). If s1 is greater than s2, it returns an integer greater than zero.
Copies a null-terminated character string
char * strcpy
(s1,
s2)
char
*s1;
char
*s2;
The strcpy interface copies string s2 to buffer s1. The interface stops copying after it copies a null character. Note that the character size is 1 byte.
See Writing Device Drivers: Tutorial for a code example of the strcpy interface.
The interface returns a pointer to the location following the end of the destination buffer, s1.
bcopy, blkclr, copystr, ovbcopy, strncpy
Returns the number of characters in a null-terminated string
The strlen interface returns the number of characters in s. The count does not include the terminating null character.
Note that the character size is 1 byte.
See Writing Device Drivers: Tutorial for a code example of the strlen interface.
The strlen interface returns the number of characters in s, not counting the terminating null character.
Submits messages for logging
#include <sys/stream.h>
#include <sys/strlog.h>
int strlog
(driverid,
minorid,
level,
flags,
format,
arg1,
...)
short
driverid;
short
minorid;
char
level;
unsigned short
flags;
char
*format;
unsigned
arg1;
Value | Meaning |
SL_ERROR | Pass the message to the error logger. |
SL_TRACE | Pass the message to the tracer. |
SL_CONSOLE | Print the message on the console terminal. |
The strlog interface submits formatted messages to the log driver. You can retrieve the messages with the getmsg system call. The flags argument specifies the type of message and where it is to be sent. The strace command receives messages from the log driver and sends them to the standard output. The strerr daemon receives error messages from the log driver and appends them to a file called /var/adm/streams/error.mm-dd , where mm-dd identifies the date of the error message.
The strlog interface returns the value zero (0) if the message is not seen by all the readers. Otherwise, it returns the value 1.
Compares two strings, using a specified number of characters
int strncmp
(s1,
s2,
n)
char
*s1;
char
*s2;
int
n;
The strncmp interface compares string s1 to string s2, using the number of characters specified in n.
See Writing Device Drivers: Tutorial for a code example of the strncmp interface.
If string s1 is equal to the null character ( \0 ), strncmp returns the value zero (0). Otherwise, it returns the difference between the number of characters in s1 and s2.
Copies a null-terminated character string with a specified limit
char * strncpy
(s1,
s2,
n)
char
*s1;
char
*s2;
int
n;
The strncpy interface copies string s2 to buffer s1. The interface stops copying after it copies a null character or n characters, whichever comes first. If the length of s2 as determined by the null character is less than n, the interface pads s1 with null characters.
See Writing Device Drivers: Tutorial for a code example of the strncpy interface.
The strncpy interface returns a pointer to /NULL at the end of the first string (or to the location following the last copied character if there is no NULL). The copied string will not be null terminated if the length of s2 is n characters or more.
bcopy, blkclr, copystr, ovbcopy, strcpy
Gets information about a queue
#include <sys/stream.h>
int strqget
(queue_pointer,
what,
pri,
val)
queue_t
*queue_pointer;
qfields_t
what;
unsigned char
pri;
long
*val;
The strqget interface gives modules and drivers a way to obtain information about a queue or a particular band of a queue without directly accessing STREAMS data structures. The values that can be returned are defined in the following enumerated data type:
typedef enum qfields { QHIWAT = 0, QLOWAT = 1, QMAXPSZ = 2, QMINPSZ = 3, QCOUNT = 4, QFIRST = 5, QLAST = 6, QFLAG = 7, QBAD = 8 } qfields_t;
Upon successful completion, strqget returns the value zero (0). On failure, strqget returns an error number.
Changes information about a queue
#include <sys/stream.h>
int strqset
(queue_pointer,
what,
pri,
val)
queue_t
*queue_pointer;
qfields_t
what;
unsigned char
pri;
long
*val;
The strqset interface gives modules and drivers a way to change information about a queue or a particular band of a queue without directly accessing STREAMS data structures. The values that can be returned are defined in the following enumerated data type:
typedef enum qfields { QHIWAT = 0, QLOWAT = 1, QMAXPSZ = 2, QMINPSZ = 3, QCOUNT = 4, QFIRST = 5, QLAST = 6, QFLAG = 7, QBAD = 8 } qfields_t;
Upon successful completion, strqset returns the value zero (0). On failure, strqset returns an error number. If the what argument is read-only, strqset returns the error constant EPERM.
Writes a byte into user address space
int subyte
(user_dest,
byte)
char
*user_dest;
char
byte;
The subyte interface copies 1 byte from the protected kernel address space to the unprotected user address space.
Upon successful completion, subyte returns the value zero (0). Otherwise, it returns a -1, indicating that the user address specified in user_dest could not be accessed.
copyout, copyoutstr, fubyte, fuword, suword
Writes a byte into user instruction address space
The suibyte interface is for use on machines that have separate address spaces for code and data. Because the Alpha architecture does not have separate address spaces for code and data, suibyte is mapped to subyte. The suibyte interface is provided for compatibility reasons. See the interface description for subyte for a complete description of suibyte.
Writes a word into user instruction address space
The suiword interface is for use on machines that have separate address spaces for code and data. Because the Alpha architecture does not have separate address spaces for code and data, suiword is mapped to suword. The suiword interface is provided for compatibility reasons. See the interface description for suword for a complete description of suiword.
Checks whether the current user is the superuser
#include <sys/proc.h>
#include <sys/acct.h>
int suser
(cred,
ac_flag)
struct u_cred
*cred;
struct flag_field
*ac_flag;
The suser interface checks whether the current user is the superuser. If the test succeeds and ac_flag is not a null pointer, the ASU flag is set in the flag_field structure pointed to by ac_flag. The most common value for ac_flag is as follows:
&u.u_acflag
You use the suser interface only if the security feature is not enabled. If the security feature is enabled, use the privileged interface to determine if the current process has the appropriate privilege.
If the current user is the superuser, the suser interface returns the value zero (0). Otherwise, it returns an error.
Writes a word into user address space
int suword
(user_dest,
word)
char
*user_dest;
int
word;
The suword interface copies one word from the protected kernel address space to the unprotected user address space.
Upon successful completion, suword returns the value zero (0). Otherwise, it returns a -1, indicating that the user address specified in user_dest could not be accessed.
copyout, copyoutstr, fubyte, fuword, subyte
Converts a system virtual address to a physical address
kern_return_t svatophys
(kern_addr,
phys_addr)
vm_offset_t
kern_addr;
vm_offset_t
*phys_addr;
The svatophys interface converts a system virtual address to the corresponding physical address. All address and data structure manipulation done within the kernel is performed using system virtual addresses. Typically, system virtual addresses are a means of mapping physical memory and I/O space, which often consists of device registers and DMA buffers. In contrast to this, devices are usually unaware of any virtual addressing and for this reason utilize physical addresses. You use the svatophys interface to perform this address translation.
As an example of where you can use this address translation, a disk device driver can utilize DMA buffers to transfer blocks of data to the disk (for the case of a write operation). The data to be written to disk is present in system memory at a system virtual address known to the driver. To initiate the DMA operation, the disk driver can set up a command packet to specify a write operation to the underlying disk controller hardware. This write command packet contains (among other things) the location of the DMA buffer as a physical address and the length of the buffer. Here, the driver calls the svatophys interface to translate the system virtual address of the DMA buffer to a physical address in the command packet issued to the disk driver.
The svatophys interface returns the following:
Perform byte-swapping operations
unsigned int swap_lw_bytes
(buffer)
unsigned int
buffer;
unsigned int swap_word_bytes
(buffer)
unsigned int
buffer;
unsigned int swap_words
(buffer)
unsigned int
buffer;
The swap_lw_bytes interface performs a longword byte swap. The swap_word_bytes interface performs a short word byte swap. The swap_words interface performs a word byte swap. Many computer vendors support devices that use a big endian model of byte ordering. Because Digital devices support the little endian model of byte ordering, there is a need for these byte-swapping interfaces. In addition, some buses (for example, the VMEbus) can have specific or implied byte ordering that may require the use of these interfaces.
Figure 2-1 compares the byte swapping performed by these interfaces. For the purposes of the following discussion, a longword is equal to 4 bytes; a short word is equal to 2 bytes; and 1 byte is equal to 8 bits. The swap_lw_bytes interface takes the 32-bit quantity specified by the buffer argument and swaps all 4 bytes. The swap_word_bytes interface takes the 32-bit quantity specified by the buffer argument and swaps the individual bytes that make up each word of the 32-bit quantity. The swap_words interface takes the 32-bit quantity specified by the buffer argument and swaps the two 16-bit words.
Upon successful completion, these interfaces return the result of the byte swapping.
Checks for an available buffer
#include <sys/stream.h>
int testb
(size,
pri)
int
size;
uint
pri;
The testb interface determines if a call to the allocb interface is likely to succeed if a buffer of size bytes at priority pri is requested. Even if testb returns successfully, the call to allocb can fail.
If a buffer of the requested size is available, testb returns the value 1. If a buffer of the requested size is not available, testb returns the value zero (0).
Blocks (puts to sleep) the current kernel thread
None
The thread_block interface blocks (puts to sleep) the current kernel thread and selects the next kernel thread to start (run). The interface schedules the next kernel thread onto this CPU.
When a kernel thread is waiting for an event, it should block. In general, a kernel thread is blocked when the thread wait bit in the thread structure pointer associated with the current kernel thread is set. This bit signifies that this kernel thread is on the appropriate wait hash queue, waiting for a wakeup call. This allows the CPU to execute another kernel thread while this kernel thread is blocked.
See Writing Device Drivers: Advanced Topics for a code example of the thread_block interface.
None
assert_wait_mesg, current_thread
Handles asynchronous traps for self-terminating kernel threads
None
The thread_halt_self interface performs the work associated with a variety of asynchronous traps (ASTs) for a kernel thread that terminates itself. A kernel thread terminates itself (or one kernel thread terminates another kernel thread) by calling the thread_terminate interface.
The thread_halt_self interface examines the AST-related member of the thread structure pointer associated with the kernel thread that wants to terminate itself. This thread structure pointer was returned in a previous call to kernel_isrthread or kernel_thread_w_arg and passed by you to the thread_terminate interface. This AST-related member is set to a bit that identifies the specific AST trap associated with this kernel thread. Based on the AST bit set in this member, thread_halt_self does the appropriate cleanup work before the kernel thread exits from the kernel.
A kernel thread that terminates itself must call thread_halt_self immediately after the call to thread_terminate. The reason for this is that thread_terminate only prepares the self-terminating kernel thread to stop execution. The thread_halt_self interface completes the work needed to stop execution (by performing the appropriate cleanup work) of the self-terminating kernel thread.
See Writing Device Drivers: Advanced Topics for a code example of the thread_halt_self interface.
None
Sets a timer for the current kernel thread
void thread_set_timeout
(timeout_interval)
int
timeout_interval;
The thread_set_timeout interface must be called as follows:
The time you specify to wait for the event is automatically canceled when the kernel thread awakes.
See Writing Device Drivers: Advanced Topics for a code example of the thread_set_timeout interface.
None
assert_wait_mesg, current_thread, thread_block
Prepares to stop or stops execution of the specified kernel thread
kern_return_t thread_terminate
(thread_to_terminate)
thread_t
thread_to_terminate;
The thread_terminate interface prepares to stop or permanently stops execution of the specified kernel thread. You created and started this kernel thread in a previous call to the kernel_isrthread or kernel_thread_w_arg interface. These interfaces return a pointer to the thread structure associated with the newly created and started kernel thread. Device drivers use this pointer as a handle to identify the specific kernel thread that thread_terminate stops executing.
Typically, a kernel thread terminates itself. However, one kernel thread can terminate another kernel thread. A kernel thread that terminates itself must call thread_halt_self immediately after the call to thread_terminate. The reason for this is that thread_terminate only prepares the self-terminating kernel thread to stop execution. The thread_halt_self interface completes the work needed to stop execution (by performing the appropriate cleanup work) of the self-terminating kernel thread.
Specifically, the thread_terminate interface works as follows:
The thread_terminate interface stops execution of the specified kernel thread, frees any resources associated with that kernel thread, and thus makes the kernel thread unavailable. To make the kernel thread available again, you need to create it by calling kernel_isrthread or kernel_thread_w_arg.
The thread_terminate interface prepares to stop execution of the kernel thread that needs to terminate itself. The thread_halt_self interface completes the work needed to stop execution (by performing the appropriate cleanup work) of the self-terminating kernel thread. After you call these interfaces, the self-terminating kernel thread becomes unavailable until you create it again by calling kernel_isrthread or kernel_thread_w_arg.
You do not need to terminate every kernel thread that you create. You should not terminate a kernel thread that is waiting for some event. The basic rule is that you should terminate only those kernel threads that you do not need anymore. For example, if a dynamically configured device driver uses kernel threads, you should terminate them in the CFG_OP_UNCONFIGURE entry point of the loadable driver's configure interface. The kernel threads are no longer needed after the driver is unconfigured.
Note that the thread_terminate interface (for kernel threads that terminate other kernel threads) not only permanently stops execution of the specified kernel thread, but it also frees any resources associated with that kernel thread; thus, this kernel thread can no longer be used.
Upon successfully terminating the specified kernel thread, thread_terminate returns the constant KERN_SUCCESS. If the thread structure pointer passed to the thread_to_terminate argument does not identify a valid kernel thread, thread_terminate returns the constant KERN_INVALID_ARGUMENT. On any other error, thread_terminate returns the constant KERN_FAILURE.
kernel_isrthread, kernel_thread_w_arg, task, thread, thread_halt_self
Wakes up all kernel threads waiting for the specified event
void thread_wakeup
(event)
vm_offset_t
event;
The thread_wakeup interface wakes up all kernel threads waiting for the event specified in the event argument. This interface is actually a convenience wrapper for the thread_wakeup_prim interface with the one_thread argument set to FALSE (wake up all kernel threads) and the result argument set to THREAD_AWAKENED (wakeup is normal).
See Writing Device Drivers: Advanced Topics for a code example of the thread_wakeup interface.
None
assert_wait_mesg, clear_wait, thread_block, thread_wakeup_one
Wakes up the first kernel thread waiting on a channel
void thread_wakeup_one
(event)
vm_offset_t
event;
The thread_wakeup_one interface wakes up only the first kernel thread in the hash chain waiting for the event specified in the event argument. This interface is actually a convenience wrapper for the thread_wakeup_prim interface with the one_thread argument set to TRUE (wake up only the first kernel thread) and the result argument set to THREAD_AWAKENED (wakeup is normal).
See Writing Device Drivers: Advanced Topics for a code example of the thread_wakeup_one interface.
None
assert_wait_mesg, clear_wait, thread_block, thread_wakeup
Initializes a callout queue element
void timeout
(function,
argument,
time)
int
(*function) ();
caddr_t
argument;
register int
time;
The timeout interface initializes a callout queue element to make it easy to execute the specified interface at the time specified in the time argument. You often use callout interfaces for infrequent polling or error handling. The interface you specify will be called on the interrupt stack (not in processor context) as dispatched from the softclock interface.
The global variable hz contains the number of clock ticks per second. This variable is a second's worth of clock ticks.
Thus, if you wanted a 4-minute timeout, you would pass 4 * 60 * hz as the third argument to the timeout interface as follows:
/* A 4-minute timeout */ . . . timeout(lptout, (caddr_t)dev, 4 * 60 * hz);
The granularity of the time delay is dependent on the hardware. For example, the granularity of some Alpha CPUs is 1024 clock ticks per second. Other Alpha CPUs have a granularity of 1200 clock ticks per second. Still other Alpha CPUs exhibit a granularity of 128 clock ticks per second. Because the granularity of the time delay is dependent on the hardware, Digital provides the hz and lbolt global variables. Use the hz global variable to determine the number of clock ticks per second for a specific Alpha CPU. Use the lbolt global variable as a periodic wakeup mechanism.
See Writing Device Drivers: Tutorial for a code example of the timeout interface.
None
Truncates the specified address
#include <mach/vm_param.h>
vm_offset_t trunc_page
(address)
vm_offset_t
address;
The trunc_page interface truncates the specified address to be aligned on a page boundary. This interface shields the driver writer from having to know the page size of the system, which could vary in different CPU architectures and on different CPU types within the same architecture. Typically, a device driver calls trunc_page in preparation for doing a DMA operation on a user's buffer. The value returned by this interface is used in the call to the vm_map_pageable interface. To use this interface, the driver writer must include the <mach/vm_param.h> header file in the driver.
The trunc_page interface returns an address truncated to a page boundary.
current_task, round_page, vm_map_pageable
Moves data between user and system virtual space
#include <sys/uio.h>
int uiomove
(kern_buf,
nbytes,
uio)
caddr_t
kern_buf;
int
nbytes;
struct uio
*uio;
The uiomove interface moves data between user and system virtual space. Data can be moved in either direction. Accessibility to the logical user buffer is verified before the move is made. Accessibility to the kernel buffer is always assumed.
The kernel buffer must always be of sufficient size to accommodate the data. It cannot be less than the number of bytes requested to be moved. Data corruption or a system panic may result if this condition occurs.
The size of the logical user buffer, as described by the uio structure, may be less than, equal to, or greater than the number of bytes requested. The number of bytes actually moved is truncated whenever this size is not sufficient to fulfill a request. In all other cases, only the bytes requested are moved.
Normally there is no need for device drivers to set up uio structures or worry about their composition or content. The uio structures are usually set up externally to drivers. Their addresses are passed in through the dsent table as arguments to driver read and write interfaces. The user logical buffers they describe are accessed only by kernel interfaces external to the driver, for example, uiomove. The external uio structures are quite often updated by such accesses.
The uiomove interface always updates the uio structure to reflect the number of bytes actually moved. The structure continues to describe the current position within the logical user buffer. The structure members that are subject to change are listed in the Side Effects section.
You can also use the uiomove interface to move data only within system virtual space. In such cases, you still specify a pointer to a uio structure. However, in these cases, the structure describes a logical buffer in system virtual space.
See Writing Device Drivers: Tutorial for a code example of the uiomove interface.
The uiomove interface can update the following members of the uio structure:
The uiomove interface can update the following members of uio_iov (the logical buffer segment descriptor vector):
A zero (0) value is returned whenever the user virtual space described by the uio structure is accessible and the data is successfully moved. Otherwise, an EFAULT error value is returned, indicating an inability to fully access the user virtual space from within the context of the current process. A partial move may have occurred before the logical user buffer became inaccessible. The uio structure is appropriately updated to reflect such partial moves.
The EFAULT return value is suitable for placement in the u_error member of the user structure. Following failure of a system call, this member contains the error code automatically returned in errno to the current process. Device driver writers should explicitly set this value when it is returned and disallow the requested operation. This setting lets the current process determine the appropriate reason (``bad address'') why its request could not be satisfied.
copyin, copyout, fubyte, subyte, uio
Cancels a pending bufcall request
#include <sys/stream.h>
void unbufcall
(id)
int
id;
The unbufcall interface cancels a pending bufcall request. The id argument is a nonzero identifier for the request to be canceled. This nonzero identifier is returned to the module from a previous call to the bufcall interface.
The unbufcall interface will not return until the pending callback is canceled or has run. Because of this, locks acquired by the callback interface should not be held across the call to unbufcall or a deadlock condition can occur.
None
Unconfigures a device driver that was dynamically configured
int unconfigure_driver
(bus_name,
bus_num,
driver_struct,
target_name,
target_num)
char
*bus_name;
int
bus_num;
struct driver
*driver_struct;
char
*target_name;
int
target_num;
The unconfigure_driver interface unconfigures a device driver that was dynamically configured into the kernel from the system (hardware) configuration tree. The unconfiguration tasks consist of marking the bus, controller, and device structures associated with the specific device driver as unused and available. The unconfigure_driver interface does not remove these structures from the hardware topology tree.
See Writing Device Drivers: Tutorial for a code example of the unconfigure_driver interface.
The unconfigure_driver interface can return one of the following constants defined in /usr/sys/include/sys/errno.h:
Forces execution onto the master CPU
None
The unix_master interface forces execution of the kernel thread onto the master CPU (also called the boot CPU). In other words, unix_master binds the kernel thread to the master CPU. To release the kernel thread from the bind to the master CPU, call the unix_release interface. You can make recursive calls to unix_master as long as you make an equal number of calls to unix_release.
The unix_master interface provides another way besides the simple and complex lock interfaces to make a device driver symmetric multiprocessing (SMP) safe. Although calling unix_master is not optimal for performance on an SMP CPU, it does provide third-party device driver writers with an easy way to make their drivers SMP safe without using the lock interfaces.
Device drivers should not directly call the
unix_master
and
unix_release
interfaces.
One exception to this recommendation is when you want a device driver's
kernel threads to run only on the master CPU.
This situation occurs when your driver creates and starts its own
kernel threads and you set
the
d_funnel
member of the
dsent
or
dsent
table to the value
DEV_FUNNEL.
In this case, each kernel thread
must call
unix_master
once to ensure
that the kernel thread runs only on the master CPU.
Remember to make a corresponding call to
unix_release.
To avoid deadlock, do not call the unix_master interface under the following circumstances:
None
Releases binding of the kernel thread
None
The unix_release interface releases a kernel thread from being bound to the master CPU. This binding was enforced in a previous call to the unix_master interface.
You can make recursive calls to unix_master as long as you make an equal number of calls to unix_release.
None
Removes a message block from the head of a message
#include <sys/stream.h>
MBLKP unlinkb
(message_ptr)
MBLKP
message_ptr;
The unlinkb interface removes the first message block from the message block pointed to by the message_ptr argument. The interface returns a pointer to the new message block, minus the removed message block.
Upon successful completion, unlinkb returns a pointer to the new message block with the first message block removed. If there is only one message block in the specified message block, unlinkb returns NULL.
Deregisters a callback request (interface)
int unregister_callback
(function,
point,
order,
argument)
void
(*function) ();
int
point;
int
order;
ulong
argument;
The unregister_callback interface deregisters a device driver's xxcallback interface. The device driver previously registered its xxcallback interface or interfaces by calling the register_callback interface. The values you pass to unregister_callback must be the same ones you previously passed to register_callback to deregister a specific xxcallback interface. Device drivers call unregister_callback to deregister the xxcallback interface when it is no longer needed.
A device driver typically calls unregister_callback when it encounters a fatal error during static or dynamic configuration. The cfgmgr framework executes xxcallback interfaces scheduled to run after single-user mode each time the system goes from multiuser mode to single-user mode and back to multiuser mode. You should unregister any xxcallback interfaces if you do not want this to occur.
The kernel maintains an internal callback list that stores the values you pass to the register_callback interface. These callbacks remain registered until the user removes them.
The unregister_callback interface searches through the callback list for the specified callback request (interface), the specified point, the specified order, and the specified argument and removes that request (interface) from the list.
Upon successful completion, the unregister_callback interface returns the value ESUCCESS. Otherwise, unregister_callback returns the following error constant defined in /usr/sys/include/sys/errno.h:
The callback you specified to be removed does not exist in the callback list.
register_callback, sysconfig.h, xxcallback, xxconfigure
Removes the scheduled interface from the callout queues
boolean_t untimeout
(function,
argument)
int
(*function) ();
caddr_t
argument;
The untimeout interface removes the scheduled interface from the callout queue. The specified interface was placed on the callout queue in a previous call to the timeout interface. The argument formal parameter must match the argument parameter you specified in the previous call to timeout.
If the specified interface is not in the callout queue, untimeout returns without removing any scheduled interfaces.
See Writing Device Drivers: Tutorial for a code example of the untimeout interface.
The untimeout interface returns the value TRUE on success and the value FALSE on failure.
Sets pageability of the specified address range
kern_return_t vm_map_pageable
(map,
start,
end,
access_type)
vm_map_t
map;
vm_offset_t
start;
vm_offset_t
end;
vm_prot_t
access_type;
Value | Meaning |
VM_PROT_NONE | Modifies the memory attributes so that the specified range of addresses is no longer locked. This should be done after the DMA operation has completed. |
VM_PROT_READ | Verifies that the specifed range of addresses are readable by the specified process. If so, the range of addresses are locked in memory to remain stable throughout the DMA operation. |
VM_PROT_WRITE | Verifies that the specifed range of addresses is writeable by the specified process. If so, the range of addresses are locked in memory to remain stable throughout the DMA operation. |
VM_PROT_READ | VM_PROT_WRITE | Verifies that the specifed range of addresses is readable and writeable by the specified process. If so, the range of addresses are locked in memory to remain stable throughout the DMA operation. |
The vm_map_pageable interface ensures that the address range you specified in the start and end arguments is accessible. If the address range is accessible by the specified process, the memory associated with this address range will have its locked attributes modified as specified by the access_type argument. A device driver can call this interface prior to performing a DMA operation to ensure that:
Historically, some ULTRIX device drivers used the useracc kernel interface to perform tasks similar to those performed by vm_map_pageable. For example, both useracc and vm_map_pageable verify that the currently running process has access permissions to the specified memory. However, vm_map_pageable locks the associated memory so that it remains available throughout the DMA operation. In contrast, useracc does not lock the corresponding memory. This leaves the memory vulnerable to having its access permissions changed, which could result in a system panic during the DMA operation.
The following code fragment shows how the vm_map_pageable interface ensures that the user's buffer is accessible to cause the corresponding memory to be locked:
if (vm_map_pageable(current_task()->map, trunc_page(bp->b_un.b_addr), round_page(bp->b_un.b_addr + (int)bp->b_bcount), (bp->b_flags == B_READ ? VM_PROT_READ : VM_PROT_WRITE))) { /*************************************************** * Here you implement the code to perform the * * actual DMA operation. Upon conclusion of the * * DMA operation, add the following code to * * release the locked attribute. * ***************************************************/ if (vm_map_pageable(current_task()->map, trunc_page(bp->b_un.b_addr), round_page(bp->b_un.b_addr + (int)bp->b_bcount), VM_PROT_NONE)) {
The following example shows a comparable use of a call to useracc. The Notes section provides reasons for not using this call on Digital UNIX systems.
if (useracc(bp->b_un.b_addr, (int)bp->b_bcount, (bp->b_flags & B_READ))) {
Upon successful completion, the vm_map_pageable interface returns the value zero (0). Otherwise, it returns a nonzero value to indicate an error.
current_task, round_page, trunc_page
Converts any virtual address to a physical address
vm_offset_t vtop
(proc_p,
virt_addr)
struct proc
*proc_p;
vm_offset_t
virt_addr;
The vtop interface converts a specified virtual address to a physical address.
The vtop interface panics and displays the following message on the console terminal if the proc structure pointer you pass is NULL and the virtual address is in user space:
vtop: user address passed with null proc pointer
Upon successful completion, vtop returns the physical address associated with the specified virtual address.
Wakes up all processes sleeping on a specified address
void wakeup
(channel)
caddr_t
channel;
The wakeup interface wakes up all processes sleeping on the address specified by the channel argument. All processes sleeping on this address are awakened and made scheduable according to the priorities they specified when they went to sleep. It is possible that there are no processes sleeping on the channel at the time the wakeup is issued. This situation can occur for a variety of reasons and does not represent an error condition.
The sleep and wakeup interfaces block and unblock a process. Generally, a device driver issues these interfaces on behalf of a process requesting I/O while a transfer is in progress. That is, a process requesting I/O is put to sleep on an address associated with the request by the appropriate device driver interface. When the transfer has asynchronously completed, the device driver interrupt service interface issues a wakeup on the address associated with the completed request. This action makes the relevant process scheduable.
The process resumes execution within the relevant device driver interface at the point immediately following the request to sleep. The driver, on behalf of the process, can then determine whether the condition for which it was sleeping (in this example completion of an I/O request) has been removed. If so, it can continue on to complete the I/O request. Otherwise, the appropriate driver interface can decide to put the process back to sleep to await removal of the indicated condition.
See Writing Device Drivers: Tutorial for a code example of the wakeup interface.
None
Gets a pointer to this module's write queue
#include <sys/stream.h>
queue_t * WR
(queue_pointer)
queue_t
*queue_pointer;
The WR interface accepts a read queue pointer as an argument and returns a pointer to the write queue of the same module.
Make sure the queue_pointer argument is a pointer to a read queue. The WR interface does not check for queue type. A system panic could occur if queue_pointer is not a read queue.
Upon successful completion, WR returns the pointer to the write queue.
Perform byte, word, longword, and quadword bus I/O write operations
void WRITE_BUS_D8
(dev_addr,
data)
io_handle_t
dev_addr;
long
data;
void WRITE_BUS_D16
(dev_addr,
data)
io_handle_t
dev_addr;
long
data;
void WRITE_BUS_D32
(dev_addr,
data)
io_handle_t
dev_addr;
long
data;
void WRITE_BUS_D64
(dev_addr,
data)
io_handle_t
dev_addr;
long
data;
The WRITE_BUS_D8 interface writes a byte (8 bits) to a device register located in the bus I/O address space. The WRITE_BUS_D16 interface writes a word (16 bits) to a device register located in the bus I/O address space. The WRITE_BUS_D32 interface writes a longword (32 bits) to a device register located in the bus I/O address space. The WRITE_BUS_D64 interface writes a quadword (64 bits) to a device register located in the bus I/O address space. These are convenience interfaces that call write_io_port, which is a generic interface that maps to a bus- and machine-specific interface that actually performs the task of writing the byte, word, longword, or quadword to a device register. Use of these interfaces to write data to a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
The WRITE_BUS_D8, WRITE_BUS_D16, WRITE_BUS_D32, and WRITE_BUS_D64 interfaces automatically pass values to the width and flags arguments of write_io_port. The following list identifies these values:
The I/O handle that you pass to the dev_addr argument of the WRITE_BUS_D8, WRITE_BUS_D16, WRITE_BUS_D32, and WRITE_BUS_D64 interfaces must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the dev_addr argument that references addresses residing in some other space (for example, dense space) the results of the copy operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
The read_io_port and write_io_port interfaces (and by extension, the Digital-supplied macros built from these interfaces) do not support unaligned data accesses that cross longword boundaries. You can access unaligned data by providing an interface (macro) that checks the lower bits of an I/O address to determine the byte boundary of the I/O read or write operation to be performed and the width of the data to be read or written. If an alignment problem exists, you can break up the read or write operation into separate byte-size reads or writes.
The WRITE_DEVICECSR_USHORT interface (macro) is an example of a support interface (macro) that writes an unsigned word of data to a device register. The WRITE_DEVICECSR_USHORT interface (macro) is called instead of directly calling the Digital-provided interface (macro) WRITE_BUS_D16. The WRITE_DEVICECSR_USHORT interface (macro) first masks out the lower 2 bits of the base address. If the lower 2 bits are both high (indicating an address on a tribyte boundary), the driver must break up the write operation into 2-byte write operations. The driver must also perform appropriate bit-shifting operations to write high and low bytes that are then ORed together.
#define WRITE_DEVICECSR_USHORT(a, data) ( (u_short)( (((u_short)(a)&3) == 3) /* (((u_short)(a)&1) == 1) This can be used in drivers with 16-bit CSRs to check if a word write operation crosses a 16-bit register boundary */ ? ( WRITE_BUS_D8( (io_handle_t)sc->regbase + (a), data), mb(), WRITE_BUS_D8( (io_handle_t)sc->regbase + (a)+1, (u_short)(data) >> 8), mb() ) : ( WRITE_BUS_D16( (io_handle_t)sc->regbase + (a), data), mb() ); )
None
Writes a disk label
int writedisklabel
(dev,
strategy,
lp)
dev_t
dev;
int
(*strategy) ();
register struct disklabel
*lp;
The writedisklabel interface writes a disklabel to a device, using the device driver's strategy interface.
The DIOCWDINFO ioctl command calls writedisklabel to write the disk label to disk. The device driver should first call setdisklabel to validate the new settings.
See Writing Device Drivers: Advanced Topics for a code example of the writedisklabel interface.
Upon successful completion, writedisklabel returns the value zero (0). Otherwise, it returns one of the following values:
disklabel, readdisklabel, setdisklabel
Writes data to a device register
#include <io/common/devdriver.h>
void write_io_port
(dev_addr,
width,
flags,
data)
io_handle_t
dev_addr;
int
width;
int
flags;
long
data;
The write_io_port interface writes the data of the specified width to the specified device register in bus address space. This interface shifts the data to the appropriate byte lanes before performing the write to bus address space. The I/O handle you pass to dev_addr identifies where the write operation occurs.
The write_io_port interface is a generic interface that maps to a bus- and machine-specific interface that actually performs the write operation. Using this interface to write data to a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
You must call the mb interface immediately after calling the write_io_port interface under certain circumstances. For discussions and examples of these circumstances, see the Memory Barrier Issues section in Writing Device Drivers: Tutorial.
The I/O handle that you pass to the dev_addr argument of the write_io_port interface must be an I/O handle that references addresses residing in sparse space. All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.
If you pass an I/O handle to the dev_addr argument that references addresses residing in some other space (for example, dense space) the results of the write operation are unpredictable.
Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:
Copies a series of bytes with a specified limit
Zeros a block of memory
Copies data from a user address space to a kernel address space
Copies a null-terminated string from a user address space to a kernel address space
Copies data from a kernel address space to a user address space
Copies a null-terminated string from a kernel address space to a user address space
The read_io_port and write_io_port interfaces (and by extension, the Digital-supplied macros built from these interfaces) do not support unaligned data accesses that cross longword boundaries. You can access unaligned data by providing an interface (macro) that checks the lower bits of an I/O address to determine the byte boundary of the I/O read or write operation to be performed and the width of the data to be read or written. If an alignment problem exists, you can break up the read or write operation into separate byte-size reads or writes.
The WRITE_DEVICECSR_USHORT interface (macro) is an example of a support interface (macro) that writes an unsigned word of data to a device register. The WRITE_DEVICECSR_USHORT interface (macro) is called instead of directly calling the Digital-provided interface (macro) WRITE_BUS_D16. The WRITE_DEVICECSR_USHORT interface (macro) first masks out the lower 2 bits of the base address. If the lower 2 bits are both high (indicating an address on a tribyte boundary), the driver must break up the write operation into 2-byte write operations. The driver must also perform appropriate bit-shifting operations to write high and low bytes that are then ORed together.
#define WRITE_DEVICECSR_USHORT(a, data) ( (u_short)( (((u_short)(a)&3) == 3) /* (((u_short)(a)&1) == 1) This can be used in drivers with 16-bit CSRs to check if a word write operation crosses a 16-bit register boundary */ ? ( WRITE_BUS_D8( (io_handle_t)sc->regbase + (a), data), mb(), WRITE_BUS_D8( (io_handle_t)sc->regbase + (a)+1, (u_short)(data) >> 8), mb() ) : ( WRITE_BUS_D16( (io_handle_t)sc->regbase + (a), data), mb() ); )
See Writing Device Drivers: Tutorial for a code example of the write_io_port interface.
None
When defining ioctl commands that your driver supports, use a set of macros defined in /usr/sys/include/sys/ioctl.h to construct these definitions. These macros instruct the kernel on how much data, if any, is transferred between the application program making the ioctl system call and the device driver. Table 2-3 describes the macros defined in /usr/sys/include/sys/ioctl.h as well as some ioctl commands defined in /usr/sys/include/sys/ioctl_compat.h.
ioctl Macro/Command | Summary Description |
DEVGETGEOM | Obtains device geometry information. |
DEVGETINFO | Obtains information about a device. |
DEVIOCGET | Obtains information about a device. |
DIOCGCURPT | Returns the current partition map for a disk. |
DIOCGDEFPT | Returns the default partition map for a disk. |
DIOCGDINFO | Returns a disk label. |
DIOCGPART | Returns information about a single disk partition. |
DIOCSDINFO | Sets a disklabel structure. |
DIOCWDINFO | Writes a disk label. |
DIOCWLABEL | Sets a write-enable label on a disk. |
_IO | Defines ioctl types for device control operations. |
_IOR | Defines ioctl types for device control operations. |
_IOW | Defines ioctl types for device control operations. |
_IOWR | Defines ioctl types for device control operations. |
Obtains device geometry information
#include <io/common/devio.h>
#include <sys/ioctl.h>
The DEVGETGEOM ioctl command obtains generic device geometry information by polling the underlying device driver. DEVGETGEOM uses the following union defined in the file /usr/sys/include/io/common/devio.h:
typedef union devgeom { struct { unsigned long dev_size; unsigned short ntracks; unsigned short nsectors; unsigned short ncylinders; unsigned long attributes; unsigned long sector_size; unsigned long min_trans; unsigned long max_trans; unsigned long prefer_trans; } geom_info; unsigned char pad[108]; } DEVGEOMST;
This union contains two members: a geom_info data structure and a pad array that allocates space to allow for future expansion. The following list describes the members of the geom_info structure:
The following code example shows how a disk device driver can use the DEVGETGEOM ioctl command. The example uses the attributes, dev_size, and sector_size members of the geom_info structure.
.
.
.
cdisk_ioctl(dev, cmd, data, flag) dev_t dev; /* major/minor number */ register int cmd; /* ioctl command */ caddr_t data; /* User data buffer - already copied in */ int flag; /* unused */
.
.
.
switch (cmd) {
.
.
.
/* * Disk geometry info. */ case DEVGETGEOM: { DEVGEOMST *devgeom = (DEVGEOMST *)data;
.
.
.
bzero((caddr_t)devgeom, sizeof(DEVGEOMST)); if(inqp->rmb) devgeom->geom_info.attributes |= DEVGEOM_REMOVE; /* * HSX00 and HSX01 FIB RAID devices are flagged * as having "dynamic geometry" because the * geometry of the underlying device can change * depending on the configuration of units. */ if ( dd->dd_flags & SZ_DYNAMIC_GEOM != 0) devgeom->geom_info.attributes |= DEVGEOM_DYNAMIC; /* * Get disk size via read capacity command. */ read_cap_data = (DIR_READ_CAP_DATA *) ccmn_get_dbuf((U32)sizeof(DIR_READ_CAP_DATA)); if(cdisk_read_capacity(pd, read_cap_data) == 0) { BTOL(&read_cap_data->lbn3, nblocks); /* * RDCAP returns the address of the last LBN. * Add one to get the number of LBNs. */ devgeom->geom_info.dev_size = ++nblocks; /* * Get the sector size. */ BTOL(&read_cap_data->block_len3, blk_len); devgeom->geom_info.sector_size = blk_len ;
.
.
.
Obtains information about a device
The DEVGETINFO ioctl command returns information about a device by polling the underlying device driver. The DEVGETINFO ioctl command queries the device for as much information as it can, initializes members of the appropriate data structures within the device_info_t union, then assigns the address of this union to the user's data buffer.
The device_info_t union is the top-level definition of the information returned to the DEVGETINFO ioctl command. It has the following definition:
typedef union device_info { int version; v1_device_info_t v1; uchar_t pad[1024]; } device_info_t;
The v1_device_info_t structure contains all the information returned to the user data buffer by the DEVGETINFO ioctl command. This structure is contained within the device_info_t structure so that the same interface can be used for various versions of the data structure. It has the following definition:
typedef struct v1_device_info { int version; short category; short bus; char interface[DEV_STRING_SIZE]; char device[DEV_STRING_SIZE]; char dev_name[DEV_STRING_SIZE]; ulong_t soft_count; ulong_t hard_count; v1_devtype_info_t devinfo; v1_bustype_info_t businfo; uchar_t private[DEV_PRIVATE_LEN]; } v1_device_info_t;
The v1_devtype_info_t union returns device-specific information. The contents of the v1_devtype_info_t union depends on the device type, either disk, tape, or other device type. The structure has the following definition:
typedef union v1_devtype_info { v1_disk_dev_info_t disk; v1_tape_dev_info_t tape; uchar_t devdata[DEV_TYPE_LEN]; } v1_devtype_info_t;
The v1_bustype_info_t structure returns information about the bus type. It has the following definition:
typedef struct v1_bustype_info { int adpt_num; int nexus_num; int bus_num; int ctlr_num; int rctlr_num; int slave_num; int unit_num; int pad0; union { v1_scsi_bus_info_t scsi; uchar_t busdata[DEV_BUS_LEN]; } bus; } v1_bustype_info_t;
For disk devices, the v1_devtype_info_t union contains a v1_disk_dev_info_t structure, defined as follows:
typedef struct v1_disk_dev_info { ulong_t status; ulong_t capacity; ulong_t blocksz; uchar_t class; uchar_t part_num; uchar_t raid_level; uchar_t pad0; ushort_t media_changes; ushort_t pad1; union { struct { uchar_t density_code; uchar_t flags; } scsi; uchar_t archdata[DEV_ARCH_LEN]; } arch; } v1_disk_dev_info_t;
For tape devices, the v1_devtype_info_t union contains a v1_tape_dev_info_t structure, defined as follows:
typedef struct v1_tape_dev_info { uint_t media_status; uint_t unit_status; long recordsz; long density_bpi; long density_bpi_wrt; long position; long fm_cnt; uchar_t class; uchar_t pad[7]; union { struct { uint_t blocking_factor; uchar_t valid_flags; uchar_t density_code; uchar_t buffered_mode; uchar_t speed; uchar_t compression_code; } scsi; uchar_t archdata[DEV_ARCH_LEN]; } arch; } v1_tape_dev_info_t;
The DEVGETINFO ioctl command replaces the DEVIOCGET ioctl command. Your driver may still support DEVIOCGET for backwards compatibility, but Digital recommends that you use the DEVGETINFO ioctl command. The DEVIOCGET ioctl command may not be supported in future releases.
The following example shows how a device driver can return generic information about any device type. For information on filling in bus-specific information and polling a disk device for device-specific information, see Writing Device Drivers: Advanced Topics.
.
.
.
case DEVGETINFO: { struct device *device; [1] v1_device_info_t *devi_p; devi_p = (v1_device_info_t *)data; [2] bzero((caddr_t)devi_p,sizeof(*devi_p)); /**************************************************** * fill in generic information ****************************************************/ devi_p->version = VERSION_1; [3] devi_p->category = DEV_DISK; devi_p->bus = FILLIN; bcopy("XXX", devi_p->interface, 3); bcopy("xxxdev", devi_p->device, 6); bcopy("xx", devi_p->dev_name, 2); devi_p->soft_count = devp->soft_err_cnt; devi_p->hard_count = devp->hard_err_cnt;
.
.
.
device_info_t, v1_bustype_info_t, v1_device_info_t, v1_devtype_info_t, v1_disk_dev_info_t, v1_tape_dev_info_t
Obtains information about a device
#include <io/common/devio.h>
#include <sys/ioctl.h>
The DEVIOCGET ioctl command obtains information about a device. This command obtains generic device information by polling the underlying device driver. DEVIOCGET uses the following structure defined in the file /usr/sys/include/io/common/devio.h:
struct devget { short category; short bus; char interface[DEV_SIZE]; char device[DEV_SIZE]; short adpt_num; short nexus_num; short bus_num; short ctlr_num; short rctlr_num; short slave_num; char dev_name[DEV_SIZE]; short unit_num; unsigned soft_count; unsigned hard_count; long stat; long category_stat; };
The following list describes the members of this structure:
The DEVGETINFO ioctl command replaces the DEVIOCGET ioctl command. Your driver may still support DEVIOCGET for backwards compatibility, but Digital recommends that you use the DEVGETINFO ioctl command. The DEVIOCGET ioctl command may not be supported in future releases.
The following example prints out the device type and unit number:
. . . struct devget dev_st; [1] if (ioctl (fd, DEVIOCGET, &dev_st) < 0) { printf ("DEVIOCGET failed\n"); exit(1); } [2] printf ("Device type = %s\n",dev_st.device); [3] printf ("Unit number = %d\n",dev_st.unit_num); [4]
Returns the current partition map for a disk
The DIOCGCURPT ioctl command returns the current partition map to the user data buffer in a pt_tbl structure. The pt_tbl structure contains an array of partition structures, up to the maximum partitions allowed on the device.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCGCURPT command.
Returns the default partition map for a disk
The DIOCGDEFPT ioctl command returns a disk's default partition map to the user data buffer. This information is returned in a pt_tbl structure, which contains an array of partition structures, up to the maximum number of partitions allowed on the device. The DIOCGDEFPT ioctl command can return the partition map regardless of whether a valid disklabel exists on the disk.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCGDEFPT command.
disklabel, get_def_partitionmap, pt_tbl
Returns a disk label
The DIOCGDINFO ioctl command returns a disklabel structure to the user data buffer. If the disk label is not valid, it returns EINVAL.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCGDINFO command.
Returns information about a single disk partition
The DIOCGPART ioctl command returns information about a device and partition. This information is returned in a partinfo structure, which contains pointers to the disklabel and partition structures for a single partition on a disk. If the disk does not have a disk label, the DIOCGPART ioctl command returns EINVAL.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCGPART command.
Sets a disklabel structure
The DIOCSDINFO ioctl command sets the in-memory disklabel structure to the value specified in the user data buffer. Only users with root privileges are allowed to change the disklabel structure.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCSDINFO command.
Writes a disk label
The DIOCWDINFO ioctl command writes a disklabel structure and updates the in-memory copy of the structure. The command turns off write-protection for the duration of the request, but does not check to see if the user has root privileges.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCWDINFO command.
Sets a write-enable label on a disk
The DIOCWLABEL ioctl command turns the write-protect flag on and off for the block where the disk label is kept.
See Writing Device Drivers: Advanced Topics for an example of how a disk device driver implements the DIOCWLABEL command.
Defines ioctl types for device control operations
#include <sys/ioctl.h>
The _IO macro defines ioctl types for situations where no data is actually transferred between the application program and the kernel. For example, this could occur in a device control operation.
The following example uses the _IO macro to construct an ioctl called DN_OPERATION1. Note that DN_OPERATION1 passes the value zero (0) for the group that this ioctl belongs to and the value 1 to identify the specific ioctl type within the group.
#define DN_OPERATION1 _IO(0,1)
Defines ioctl types for device control operations
#include <sys/ioctl.h>
The _IOR macro defines ioctl types for situations where data is transferred from the kernel into the user's buffer. Typically, this data consists of device control or status information returned to the application program.
The following example uses the _IOR macro to construct an ioctl called DN_GETCOUNT. Note that DN_GETCOUNT passes the value zero (0) for the group that this ioctl belongs to and the value 2 to identify the specific ioctl type within the group. The DN_GETCOUNT ioctl also passes the data type int, which the kernel passes to the sizeof operator to determine how much data is passed from the kernel back to the user application.
#define DN_GETCOUNT _IOR(0,2,int)
Defines ioctl types for device control operations
#include <sys/ioctl.h>
The _IOW macro defines ioctl types for situations where data is transferred from the user's buffer into the kernel. Typically, this data consists of device control or status information passed to the driver from the application program.
The following example uses the _IOW macro to construct an ioctl called DN_SETCOUNT. Note that DN_SETCOUNT passes the value zero (0) for the group that this ioctl belongs to and the value 3 to identify the specific ioctl type within the group. The DN_SETCOUNT ioctl also passes the data type int, which the kernel passes to the sizeof operator to determine how much data is passed from the user application back to the kernel.
#define DN_SETCOUNT _IOR(0,3,int)
Defines ioctl types for device control operations
#include <sys/ioctl.h>
The _IOWR macro defines ioctl types for situations where data is transferred from the user's buffer into the kernel. The driver then performs the appropriate ioctl operation and returns data of the same size back up to the user-level application. Typically, this data consists of device control or status information passed to the driver from the application program.
The following example uses the _IOWR macro to construct an ioctl called DN_SETVERIFY. Note that DN_SETVERIFY passes the value zero (0) for the group that this ioctl belongs to and the value 4 to identify the specific ioctl type within the group. The DN_SETVERIFY ioctl also passes the data type int, which the kernel passes to the sizeof operator to determine how much data is passed from the user application back to the kernel.
#define DN_SETVERIFY _IOWR(0,4,int)
Table 2-4 summarizes the global variables that device drivers use.
Global Variable | Summary Description |
cpu | Provides a unique logical processor-type family identifier. |
hz | Stores the number of clock ticks per second. |
lbolt | Is a periodic wakeup mechanism. |
page_size | Is the virtual page size. |
Provides a unique logical processor-type family identifier
extern int cpu;
The cpu global variable provides a unique logical family identifier of the processor type of the running system. The logical system name can represent a single processor or a family of processor types. For example, the constant DEC_3000_500 represents the DEC 3000 Model 500 AXP Workstation. The defined processor names appear in the following file: /usr/sys/include/arch/alpha/hal/cpuconf.h.
You use the cpu global variable to conditionally execute processor-specific code. For example, the following code fragment calls a system-specific initialization interface for the DEC 3000 Model 500 AXP Workstation:
. . . if (cpu == DEC_3000_500) { init_3000_500(); } . . .
Stores the number of clock ticks per second
extern int hz;
/usr/sys/include/sys/kernel.h
The hz global variable is set to the number of clock ticks per second. The value is useful for timing purposes. For example, if a device driver wants to schedule an interface to be run in 2 seconds, you could use the following call:
. . . timeout(lptout, (caddr_t)dev, 2*hz); . . .
Is a periodic wakeup mechanism
extern time_t lbolt;
/usr/sys/include/sys/kernel.h
You use the lbolt global variable as a periodic wakeup mechanism. Wakeups are done on the lbolt variable once per second. For example, if a driver was polling for an event once per second, you could use the following code:
. . . sleep((caddr_t) &lbolt, PZERO); . . .
Is the virtual page size
extern vm_size_t page_size;
/usr/sys/include/mach/vm_param.h
The page_size global variable is the size of a virtual page on a CPU. A device driver can use this global variable to partition I/O transfers so that they never cross a virtual page boundary. You should use this global variable only when: