Writing Device Drivers

Appendix C Summary of Solaris 7 DDI/DKI Services

This chapter discusses the interfaces provided by the Solaris 7 DDI/DKI. After each category of interfaces is introduced, each function in that category is listed with a brief description. These descriptions should not be considered complete or definitive, nor do they provide a thorough guide to usage. The descriptions are intended to describe what the functions do in general terms, and what the arguments and return values mean. See the manual pages for more detailed information. The categories are:

This appendix does not discuss STREAMS interfaces; to learn more about network drivers, see the Streams Programming Guide.

buf(9S) Handling

These interfaces manipulate the buf(9S) data structure. It is used to encode block I/O transfer requests, but some character drivers also use buf(9S) to encode character I/O requests with physio(9F). Drivers that use buf(9S) as their primary means of encoding I/O requests have to implement a strategy(9E) routine. For more information, see Chapter 9, Drivers for Character Devices and Chapter 10, Drivers for Block Devices.

void biodone(struct buf *bp);

biodone(9F) marks the I/O described by the buf(9S) structure pointed to by bp as complete by setting the B_DONE flag in bp->b_flags. biodone(9F) then notifies any threads waiting in biowait(9F) for this buffer. Call biodone(9F) on bp when the I/O request it encodes is finished.

void bioerror(struct buf *bp, int error);

bioerror(9F) marks the error bits in the I/O described by the buf(9S) structure pointed to by bp with error.

void bioreset(struct buf *bp);

Use bioreset(9F) reset the buf(9S) structure pointed to by bp, allowing a device driver to reuse privately allocated buffers. bioreset(9F) resets the buffer header to its initially allocated state.

int biowait(struct buf *bp);

biowait(9F) suspends the calling thread until the I/O request described by bp is completed. A call to biodone(9F) unblocks the waiting thread. Usually, if a driver does synchronous I/O, it calls biowait(9F) in its strategy(9E) routine, and calls biodone(9F) in its interrupt handler when the request is complete.

biowait(9F) is usually not called by the driver; instead it is called by physio(9F), or by the file system after calling strategy(9E). The driver is responsible for calling biodone(9F) when the I/O request is completed.

void bp_mapin(struct buf *bp);

bp_mapin(9F) maps the data buffer associated with the buf(9S) structure pointed to by bp into the kernel virtual address space so that the driver can access it. Programmed I/O device drivers often use bp_mapin(9F) because they have to transfer data explicitly between the buf(9S) structure's buffer and a device buffer. See "bp_mapin(9F)" for more information.

void bp_mapout(struct buf *bp);

bp_mapout(9F) unmaps the data buffer associated with the buf(9S) structure pointed to by bp. The buffer must have been mapped previously by bp_mapin(9F). bp_mapout(9F) can only be called from user or kernel context.

void clrbuf(struct buf *bp);

clrbuf(9F) zeroes bp->b_bcount bytes starting at bp->b_un.b_addr.

void disksort(struct diskhd *dp, struct buf *bp);

disksort(9F) implements a queueing strategy for block I/O requests to block-oriented devices. dp is a pointer to a diskhd structure that represents the head of the request queue for the disk. disksort(9F) sorts bp into this queue in ascending order of cylinder number. The cylinder number is stored in the b_resid field of the buf(9S) structure. This strategy minimizes seek time for some disks.

void freerbuf(struct buf *bp);

freerbuf(9F) frees the buf(9S) structure pointed to by bp. The structure must have been allocated previously by getrbuf(9F).

int geterror(struct buf *bp);

geterror(9F) returns the error code stored in bp if the B_ERROR flag is set in bp->b_flags. It returns zero if no error occurred.

struct buf *getrbuf(int sleepflag);

getrbuf(9F) allocates a buf(9S) structure and returns a pointer to it. sleepflag should be either KM_SLEEP or KM_NOSLEEP, depending on whether getrbuf(9F) should wait for a buf(9S) structure to become available if one cannot be allocated immediately.

int physio(int (*strat)(struct buf *), struct buf *bp, 
			dev_t dev, int rw, void (*mincnt)(struct buf *), struct uio *uio);

physio(9F) translates a read or write I/O request encoded in a uio(9S) structure into a buf(9S) I/O request. strat is a pointer to a strategy(9E) routine that physio(9F) calls to handle the I/O request. If bp is NULL, physio(9F) allocates a private buf(9S) structure.

Before calling strategy(9E), physio(9F) locks down the memory referred to by the buf(9S) structure (initialized from the uio(9S) structure). For this reason, many drivers that do DMA must use physio(9F), as it is the only way to lock down memory.

In most block device drivers, read(9E) and write(9E) handle raw I/O requests, and consist of little more than a call to physio(9F).

void minphys(struct buf *bp);

minphys(9F) can be passed as the mincnt argument to physio(9F). This causes physio(9F) to make I/O requests to the strategy routine that are no larger than the system default maximum data transfer size. If the original uio(9S) I/O request is to transfer a greater amount of data than )minphys(9F) allows, physio(9F) calls strategy(9E) repeatedly.

Copying Data

These interfaces are data copying utilities, used both for copying data within the kernel, and for copying data between the kernel and an application.

void bcopy(const void *from, void *to, size_t bcount);

bcopy(9F) copies count bytes from the location pointed to by from to the location pointed to by to.

int copyin(const void *userbuf, void *driverbuf,  	size_t cn);

copyin(9F) copies data from an application's virtual address space to the kernel virtual address space, where the driver can address the data. The driver developer must ensure that adequate space is allocated for driverbuf.

int copyout(const void *driverbuf, void *userbuf,  	size_t cn);

copyout(9F) copies data from the kernel virtual address space to an application program's virtual address space.

int ddi_copyin(const void *buf, void *driverbuf, 
			size_t cn, int flags);

This routine is designed for use in driver ioctl(9E) routines. It copies data from a source address to a driver buffer. The driver developer must ensure that adequate space is allocated for the destination address.

The flags argument is used to determine the address space information about buf. If the FKIOCTL flag is set, it indicates that buf is a kernel address, and ddi_copyin(9F) behaves like bcopy(9F). Otherwise, buf is interpreted as a user buffer address, and ddi_copyin(9F) behaves like copyin(9F).

The value of the flags argument to ddi_copyin(9F) should be passed directly from the mode argument of ioctl(9E) untranslated.

int ddi_copyout(const void *driverbuf, void *buf, size_t cn, 
			int flags);

This routine is designed for use in driver ioctl(9E) routines for drivers that support layered I/O controls. ddi_copyout(9F) copies data from a driver buffer to a destination address, buf.

The flags argument is used to determine the address space information about buf. If the FKIOCTL flag is set, it indicates that buf is a kernel address, and ddi_copyout(9F) behaves like bcopy(9F). Otherwise, buf is interpreted as a user buffer address, and ddi_copyin(9F) behaves like copyout(9F).

The value of the flags argument to ddi_copyout(9F) should be passed directly from the mode argument of ioctl(9E) untranslated.

Device Access

These interfaces verify the credentials of application threads making system calls into drivers. They are sometimes used in the open(9E) entry point to restrict access to a device, though this is usually achieved with the permissions on the special files in the file system.

int drv_priv(cred_t *credp);
drv_priv(9F) returns zero if the credential structure pointed to by credp is that of a privileged thread. It returns EPERM otherwise. Use drv_priv(9F) only in place of calls to the obsolete suser() function and when making explicit checks of a calling thread's UID.

cred_t *ddi_get_cred (void);
ddi_get_cred(9F) returns a pointer to the user credential structure of the the caller.

Device Configuration

These interfaces are used in setting up a driver and preparing it for use. Some of these routines handle the dynamic loading of device driver modules into the kernel, and some manage the minor device nodes in /devices that are the interface to a device for application programs. All of these routines are called in the driver's _init(9E), _fini(9E), _info(9E), attach(9E), detach(9E), and probe(9E) entry points.

int ddi_create_minor_node(dev_info_t *dip, char *name, 
			int spec_type, int minor_num, char *node_type,  	int is_clone);

ddi_create_minor_node(9F) advertises a minor device node, which will eventually appear in the /devices directory and refer to the device specified by dip.

void ddi_remove_minor_node(dev_info_t *dip, char *name);

ddi_remove_minor_node(9F) removes the minor device node name for the device dip from the system. name is assumed to have been created by ddi_create_minor_node(9F). If name is NULL, all minor node information is removed.

int mod_install(struct modlinkage *modlinkage);

mod_install(9F) links the calling driver module into the system and prepares the driver to be used. modlinkage is a pointer to the modlinkage structure defined in the driver. mod_install(9F) must be called from the _init(9E) entry point.

int mod_remove(struct modlinkage *modlinkage);

mod_remove(9F) unlinks the calling driver module from the system. modlinkage is a pointer to the modlinkage structure defined in the driver. mod_remove(9F) must be called from the _fini(9E) entry point.

int mod_info(struct modlinkage *modlinkage, 
			struct modinfo *modinfop);

mod_info(9F) reports the status of a dynamically loadable driver module. It must be called from the _info(9E) entry point.

Device Information

These interfaces provide information to the driver about a device, such as whether the device is self-identifying, what instance number the system has assigned to a device instance, the name of the dev_info node for the device, and the dev_info node of the device's parent.

int ddi_dev_is_sid(dev_info_t *dip);

ddi_dev_is_sid(9F) returns DDI_SUCCESS if the device identified by dip is self-identifying (see "Device Identification"). Otherwise, it returns DDI_FAILURE.

int ddi_get_instance(dev_info_t *dip);

ddi_get_instance(9F) returns the instance number assigned by the system for the device instance specified by dip.

char *ddi_get_name(dev_info_t *dip);

ddi_get_name(9F) returns a pointer to a character string that is the name of the dev_info tree node specified by dip.

char *ddi_binding_name(dev_info_t *dip);

ddi_binding_name(9F) also returns a pointer to a character string that is the name of the dev_info tree node specified by dip.

const char *ddi_driver_name(dev_info_t *devi);

ddi_driver_name(9F) returns the actual name of the driver bound to a device..

dev_info_t *ddi_get_parent(dev_info_t *dip);

ddi_get_parent(9F) returns the dev_info_t pointer for the parent dev_info node of the passed node, identified by dip.

int ddi_slaveonly(dev_info_t *dip);

ddi_slaveonly(9F) returns DDI_SUCCESS if the device indicated by dip is installed in a slave-access only bus slot. It returns DDI_FAILURE otherwise.

DMA Handling

These interfaces allocate, synchronize and release DMA resources for devices capable of directly accessing system memory.

DMA resources are identified by a DMA handle. A DMA handle is created obeying the DMA attributes structure. It allows any constraints that the device's DMA controller may impose on DMA transfers to be specified, such as a limited transfer size and DMA address range.

A family of DMA setup functions are provided that make it easier to allocate DMA resources for use with kernel virtual addresses (ddi_dma_addr_bind_handle(9F), and buf(9S) structures ddi_dma_addr_bind_handle(9F)). The setup functions pass back a pointer to a DMA cookie, which countains I/O address and size information.

The DMA setup functions also provide a callback mechanism where a function can be specified to be called later if the requested mapping can't be set up immediately.

The DMA window functions allow resources to be allocated for a large object. The resources can be moved from one part of the object to another by moving the DMA window.

The DMA engine functions allow drivers to manipulate the system DMA engine, if there is one. These are currently used on x86 systems.

int ddi_dma_burstsizes(ddi_dma_handle_t handle);

ddi_dma_burstsizes(9F) returns an integer that encodes the allowed burst sizes for the DMA resources specified by handle. Allowed power of two burst sizes are bit-encoded in the return value. For a mapping that allows only 2-byte bursts, for example, the return value would be 0x2. For a mapping that allows 1-, 2-, 4-, and 8-byte bursts, the return value would be 0xf.

int ddi_dma_devalign(ddi_dma_handle_t handle, 
			u_int *alignment, u_int *minxfr);

ddi_dma_devalign(9F) passes back, in the location pointed to by alignment, the required alignment for the beginning of a DMA transfer using the resources identified by handle. The alignment will be a power of two. ddi_dma_devalign(9F) also passes back in the location pointed to by minxfr the minimum number of bytes of the mapping that will be read or written in a single transfer.

int ddi_dma_sync(ddi_dma_handle_t handle, off_t off, 
			size_t len, u_int type);

ddi_dma_sync(9F) ensures that any CPU and the device see the same data starting at off bytes into the DMA resources identified by handle and continuing for len bytes. type should be:

int   ddi_dmae_alloc(dev_info_t *dip, int chnl, 
			int (*dmae_waitfp)(), caddr_t arg);

ddi_dmae_alloc(9F) allocates a DMA channel from the system DMA engine. It must be called prior to any operation on a channel.

int ddi_dmae_release(dev_info_t *dip, int chnl);

ddi_dmae_release(9F) releases a previously allocated DMA channel.

int ddi_dmae_prog(dev_info_t *dip, struct ddi_dmae_req *dmaereqp, 
			ddi_dma_cookie_t *cookiep, int chnl);

The ddi_dmae_prog(9F) function programs the DMA channel for an operation. This function allows access to various capabilities of the DMA engine hardware. It disables the channel prior to setup, and enables the channel before returning.

The DMA address and count are specified by passing ddi_dmae_prog(9F) a cookie obtained from ddi_dma_buf_bind_handle(9F). Other DMA engine parameters are specified by the DMA engine request structure passed in through dmaereqp. The fields of that structure are documented in ddi_dmae_req(9S).

int ddi_dmae_disable(dev_info_t *dip, int chnl);

The ddi_dmae_disable(9F) function disables the DMA channel so that it no longer responds to a device's DMA service requests.

int ddi_dmae_enable(dev_info_t *dip, int chnl);

The ddi_dmae_enable(9F) function enables the DMA channel for operation. This may be used to re-enable the channel after a call to ddi_dmae_disable(9F). The channel is automatically enabled after successful programming by ddi_dmae_prog(9F).

int ddi_dmae_stop(dev_info_t *dip, int chnl);

The ddi_dmae_stop(9F) function disables the channel and terminates any active operation.

int ddi_dmae_getcnt(dev_info_t *dip, int chnl, int *countp);

The ddi_dmae_getcnt(9F) function examines the count register of the DMA channel and sets (*countp) to the number of bytes remaining to be transferred. The channel is assumed to be stopped.

int ddi_dmae_1stparty(dev_info_t *dip, int chnl);

The ddi_dmae_1stparty(9F) function is used, by device drivers using first-party DMA, to configure a channel in the system's DMA engine to operate in a ``slave'' mode.

int ddi_dmae_getattr(dev_info_t *dip, ddi_dma_attr_t *attrp);

The ddi_dmae_getattr(9F) function fills in the DMA attribute structure, pointed to by attrp, with the DMA attributes of the system DMA engine. This attribute structure must be passed to ddi_dma_alloc_handle(9F). If the device has any particular restrictions on transfer size or granularity (for example, a disk sector size), the driver should further restrict the values in the structure members before passing them to ddi_dma_alloc_handle(9F). The driver must not relax any of the restrictions embodied in the structure after it is filled in by ddi_dmae_getattr(9F).

int ddi_iomin(dev_info_t *dip, int initial, int streaming);

ddi_iomin(9F) returns an integer that encodes the required alignment and the minimum number of bytes that must be read or written by the DMA controller of the device identified by dip. ddi_iomin(9F) is like ddi_dma_devalign(9F), but the memory object is assumed to be primary memory, and the alignment is assumed to be equal to the minimum possible transfer.

int ddi_dma_alloc_handle(dev_info_t *dip,  	
			ddi_dma_attr_t *attr, int  (*callback)(caddr_t),  	
			caddr_t arg, ddi_dma_handle_t *handlep);

ddi_dma_alloc_handle(9F) allocates a new DMA handle. A DMA handle is an opaque object used as a reference to subsequently allocated DMA resources. ddi_dma_alloc_handle(9F) accepts as parameters the device information referred to by dip and the device's DMA attributes described by a ddi_dma_attr(9S) structure. A successful call to ddi_dma_alloc_handle(9F) fills in the value pointed to by handlep. A DMA handle must only be used by the device for which it was allocated and is valid only for one I/O transaction at a time.

If callback is set to DDI_DMA_DONTWAIT, then the caller does not care if the allocation fails, and can handle an allocation failure appropriately. If callback is set to DDI_DMA_SLEEP, then the caller needs to have the allocation routines wait for resources to become available. If any other value is set, and a DMA resource allocation fails, this value is assumed to be a function to call at a later time when resources may become available. When the specified function is called, it is passed arg as an argument. The specified callback function must return either DDI_DMA_CALLBACK_RUNOUT or DDI_DMA_CALLBACK_DONE.

DDI_DMA_CALLBACK_RUNOUT indicates that the callback routine attempted to allocate DMA resources but failed to do so, in which case the callback function is put back on a list to be called again later. DDI_DMA_CALLBACK_DONE indicates either success at allocating DMA resources or the driver no longer available.

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

ddi_dma_mem_alloc(9F) allocates memory for DMA transfers to or from a device. The allocation will conform to the alignment, padding constraints, and device granularity as specified by the DMA attributes (see ddi_dma_attr(9S)) passed to ddi_dma_alloc_handle(9F) and the more restrictive attributes imposed by the system.

void ddi_dma_mem_free(ddi_acc_handle_t *handlep);

ddi_dma_mem_free(9F) deallocates the memory acquired by ddi_dma_mem_alloc(9F). In addition, it destroys the data access handle handlep associated with the memory.

void ddi_dma_free_handle(ddi_dma_handle_t *handle);

ddi_dma_free_handle(9F) destroys the DMA handle pointed to by the handle. Any further references to the DMA handle will have undefined results. Note that ddi_dma_unbind_handle(9F) must be called prior to ddi_dma_free_handle(9F) to free any resources the system may be caching on the handle.

int ddi_dma_addr_bind_handle(ddi_dma_handle_t handle,  	
			struct as *as, caddr_t addr, size_t len, uint_t flags, 
			int (*callback)(caddr_t), caddr_t arg, ddi_dma_cookie_t *cookiep,  
			uint_t *ccountp);

ddi_dma_addr_bind_handle(9F) allocates DMA resources for a memory object so that a device can perform DMA to or from the object. DMA resources are allocated with respect to the device's DMA attributes as expressed by ddi_dma_attr(9S) (see ddi_dma_alloc_handle(9F)).

ddi_dma_addr_bind_handle(9F) fills in the first DMA cookie pointed to by cookiep with the appropriate address, length, and bus type. *ccountp is set to the number of DMA cookies representing this DMA object. Subsequent DMA cookies must be retrieved by calling ddi_dma_nextcookie(9F) *countp - 1 times.

When a DMA transfer completes, the driver should free up system DMA resources by calling ddi_dma_unbind_handle(9F).

int ddi_dma_set_sbus64(ddi_dma_handle_t handle,  	
			uint_t burstsizes);

ddi_dma_set_sbus64(9F) informs the system that the device needs to perform 64-bit data transfers on the SBus. The driver must first allocate a DMA handle using ddi_dma_alloc_handle(9F) with a ddi_dma_attr(9S) structure describing the DMA attributes for a 32-bit transfer mode.

int ddi_dma_buf_bind_handle(ddi_dma_handle_t handle,  	
			struct buf *bp, uint_t flags, int (*callback)(caddr_t),caddr_t arg,  	
			ddi_dma_cookie_t *cookiep, uint_t *ccountp);

ddi_dma_buf_bind_handle(9F) allocates DMA resources for a system buffer such that a device can perform DMA to or from the buffer. DMA resources are allocated with respect to the device's DMA attributes as expressed by ddi_dma_attr(9S) (see ddi_dma_alloc_handle(9F)).

int ddi_dma_unbind_handle(ddi_dma_handle_t handle);

ddi_dma_unbind_handle(9F) frees all DMA resources associated with an existing DMA handle. When a DMA transfer is completed, the driver should call ddi_dma_unbind_handle(9F) to free system DMA resources established by a call to ddi_dma_buf_bind_handle(9F) or ddi_dma_addr_bind_handle(9F).

ddi_dma_unbind_handle(9F) does an implicit ddi_dma_sync(9F), making further synchronization steps unnecessary.

int ddi_dma_numwin(ddi_dma_handle_t handle, uint_t *nwinp);

ddi_dma_numwin(9F) returns the number of DMA windows for a DMA object if partial resource allocation was permitted.

int ddi_dma_getwin(ddi_dma_handle_t handle,  	
			uint_t win, off_t *offp, size_t *lenp,
 		ddi_dma_cookie_t *cookiep, uint_t *ccountp);

ddi_dma_getwin(9F) activates a new DMA window. If a DMA resource allocation request returns DDI_DMA_PARTIAL_MAP, indicating that resources for less than the entire object were allocated, the current DMA window can be changed by a call to ddi_dma_getwin(9F).

void ddi_dma_nextcookie(ddi_dma_handle_t handle,  	
			ddi_dma_cookie_t *cookiep);

ddi_dma_nextcookie(9F) retrieves subsequent DMA cookies for a DMA object. ddi_dma_nextcookie(9F) fills in the ddi_dma_cookie(9S) structure pointed to by cookiep. The ddi_dma_cookie(9S) structure must be allocated prior to calling ddi_dma_nextcookie(9F).

Flow of Control

These interfaces influence the flow of program control in a driver. These are mostly callback mechanisms, functions that schedule another function to run at a later time. Many drivers schedule a function to run intermittently to check on the status of the device, and possibly issue an error message if a strange condition is detected.


Note -

The detach(9E) entry point must ensure that no callback functions are pending in the driver before returning successfully. See Chapter 5, Autoconfiguration.


timeout_id_t timeout(void (*ftn)(caddr_t), caddr_t arg, clock_t ticks);
timeout(9F) schedules the function pointed to by ftn to be run after ticks clock ticks have elapsed. arg is passed to the function when it is run. timeout(9F) returns a "timeout ID" that can be used to cancel the timeout later.

int untimeout(timeout_id_t id);
untimeout(9F) cancels the timeout indicated by the timeout ID id. If the number of clock ticks orignially specified to timeout(9F) have not elapsed, the callback function will not be called.

Interrupt Handling

These interfaces manage device interrupts and software interrupts. The basic model is to register with the system an interrupt-handling function to be called when a device interrupts or a software interrupt is triggered.

int ddi_get_iblock_cookie(dev_info_t *dip, u_int inumber, ddi_iblock_cooki
e_t *iblock_cookiep);

ddi_get_iblock_cookie retrieves the interrupt block cookie associated with a particular interrupt specification. This routine should be called before ddi_add_intr to retrieve the interrupt block cookie needed to initialize lock (mutex(9F), rwlock(9F)) used by the interrupt routine. The interrupt number inumber determines which interrupt specification to retrieve the cookie for. inumber is associated with information provided either by the device (see sbus(4)) or the hardware configuration file (see vme(4), sysbus(4), isa(4), eisa(4), mca(4), and driver.conf(4)). If only one interrupt is associated with the device, inumber should be 0.

On a successful return, *iblock_cookiep contains information needed for initializing locks associated with the interrupt specification corresponding to inumber (see mutex_init(9F) and rw_init(9F)). The driver can then initialize locks acquired by the interrupt routine before calling ddi_add_intr which prevents a possible race condition where the driver's interrupt handler is called immediately after the driver has called ddi_add_intr but before the driver has initialized the locks. This may happen when an interrupt for a different device occurs on the same interrupt level. If the interrupt routine acquires the lock before the lock has been initialized, undefined behavior may result.

int ddi_add_intr(dev_info_t *dip, u_int inumber,  	
			ddi_iblock_cookie_t *iblock_cookiep, ddi_idevice_cookie_t *idevice_cookiep,  
				u_int (*int_handler)(caddr_t), caddr_t int_handler_arg);

ddi_add_intr(9F) tells the system to call the function pointed to by int_handler when the device specified by dip issues the interrupt identified by inumber. ddi_add_intr(9F) passes back an interrupt block cookie in the location pointed to by iblock_cookiep, and an interrupt device cookie in the location pointed to by idevice_cookiep. The interrupt block cookie is used to initialize mutual exclusion locks (mutexes) and other synchronization variables. The device interrupt cookie is used to program the level at which the device interrupts, for those devices that support such programming.

void ddi_remove_intr(dev_info_t *dip, u_int inumber,  	
			ddi_iblock_cookie_t iblock_cookie);

ddi_remove_intr(9F) tells the system to stop calling the interrupt handler registered for the interrupt inumber on the device identified by dip. iblock_cookie is the interrupt block cookie that was returned by ddi_add_intr(9F) when the interrupt handler was set up. Device interrupts must be disabled before calling ddi_remove_intr(9F), and always call ddi_remove_intr(9F) in the detach(9E) entry point before returning successfully (if any interrupts handlers were added).

int ddi_add_softintr(dev_info_t *dip, int preference,  	
			ddi_softintr_t *idp, ddi_iblock_cookie_t *ibcp,  	
			ddi_idevice_cookie_t *idcp, u_int (*int_handler)(caddr_t),  	
			caddr_t int_handler_arg);

ddi_add_softintr(9F) tells the system to call the function pointed to by int_handler when a certain software interrupt is triggered. ddi_add_softintr(9F) returns a software interrupt ID in the location pointed to by idp. This ID is later used by ddi_trigger_softintr(9F) to trigger the software interrupt.

void ddi_trigger_softintr(ddi_softintr_t id);

ddi_trigger_softintr(9F) triggers the software interrupt identified by id. The interrupt handling function that was set up for this software interrupt by ddi_add_softintr(9F) is then called.

void ddi_remove_softintr(ddi_softintr_t id);

ddi_remove_softintr(9F) tells the system to stop calling the software-interrupt handler for the software interrupt identified by id. If the driver has soft interrupts registered, it must call ddi_remove_softintr(9F) in the detach(9E) entry point before returning successfully.

int ddi_dev_nintrs(dev_info_t *dip, int *result);

ddi_dev_nintrs(9F) passes back in the location pointed to by result the number of different interrupt specifications that the device indicated by dip can generate. This is useful when dealing with a device that can interrupt at more than one level.

int ddi_intr_hilevel(dev_info_t *dip, u_int inumber);

ddi_intr_hilevel(9F) returns nonzero if the system considers the interrupt specified by inumber on the device identified by dip to be high level. Otherwise, it returns zero.

Kernel Statistics

These interfaces enable device drivers to store statistics about the device in the kernel for later retrieval by applications.

kstat_t *kstat_create(char *module, int instance,  	
			char *name, char *class, uchar_t type,  	
			ulong_t ndata, uchar_t ks_flag);

kstat_create(9F) allocates and performs necessary system initialization of a kstat(9S) structure. After a successful call to kstat_create(9F), the driver must perform any necessary initialization of the data structure and then use kstat_install(9F) to make the kstat(9S) structure accessible to user applications.

void kstat_delete(kstat_t *ksp);

kstat_delete(9F) removes the kstat(9S) structure pointed to by ksp from the kernel statistics data and frees associated system resources.

void kstat_install(kstat_t *ksp);

kstat_install(9F) enables the kstat(9S) structure pointed to by ksp to be accessible by the user land applications.

void kstat_named_init(kstat_named_t *knp, char *name,  	
			uchar_t data_type);

kstat_named_init(9F) associates the name pointed to by name and the type specified in data_type with the kstat_named(9S) structure pointed to by knp.

void kstat_waitq_enter(kstat_io_t *kiop);

kstat_waitq_enter(9F) is used to update the kstat_io(9S) structure pointed to by kiop, indicating that a request has arrived but has not yet been processed.

void kstat_waitq_exit(kstat_io_t *kiop);

kstat_waitq_exit(9F) is used to update the kstat_io(9S) structure pointed to by kiop, indicating that the request is about to be serviced.

void kstat_runq_enter(kstat_io_t *kiop);

kstat_runq_enter(9F) is used to update the kstat_io(9S) structure pointed to by kiop, indicating that the request is in the process of being serviced. kstat_runq_enter(9F) is generally invoked after a call to kstat_waitq_exit(9F).

void kstat_runq_exit(kstat_io_t *kiop);

kstat_runq_exit(9F) is used to update the kstat_io(9S) structure pointed to by kiop, indicating that the request is serviced.

void kstat_waitq_to_runq(kstat_io_t *kiop);

kstat_waitq_to_runq(9F) is used to update the kstat_io(9S) structure pointed to by kiop, indicating that the request is transitioning from one state to the next. kstat_waitq_to_runq(9F) is used when a driver would normally call kstat_waitq_exit(9F) followed immediately by kstat_runq_enter(9F).

void kstat_runq_back_to_waitq(kstat_io_t *kiop);

kstat_runq_back_to_waitq(9F) is used to update the kstat_io(9S) structure pointed to by kiop indicating that the request is transitioning from one state to the next. kstat_runq_back_to_waitq(9F) is used when a driver would normally call kstat_runq_exit(9F) followed immediately by a call to kstat_waitq_enter(9F).

Memory Allocation

These interfaces dynamically allocate memory for the driver to use.

void *kmem_alloc(size_t size, int flag);

kmem_alloc(9F) allocates a block of kernel virtual memory of length size and returns a pointer to it. If flag is KM_SLEEP, kmem_alloc(9F) may block, waiting for memory to become available. If flag is KM_NOSLEEP, kmem_alloc(9F) returns NULL if the request cannot be satisfied immediately.

void kmem_free(void *cp, size_t size);

kmem_free(9F) releases a block of memory of length size, starting at address addr, that was previously allocated by kmem_alloc(9F). size must be the original amount allocated.

void *kmem_zalloc(size_t size, int flags);

kmem_zalloc(9F) calls kmem_alloc(9F) to allocate a block of memory of length size, and calls bzero(9F) on the block to zero its contents before returning its address.

These interfaces allocate/free memory that can be exported to applications using devmap_umem_setup(9F).

void *ddi_umem_alloc(size_t size, int flag,  
				ddi_umem_cookie_t *cookiep);

ddi_umem_alloc(9F) allocates a block of kernel virtual memory of length size and returns a pointer to it. The memory can be used inside the kernel and it can be exported to user applications using devmap_umem_setup(9F). If flag is DDI_UMEM_SLEEP, ddi_umem_alloc(9F) may block, waiting for memory to become available. If flag is DDI_UMEM_NOSLEEP, ddi_umem_alloc(9F) returns NULL if the request cannot be satisfied immediately. If the DDI_UMEM_PAGEABLE flag is set, the memory can be paged out.

void ddi_umem_free(ddi_umem_cookie_t cookie);

ddi_umem_free(9F) releases a block of memory that was previously allocated by ddi_umem_alloc(9F).

Memory Space Access

uint8_t ddi_mem_get8(ddi_acc_handle_t handle, uint8_t *dev_addr);
uint16_t ddi_mem_get16(ddi_acc_handle_t handle, uint16_t *dev_addr);
uint32_t ddi_mem_get32(ddi_acc_handle_t handle, uint32_t *dev_addr);
uint64_t ddi_mem_get64(ddi_acc_handle_t handle, uint64_t *dev_addr);

These routines generate a read of various sizes from memory space or allocated DMA memory. The ddi_mem_get8(9F), ddi_mem_get16(9F), ddi_mem_get32(9F), and ddi_mem_get64(9F) functions read 8 bits, 16 bits, 32 bits, and 64 bits of data respectively from the device address, dev_addr, in memory space.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_mem_put8(ddi_acc_handle_t handle, 
			uint8_t *dev_addr, uint8_t value);
void ddi_mem_put16(ddi_acc_handle_t handle, 
			uint16_t *dev_addr, uint16_t value)
void ddi_mem_put32(ddi_acc_handle_t handle, 
			uint32_t *dev_addr, uint32_t value);
void ddi_mem_put64(ddi_acc_handle_t handle, 
			uint64_t *dev_addr, uint64_t value);

These routines generate a write of various sizes to memory space or allocated DMA memory. The ddi_mem_put8(9F), ddi_mem_put16(9F), ddi_mem_put32(9F), and ddi_mem_put64(9F) functions write 8 bits, 16 bits, 32 bits, and 64 bits of data respectively to the device address, dev_addr, in memory space.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_mem_rep_get8(ddi_acc_handle_t handle,  
			uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, 
			uint_t flags);
void ddi_mem_rep_get16(ddi_acc_handle_t handle,  	
			uint16_t *host_addr, uint16_t *dev_addr,
 		size_t repcount, uint_t flags);
void ddi_mem_rep_get32(ddi_acc_handle_t handle,  	
			uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, 
			uint_t flags);
void ddi_mem_rep_get64(ddi_acc_handle_t handle,  	
			uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, 
			uint_t flags);

These routines generate multiple reads from memory space or allocated DMA memory. repcount data is copied from the device address, dev_addr, in memory space to the host address, host_addr. For each input datum, the ddi_mem_rep_get8(9F), ddi_mem_rep_get16(9F), ddi_mem_rep_get32(9F), and ddi_mem_rep_get64(9F) functions read 8 bits, 16 bits, 32 bits, and 64 bits of data respectively from the device address, dev_addr. dev_addr and host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_mem_rep_put8(ddi_acc_handle_t handle,  	
			uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, 
			uint_t flags);
void ddi_mem_rep_put16(ddi_acc_handle_t handle,  	
			uint16_t *host_addr, uint16_t *dev_addr, size_t repcount, 
			uint_t flags);
void ddi_mem_rep_put32(ddi_acc_handle_t handle,  	
			uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, 
			uint_t flags);
void ddi_mem_rep_put64(ddi_acc_handle_t handle,  	
			uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, 
			uint_t flags);

These routines generate multiple writes to memory space or allocated DMA memory. repcount data is copied from the host address, host_addr, to the device address, dev_addr, in memory space. For each input datum, the ddi_mem_rep_put8(9F), ddi_mem_rep_put16(9F), ddi_mem_rep_put32(9F), and ddi_mem_rep_put64(9F) functions write 8 bits, 16 bits, 32 bits, and 64 bits of data respectively to the device address. dev_addr and host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

Common Device Access Functions

uint8_t ddi_get8(ddi_acc_handle_t handle, uint8_t *dev_addr);
uint16_t ddi_get16(ddi_acc_handle_t handle, uint16_t *dev_addr);
uint32_t ddi_get32(ddi_acc_handle_t handle, uint32_t *dev_addr);
uint64_t ddi_get64(ddi_acc_handle_t handle, uint64_t *dev_addr);

ddi_get8(9F), ddi_get16(9F), ddi_get32(9F), and ddi_get64(9F) read data from the mapped memory address, device register, or allocated DMA memory address.

The ddi_get8(9F), ddi_get16(9F), ddi_get32(9F), and ddi_get64(9F) functions read 8 bits, 16 bits, 32 bits, and 64 bits of data respectively from the device address, dev_addr.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_put8(ddi_acc_handle_t handle,  	
			uint8_t *dev_addr, uint8_t value);
void ddi_put16(ddi_acc_handle_t handle,  	
			uint16_t *dev_addr, uint16_t value);
void ddi_put32(ddi_acc_handle_t handle,  	
			uint32_t *dev_addr, uint32_t value);
void ddi_put64(ddi_acc_handle_t handle,  	
			uint64_t *dev_addr, uint64_t value);

These routines generate a write of various sizes to the mapped memory or device register. The ddi_put8(9F), ddi_put16(9F), ddi_put32(9F), and ddi_put64(9F) functions write 8 bits, 16 bits, 32 bits, and 64 bits of data respectively to the device address, dev_addr.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_rep_get8(ddi_acc_handle_t handle,  	
			uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_get16(ddi_acc_handle_t handle,  
			uint16_t *host_addr, uint16_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_get32(ddi_acc_handle_t handle,  	
			uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_get64(ddi_acc_handle_t handle,  	
			uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, uint_t flags);

These routines generate multiple reads from the mapped memory or device register. repcount data is copied from the device address, dev_addr, to the host address, host_addr. For each input datum, the ddi_rep_get8(9F), ddi_rep_get16(9F), ddi_rep_get32(9F), and ddi_rep_get64(9F) functions read 8 bits, 16 bits, 32 bits, and 64 bits of data respectively from the device address, dev_addr. dev_addr and host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_rep_put8(ddi_acc_handle_t handle,  	
			uint8_t *host_addr, uint8_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_put16(ddi_acc_handle_t handle,  
			uint16_t *host_addr, uint16_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_put32(ddi_acc_handle_t handle,  	
			uint32_t *host_addr, uint32_t *dev_addr, size_t repcount, uint_t flags);
void ddi_rep_put64(ddi_acc_handle_t handle,  
			uint64_t *host_addr, uint64_t *dev_addr, size_t repcount, uint_t flags);

These routines generate multiple writes to the mapped memory or device register. repcount data is copied from the host address, host_addr, to the device address, dev_addr. For each input datum, the ddi_rep_put8(9F), ddi_rep_put16(9F), ddi_rep_put32(9F), and ddi_rep_put64(9F) functions write 8 bits, 16 bits, 32 bits, and 64 bits of data respectively to the device address, dev_addr. dev_addr and host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

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

The attributes encoded in the access handles, src_handle and dest_handle, govern how data is actually copied from the source to the destination. Only matching data sizes between the source and destination are supported.

Data will automatically be translated to maintain a consistent view between the source and the destination. The translation may involve byte-swapping if the source and the destination devices have incompatible endian characteristics.

void ddi_device_zero(ddi_acc_handle_t handle,  	
		caddr_t dev_addr, size_t bytecount, ssize_t dev_advcnt, uint_t dev_datasz);

ddi_device_zero(9F) function fills the given bytecount with the number of bytes of zeroes to the device register or memory.

The dev_advcnt argument determines the value of the device address, dev_addr, on each access. A value of 0 will use the same device address, dev_addr, on every access. A positive value increments the device address in the next access, while a negative value decrements the address. The device address is incremented or decremented in dev_datasz units.

Polling

These interfaces support the poll(2) system call, which provides a mechanism for application programs to "poll" character-oriented devices, inquiring about their readiness to perform certain I/O operations. See the poll(2) manual page for details.

int nochpoll(dev_t dev, short events, int anyyet,  	
			short *reventsp, struct pollhead **pollhdrp);

Use nochpoll(9F) as the chpoll entry in the cb_ops(9S) structure if the driver does not support polling.

void pollwakeup(struct pollhead *php, short event);

If the driver does implement a chpoll(9E) entry point to support polling, it should call pollwakeup(9F) whenever the event occurs.

Power Management

These interfaces support power management functionality.

int ddi_dev_is_needed(dev_info_t *dip, int component,  	 
			int level);

ddi_dev_is_needed(9F) informs the system that a device component is needed at the specified power level. It sets the component to the required level and sets all of the devices on which it depends (see pm(7D)) to their normal power levels. If component 0 of the device is at power level 0, the ddi_dev_is_needed(9F) call will result in component 0 being returned to normal power and the device being resumed via attach(9E) before ddi_dev_is_needed(9F) returns.

int pm_create_components(dev_info_t *dip, int components);

pm_create_components(9F) creates power manageable components for a device. This function is called from the driver's attach(9F) entry point if a device has power manageable components.

void pm_destroy_components(dev_info_t *dip,);

pm_destroy_components(9F) removes power manageable components for a device. This function is called from the driver's detach(9F) entry point.

int pm_get_normal_power(dev_info_t *dip, int component);

pm_get_normal_power(9F) returns the normal power level of a device component.

void pm_set_normal_power(dev_info_t *dip,  	
			int component, int level);

pm_set_normal_power(9F) sets a device component to the specified power level.

int pm_busy_component(dev_info_t *dip, int component);

pm_busy_component(9F) sets a device component to busy. When a device component is busy, it will not be power-managed by the system.

int pm_idle_component(dev_info_t *dip, int component);

pm_idle_component(9F) sets a device component to idle. A device component that is idle is available to be power-managed by the system.

Printing System Messages

These interfaces are functions that display messages on the system console.

void cmn_err(int level, char *format, ...);

cmn_err(9F) is the mechanism for printing messages on the system console. level may be one of CE_NOTE, CE_WARN, CE_CONT, or CE_PANIC. CE_NOTE indicates a purely informational message. CE_WARN indicates a warning to the user. CE_CONT continues a previous message. And CE_PANIC issues a fatal error and crashes the system.


Note -

Use CE_PANIC only for unrecoverable system errors.


Whenever possible, CE_CONT should be used to print system messages. Note that CE_PANIC, CE_NOTE, and CE_WARN cause cmn_err(9F) to always append a new line to the message.

void ddi_report_dev(dev_info_t *dip);

ddi_report_dev(9F) possibly prints a message announcing the presence of a device on the system. Call this function before returning from a successful attach(9E).

char *sprintf(char *buf, const char *fmt, ...);

sprintf(9F) is just like the C library's sprintf(3). Use it to format a message and place it in buf.

void vcmn_err(int level, char *format, va_list ap);

vcmn_err(9F) is a version of cmn_err(9F) that uses varargs (see the stdarg(5) manual page).

char *vsprintf(char *buf, const char *fmt, va_list ap);

vsprintf(9F) is a version of sprintf(9F) that uses varargs (see the stdarg(5) manual page).

Process Signaling

These interfaces allow a device driver to send signals to a process in a multithread-safe manner.

void *proc_ref(void);

proc_ref(9F) retrieves an unambiguous reference to the process of the current thread for signaling purposes.

int proc_signal(void *pref, int sig);

proc_signal(9F) sends the signal indicated in sig to the process defined by pref that has been referenced by proc_ref(9F).

void proc_unref(void *pref);

proc_unref(9F) unreferences the process defined by pref.

Properties

Properties are name-value pairs defined by the PROM or the kernel at boot time, by hardware configuration files, or by calls to ddi_prop_create(9F). These interfaces handle creating, modifying, retrieving, and reporting properties.

int ddi_prop_create(dev_t dev, dev_info_t *dip,  	
			int flags, char *name, caddr_t valuep, int length);

ddi_prop_create(9F) creates a property of the name pointed to by name and the value pointed to by valuep.

int ddi_prop_modify(dev_t dev, dev_info_t *dip,  	
			int flags, char *name, caddr_t valuep, int length);

ddi_prop_modify(9F) changes the value of the property identified by name to the value pointed to by valuep.

int ddi_prop_update_int_array(dev_t dev, 
			dev_info_t *dip, char *name, int *data, u_int nelements);
int ddi_prop_update_int(dev_t dev, dev_info_t *dip,  	
			char *name, int data);
int ddi_prop_update_string_array(dev_t dev,  	
			dev_info_t *dip, char *name, char **data, u_int nelements);
int ddi_prop_update_string(dev_t dev, dev_info_t *dip,  	
			char *name, char *data);
int ddi_prop_update_byte_array(dev_t dev, 
			dev_info_t *dip, char *name, u_char *data, u_int nelements);

The property update routines search for and, if found, modify the value of a given property. Properties are searched for based on the dip, name, dev, and the type of the data (integer, string, or byte). The driver software properties list is searched. If the property is found, it is updated with the supplied value. If the property is not found on this list, a new property is created with the value supplied.

For example, if a driver attempts to update the foo property, a property named foo is searched for on the driver's software property list. If foo is found, the value is updated. If foo is not found, a new property named foo is created on the driver's software property list with the supplied value, even if a foo property exists on another property list (such as a PROM property list).

For the routines ddi_prop_update_int_array(9F), ddi_prop_update_string_array(9F), ddi_prop_update_string(9F), and ddi_prop_update_byte_array(9F), data is a pointer which points to memory containing the value of the property. In each case data points to a different type of property value.

int ddi_prop_remove(dev_t dev, dev_info_t *dip, char *name);

ddi_prop_remove(9F) frees the resources associated with the property identified by name.

void ddi_prop_remove_all(dev_info_t *dip);

ddi_prop_remove_all(9F) frees the resources associated with all properties belonging to dip. ddi_prop_remove_all(9F) should be called in the detach(9E) entry point if the driver defines properties.

int ddi_prop_undefine(dev_t dev, dev_info_t *dip,  	
			int flags, char *name);

ddi_prop_undefine(9F) marks the value of the property identified by name as temporarily undefined. The property continues to exist, however, and may be redefined later using ddi_prop_modify(9F).

int ddi_prop_op(dev_t dev, dev_info_t *dip,  	
			ddi_prop_op_t prop_op, int flags, char *name, caddr_t valuep, 
			int *lengthp);

ddi_prop_op(9F) is the generic interface for retrieving properties. ddi_prop_op(9F) should be used as the prop_op(9E) entry in the cb_ops(9S) structure if the driver does not have a prop_op(9E) routine. See "Properties" for more information.

int ddi_getprop(dev_t dev, dev_info_t *dip, int flags,  	
			char *name, int defvalue);

ddi_getprop(9F) is a wrapper around ddi_prop_op(9F). It can be used to retrieve Boolean and integer-sized properties.

int ddi_prop_exists(dev_t match_dev, dev_info_t *dip,  	
			u_int flags, char *name);

ddi_prop_exists(9F) checks for the existence of a property regardless of the property value data type.

int ddi_prop_get_int(dev_t match_dev, dev_info_t *dip,  			
			u_int flags, char *name, int defvalue);

ddi_prop_get_int(9F) searches for an integer property and, if found, returns the value of the property.

int ddi_getlongprop(dev_t dev, dev_info_t *dip,  	
			int flags, char *name, caddr_t valuep, int *lengthp);

ddi_getlongprop(9F) is a wrapper around ddi_prop_op(9F). It is used to retrieve properties having values of arbitrary length. The value returned is stored in a buffer allocated by kmem_alloc(9F), which the driver must free with kmem_free(9F) when the value is no longer needed.

int ddi_getlongprop_buf(dev_t dev, dev_info_t *dip,  	
			int flags, char *name, caddr_t
valuep,  	int *lengthp);

ddi_getlongprop_buf(9F) is a wrapper around ddi_prop_op(9F). It is used retrieve a property having a value of arbitrary length and to copy that value into a buffer supplied by the driver. valuep must point to this buffer.

int ddi_prop_lookup_int_array(dev_t match_dev,  
				dev_info_t *dip,u_int flags, char *name, int **datap, 
			u_int *nelementsp);
int ddi_prop_lookup_string_array(dev_t match_dev,  	
			dev_info_t *dip, u_int flags, char *name,  	
			char **datap, u_int *nelementsp);
int ddi_prop_lookup_string(dev_t match_dev,  	
			dev_info_t *dip, u_int flags, char *name, char **datap);
int ddi_prop_lookup_byte_array(dev_t match_dev,  	
			dev_info_t *dip,u_int flags, char *name, u_char **datap, 
			u_int *nelementsp);
void ddi_prop_free(void *data);

The property lookup routines search for and, if bound, returns the value of a given property. Properties are searched for based on the dip, name, match_dev, and the type of the data (integer, string, or byte). The property search order is as follows:

  1. Search software properties created by the driver.

  2. Search the software properties created by the system (or nexus nodes in the device info tree).

  3. Search the driver global properties list.

  4. If DDI_PROP_NOTPROM is not set, search the PROM properties (if they exist).

  5. If DDI_PROP_DONTPASS is not set, pass this request to the parent device information node.

  6. Return DDI_PROP_NOT_FOUND.

int ddi_getproplen(dev_t dev, dev_info_t *dip,  	
			int flags, char *name, int *lengthp);

ddi_getproplen(9F) is a wrapper around ddi_prop_op(9F) that passes back in the location pointed to by lengthp the length of the property identified by name.

Register and Memory Mapping

These interfaces support the mapping of device memory and device registers into kernel memory so that a device driver can address them.

int ddi_devmap_segmap(dev_t dev, off_t offset,  	
			struct as *as, caddr_t *addrp, off_t len, u_int prot, 
			u_int maxprot, u_int flags, cred_t *credp);

ddi_devmap_segmap(9F) supports the mmap(2) system call, which allows application programs to map device or kernel memory into their address spaces. ddi_devmap_segmap(9F) should be used as the segmap(9E) entry in the cb_ops(9S) structure.

int devmap_devmem_setup(devmap_cookie_t handle, dev_info_t *dip,  	
			struct devmap_callback_ctl *callbackops, u_int rnumber, offset_t roff, 
			size_t len,  	u_int maxprot, u_int flags, 
			ddi_device_acc_attr_t *accattrp);

devmap_devmem_setup(9F) sets up user mappings to device space. It is called from the devmap(9E) entry point to export a range of a register set specified by rnumber, roff and len.

int devmap_umem_setup(devmap_cookie_t handle,  	
			dev_info_t *dip, struct devmap_callback_ctl *callbackops,  	
			ddi_umem_cookie_t cookie, offset_t koff, size_t len, u_int maxprot, 
			u_int flags, ddi_device_acc_attr_t *accattrp);

devmap_umem_setup(9F) sets up user mappings to kernel memory. It is called from the devmap(9E) entry point to export a range of kernel memory specified by cookie, koff and len.

int devmap_load(devmap_cookie_t handle,  	
			offset_t offset, size_t len, uint_t type, uint_t rw);
int devmap_unload(devmap_cookie_t handle  	
			offset_t offset, size_t len);

devmap_load(9F) and devmap_unload(9F) control whether user accesses to the device mappings created by devmap_devmem_setup(9F) or devmap_umem_setup(9F) in the specified range will generate an access event notification to the device driver.

devmap_unload(9F) tells the system to intercept mapping accesses and invalidates the mapping translations. devmap_load(9F) prevents the system from intercepting mapping accesses and validates the mapping translations.

int ddi_dev_nregs(dev_info_t *dip, int *resultp);

ddi_dev_nregs(9F) passes back in the location pointed to by resultp the number of register specifications a device has.

int ddi_dev_regsize(dev_info_t *dip, u_int rnumber,  	
			off_t *resultp);

ddi_dev_regsize(9F) passes back in the location pointed to by resultp the size of the register set identified by rnumber on the device identified by dip.

int ddi_regs_map_setup(dev_info_t *dip, uint_t  	rnumber, 
			caddr_t *addrp, offset_t offset, offset_t len, 
			ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handlep);

ddi_regs_map_setup(9F) maps in the register set given by rnumber. The register number determines which register set is mapped if more than one exists.

void ddi_regs_map_free(ddi_acc_handle_t *handle);

ddi_regs_map_setup(9F) frees the mapping represented by the data access handle. This function is provided for drivers preparing to detach themselves from the system, allowing them to release allocated system resources represented in the handle.

int pci_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle);  
void pci_config_teardown(ddi_acc_handle_t *handle);

pci_config_setup(9F) sets up the necessary resources for enabling subsequent data accesses to the PCI local bus configuration space. pci_config_teardown(9F) reclaims and removes those resources represented by the data access handle returned from pci_config_setup(9F).

Device Context Management

The following functions are used to manage device context:

int devmap_do_ctxmgt(devmap_cookie_t cookie,  	
			void *pvtp, offset_t off, size_t len,u_int type, u_int rw,  	
int (*devmap_contextmgt)(devmap_cookie_t,  	void *, offset_t, size_t, u_int, u_int));

Device drivers call devmap_do_ctxmgt(9F) in the devmap_access(9E) entry point to perform device context switching on a mapping. devmap_do_ctxmgt(9F) passes a pointer to a driver supplied callback function, devmap_contextmgt(9E), to the system that will perform the actual device context switching.

int devmap_default_access(devmap_cookie_t cookie,  
  	void *pvtp, offset_t off, size_t len, u_int type, u_int rw);

devmap_default_access(9F) is a function providing the semantics of devmap_access(9E). The driver calls devmap_default_access(9F) to handle the mappings that do not support context switching. The driver should call devmap_do_ctxmgt(9F) for the mappings that support context management.

PCI Configuration

uint8_t pci_config_get8(ddi_acc_handle_t handle, off_t offset);
uint16_t pci_config_get16(ddi_acc_handle_t handle, off_t offset);
uint32_t pci_config_get32(ddi_acc_handle_t handle, off_t offset);
uint64_t pci_config_get64(ddi_acc_handle_t handle, off_t offset);
void pci_config_put8(ddi_acc_handle_t handle, 
			off_t offset, uint8_t value);
void pci_config_put16(ddi_acc_handle_t handle,  	
			off_t offset, uint16_t value);
void pci_config_put32(ddi_acc_handle_t handle,  	
			off_t offset, uint32_t value);
void pci_config_put64(ddi_acc_handle_t handle,  	
			off_t offset, uint64_t value);

These routines read or write a single datum of various sizes from or to the PCI local bus configuration space. The pci_config_get8(9F), pci_config_get16(9F), pci_config_get32(9F), and pci_config_get64(9F) functions read 8 bits, 16 bits, 32 bits, and 64 bits of data respectively. The pci_config_put8(9F), pci_config_put16(9F), pci_config_put32(9F), and pci_config_put64(9F) functions write 8 bits, 16 bits, 32 bits, and 64 bits of data respectively. The offset argument must be a multiple of the datum size.

Because the PCI local bus configuration space is represented in little-endian data format, these functions translate the data from or to native host format to or from little-endian format.

pci_config_setup(9F) must be called before invoking these functions.

I/O Port Access

These interfaces support the accessing of device registers from the device driver.

uint8_t ddi_io_get8(ddi_acc_handle_t handle,  	
			int dev_port);
uint16_t ddi_io_get16(ddi_acc_handle_t handle,  	
			int dev_port);
uint32_t ddi_io_get32(ddi_acc_handle_t handle,  	
			int dev_port);

These routines generate a read of various sizes from the device port, developer, in I/O space. The ddi_io_get8(9F), ddi_io_get16(9F), and ddi_io_get32(9F) functions read 8 bits, 16 bits, and 32 bits of data respectively from the device port, dev_port.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle.

The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_io_rep_get8(ddi_acc_handle_t handle,  	
			uint8_t *host_addr, int dev_port, size_t repcount);
void ddi_io_rep_get16(ddi_acc_handle_t handle,  
				uint16_t *host_addr, int dev_port, size_t repcount);
void ddi_io_rep_get32(ddi_acc_handle_t handle,  	
			uint32_t *host_addr, int dev_port, size_t repcount);

These routines generate multiple reads from the device port, dev_port, in I/O space, repcount data is copied from the device port, dev_port, to the host address, host_addr. For each input datum, the ddi_io_rep_get8(9F), ddi_io_rep_get16(9F), and ddi_io_rep_get32(9F) functions read 8 bits, 16 bits, and 32 bits of data, respectively, from the device port. host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_io_put8(ddi_acc_handle_t handle,  	
			int dev_port, uint8_t value);
void ddi_io_put16(ddi_acc_handle_t handle,  	
			int dev_port, uint16_t value);
void ddi_io_put32(ddi_acc_handle_t handle,  	
			int dev_port, uint32_t value);

These routines generate a write of various sizes to the device port, dev_port, in I/O space. The ddi_io_put8(9F), ddi_io_put16(9F), and ddi_io_put32(9F) functions write 8 bits, 16 bits, and 32 bits of data, respectively, to the device port, dev_port.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

void ddi_io_rep_put8(ddi_acc_handle_t handle,  	
			uint8_t *host_addr, int dev_port, size_t repcount);
void ddi_io_rep_put16(ddi_acc_handle_t handle,  	
			uint16_t *host_addr, int dev_port, size_t repcount);
void ddi_io_rep_put32(ddi_acc_handle_t handle,  
			uint32_t *host_addr, int dev_port, size_t repcount);

These routines generate multiple writes to the device port, dev_port, in I/O space. repcount data is copied from the host address, host_addr, to the device port, dev_port. For each input datum, the ddi_io_rep_put8(9F), ddi_io_rep_put16(9F), and ddi_io_rep_put32(9F) functions write 8 bits, 16 bits, and 32 bits of data, respectively, to the device port. host_addr must be aligned to the datum boundary described by the function.

Each individual datum is automatically translated to maintain a consistent view between the host and the device, based on the encoded information in the data access handle. The translation may involve byte-swapping if the host and the device have incompatible endian characteristics.

SCSI and SCSA

These interfaces are part of the Sun Common SCSI Interface, routines that support the writing of "target drivers" to drive SCSI devices. Most of these routines handle allocating SCSI command "packets," formulating SCSI commands within those packets, and "transporting" the packets to the host adapter driver for execution. See Chapter 13, SCSI Target Drivers.

int scsi_setup_cdb(union scsi_cdb *cdbp, u_char cmd, u_int addr, u_int cnt, u_int othr_cdb_data);

scsi_setup_cdb(9F) initializes a group 0, 1, 2, 4, or 5 type of command descriptor block pointed to by cdpb using cmd, addr, cnt, othr_cdb_data.

void makecom_g0(struct scsi_pkt *pkt, 
			struct scsi_device *devp, int flag, int cmd, int addr, int cnt);

makecom_g0(9F) formulates a group 0 SCSI command for the target device denoted by devp in the scsi_pkt(9S) structure pointed to by pkt. The target must be a nonsequential access device. Use makecom_g0_s(9F) to formulate group 0 commands for sequential access devices.

void makecom_g0_s(struct scsi_pkt *pkt,  	
			struct scsi_device *devp, int flag, int cmd, int cnt, int fixbit);

makecom_g0_s(9F) formulates a group 0 SCSI command for the sequential access target device denoted by devp in the scsi_pkt(9S) structure pointed to by pkt. Use makecom_g0(9F) to formulate group 0 commands for nonsequential access devices.

void makecom_g1(struct scsi_pkt *pkt,  	
			struct scsi_device *devp, int flag, int cmd, int addr, int cnt);

makecom_g1(9F) formulates a group 1 SCSI command for the target device denoted by devp in the scsi_pkt(9S) structure pointed to by pkt.

void makecom_g5(struct scsi_pkt *pkt,  	
			struct scsi_device *devp, int flag, int cmd, int addr, int cnt);

makecom_g5(9F) formulates a group 5 SCSI command for the target device denoted by devp in the scsi_pkt(9S) structure pointed to by pkt.

int scsi_abort(struct scsi_address *ap,  	
			struct scsi_pkt *pkt);

scsi_abort(9F) cancels the command encoded in the scsi_pkt(9S) structure pointed to by pkt at the SCSI address denoted by ap. To indicate the current target, pass in ap the sd_address field of the scsi_device(9S) structure for the target. To abort the current command, pass NULL for pkt.

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

scsi_alloc_consistent_buf(9F) allocates a buffer header and the associated data buffer for DMA transfer. This buffer is allocated from the IOPB space, which is considered consistent memory. If bp is NULL, a new buffer header is allocated using getrbuf(9F). If datalen is nonzero, a new buffer will be allocated using ddi_dma_mem_alloc(9F).

If callback is not NULL_FUNC and the requested DMA resources are not immediately available, the function pointed to by callback will be called when resources may have become available. callback can call )scsi_alloc_consistent_buf(9F) again. If callback is SLEEP_FUNC, )scsi_alloc_consistent_buf(9F) might block, waiting for resources.

char *scsi_cname(u_char cmd, char **cmdvec);

scsi_cname(9F) searches for the command code cmd in the command vector cmdvec, and returns the command name. Each string in cmdvec starts with a one-character command code, followed by the name of the command. To use scsi_cname(9F), the driver must define a command vector that contains strings of this kind for all the SCSI commands it supports.

struct scsi_pkt *scsi_dmaget(struct scsi_pkt *pkt,  	
			opaque_t dmatoken, int (*callback)(void));

scsi_dmaget(9F) allocates resources for an existing scsi_pkt(9S) structure pointed to by pkt. Pass in dmatoken a pointer to the buf(9S) structure that encodes original I/O request.

If callback is not NULL_FUNC and the requested DMA resources are not immediately available, the function pointed to by callback will be called when resources may have become available. callback can call scsi_dmaget(9F) again. If callback is SLEEP_FUNC, scsi_dmaget(9F) might block, waiting for resources.

char *scsi_dname(int dtype);

scsi_dname(9F) decodes the device type code dtype found in the INQUIRY data and returns a character string denoting this device type.

void scsi_free_consistent_buf(struct buf *bp);

scsi_free_consistent_buf(9F) frees a buffer header and consistent data buffer that was previously allocated using scsi_alloc_consistent_buf(9F).

int scsi_hba_attach_setup(dev_info_t *dip,  	
			ddi_dma_attr_t *hba_dma_attr,  	scsi_hba_tran_t *hba_tran, int hba_flags);

scsi_hba_attach_setup(9F) registers the DMA attributes hba_dma_attr and the transport vectors hba_tran of each instance of the HBA device defined by dip. The HBA driver can pass different DMA attributes, and transport vectors for each instance of the device, as necessary, to support any constraints imposed by the HBA itself.

scsi_hba_attach_setup(9F) use the dev_bus_ops field in the dev_ops(9S) structure. The HBA driver should initialize this field to NULL before calling scsi_hba_attach_setup(9F).

int scsi_hba_detach(dev_info_t *dip);

scsi_hba_detach(9F) removes the reference to the DMA attributes structure and the transport vector for the given instance of an HBA driver.

int scsi_ifgetcap(struct scsi_address *ap, char *cap, int whom);

scsi_ifgetcap(9F) returns the current value of the host adapter capability denoted by cap for the host adapter servicing the target at the SCSI address pointed to by ap. See the manual page for a list of supported capabilities. whom indicates whether the capability applies only to the target at the specified SCSI address, or to all targets serviced by the host adapter.

int scsi_ifsetcap(struct scsi_address *ap,  	
			char *cap, int value, int whom);

scsi_ifsetcap(9F) sets the current value of the host adapter capability denoted by cap, for the host adapter servicing the target at the SCSI address pointed to by ap, to value. See the manual page for a list of supported capabilities. whom indicates whether the capability applies only to the target at the specified SCSI address, or to all targets serviced by the host adapter.

struct scsi_pkt *scsi_init_pkt(struct scsi_address *ap, 
			struct scsi_pkt *pktp, struct buf *bp, int cmdlen, int statuslen,  	
			int privatelen, int flags, int (*callback)(caddr_t), caddr_t arg);

scsi_init_pkt(9F) requests the transport layer to allocate a command packet for commands and, possibly, data transfers. If pktp is NULL, a new scsi_pkt(9S) is allocated. If bp is non-NULL and contains a valid byte count, the buf(9S) structure is set up for DMA transfer. If bp was allocated by scsi_alloc_consistent_buf(9F), the PKT_CONSISTENT flag must be set. If privatelen is set, additional space is allocated for the pkt_private area of the scsi_pkt(9S) structure; otherwise, pkt_private is a pointer that is typically used to store the bp during execution of the command. The flags are set in the command portion of the scsi_pkt(9S) structure.

If callback is not NULL_FUNC and the requested DMA resources are not immediately available, the function pointed to by callback will be called when resources may have become available. callback can call scsi_init_pkt(9F) again. If callback is SLEEP_FUNC, scsi_init_pkt(9F) might block, waiting for resources.

char *scsi_mname(u_char msg);

scsi_mname(9F) decodes the SCSI message code msg and returns the corresponding message string.

int scsi_poll(struct scsi_pkt *pkt);

scsi_poll(9F) transports the command packet pointed to by pkt to the host adapter driver for execution and waits for it to complete before it returns. Use scsi_poll(9F) sparingly and only for commands that must execute synchronously.

int scsi_probe(struct scsi_device *devp, int (*callback)(void *));

scsi_probe(9F) determines whether a target or lun is present and sets up the )scsi_device(9S) structure with inquiry data. scsi_probe(9F) uses the SCSI INQUIRY command to test if the device exists. It may retry the INQUIRY command as appropriate. If scsi_probe(9F) is successful, it will fill in the scsi_inquiry(9S) structure pointed to by the sd_inq member of the scsi_device(9S) structure, and return SCSI_PROBE_EXISTS.

If callback is not NULL_FUNC and necessary resources are not immediately available, the function pointed to by callback will be called when resources may have become available. If callback is SLEEP_FUNC, scsi_probe(9F) might block, waiting for resources.

int scsi_reset(struct scsi_address *ap, int level);

scsi_reset(9F) requests the host adapter driver to reset the target at the SCSI address pointed to by ap if level is RESET_TARGET. If level is RESET_ALL, the entire SCSI bus is reset.

char *scsi_rname(u_char reason);

scsi_rname(9F) decodes the packet completion reason code reason, and returns the corresponding reason string.

char *scsi_sname(u_char sense_key);

scsi_sname(9F) decodes the SCSI sense key sense_key, and returns the corresponding sense key string.

int scsi_transport(struct scsi_pkt *pkt);

scsi_transport(9F) requests the host adapter driver to schedule the command packet pointed to by pkt for execution. Use scsi_transport(9F) to issue most SCSI commands. scsi_poll(9F) may be used to issue synchronous commands.

void scsi_unprobe(struct scsi_device *devp);

scsi_unprobe(9F) is used to free any resources that were allocated on the driver's behalf during scsi_unprobe(9F).

Soft State Management

These interfaces comprise the soft-state structure allocator, a facility that simplifies the management of state structures for driver instances. For best results, use these routines to keep track of per-instance data.

int ddi_soft_state_init(void **state_p,  	
			size_t size, size_t n_items);

ddi_soft_state_init(9F) sets up the soft-state allocator to keep track of soft-state structures for all device instances. state_p points to a pointer to an opaque object that keeps track of the soft-state structures.

void ddi_soft_state_fini(void **state_p);

ddi_soft_state_fini(9F) is the inverse operation to ddi_soft_state_init(9F). state_p points to a pointer to an opaque object that keeps track of the soft-state structures.

int ddi_soft_state_zalloc(void *state, int item);

ddi_soft_state_zalloc(9F) allocates and zeroes a new instance of a soft-state structure. state points to an opaque object that keeps track of the soft-state structures.

void *ddi_get_soft_state(void *state, int item);

ddi_get_soft_state(9F) returns a pointer to the soft-state structure for the device instance item. state points to an opaque object that keeps track of the soft-state structures.

void ddi_soft_state_free(void *state, int item);

ddi_soft_state_free(9F) releases the resources associated with the soft-state structure for item. state points to an opaque object that keeps track of the soft-state structures.

String Manipulation

These interfaces are generic string manipulation utilities similar to, and in most cases identical to the routines of the same names defined in the standard C library used by application programmers.

int stoi(char **str);

stoi(9F) converts the ASCII decimal numeric string pointed to by *str to an integer and returns the integer. *str is updated to point to the last character examined.

void numtos(unsigned long num, char *s);

numtos(9F) converts the integer num to an ASCII decimal string and copies the string to the location pointed to by s. The driver must provide the storage for the string s and ensure that it can contain the result.

char *strchr(const char *str, int chr);

strchr(9F) returns a pointer to the first occurrence of the character chr in the string pointed to by str, or NULL, if chr is not found in the string.

int strcmp(const char *s1, const char *s2);

strcmp(9F) compares two null-terminated character strings. It returns zero if they are identical; otherwise, it returns a nonzero value.

int strncmp(const char *s1, const char *s2, size_t n);

strncmp(9F) compares the first n characters of the two strings. It returns zero if these characters are identical; otherwise, it returns a nonzero value.

char *strcpy(char *dst, const char *srs);

strcpy(9F) copies the character string pointed to by srs to the location pointed to by dst. The driver must provide storage for the string dst and ensure that it is long enough.

char *strncpy(char *dst, const char *srs, size_t n);

strncpy(9F) copies n characters from the string pointed to by srs to the string pointed to by dst. The driver must provide storage for the string dst and ensure that it is long enough.

size_t strlen(const char *sp);

strlen(9F) returns the length of the character string pointed to by sp, not including the null-termination character.

System Information

These interfaces return current information about the system, such as the root node of the system dev_info tree, and the values of certain system-wide parameters.

dev_info_t *ddi_root_node(void);
ddi_root_node(9F) returns a pointer to the root node of the system dev_info tree. Device drivers rarely use this.

clock_t ddi_get_lbolt(void);
ddi_get_lbolt(9F) returns the number of clock ticks since the last system reboot for use as a timer.

time_t ddi_get_time(void);
ddi_get_time(9F) returns the current time in seconds since 00:00 UTC, January 1, 1970. This value can be used to set or wait for expiration intervals.

pid_t ddi_get_pid(void);
ddi_get_pid(9F) returns the process id of the current process.

Thread Synchronization

These interfaces enable a device to exploit multiple CPUs on multiprocessor machines. They prevent the corruption of data by simultaneous access by more than one thread. The mechanisms for thread synchronization are mutual exclusion locks (mutexes), condition variables, readers/writer locks, and semaphores.

void cv_init(kcondvar_t *cvp, char *name,  	
			kcv_type_t type, void *arg);

cv_init(9F) prepares the condition variable pointed to by cvp for use. CV_DRIVER should be specified for type.

void cv_destroy(kcondvar_t *cvp);

cv_destroy(9F) releases the resources associated with the condition variable pointed to by cvp.

void cv_wait(kcondvar_t *cvp, kmutex_t *mp);

cv_wait(9F) must be called while holding the mutex pointed to by mp. cv_wait(9F) releases the mutex and blocks until a call is made to cv_signal(9F) or cv_broadcast(9F) for the condition variable pointed to by cvp. cv_wait(9F) then reacquires the mutex and returns.

Use cv_wait(9F) to block on a condition that may take a while to change.

void cv_signal(kcondvar_t *cvp);

cv_signal(9F) unblocks one cv_wait(9F) call that is blocked on the condition variable pointed to by cvp. Call cv_signal(9F) when the condition that cv_wait(9F) is waiting for becomes true. To unblock all threads blocked on this condition variable, use cv_broadcast(9F).

void cv_broadcast(kcondvar_t *cvp);

cv_broadcast(9F) unblocks all threads that are blocked on the condition variable pointed to by cvp. To unblock only one thread, use cv_signal(9F).

int cv_wait_sig(kcondvar_t *cvp, kmutex_t *mp);

cv_wait_sig(9F) is like cv_wait(9F), but if the calling thread receives a signal while cv_wait_sig(9F) is blocked, cv_wait_sig(9F) immediately reacquires the mutex and returns zero.

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

cv_timedwait(9F) is like cv_wait(9F), but it returns -1 at time timeout if the condition has not occurred. timeout is given as a number of clock ticks since the last reboot. drv_usectohz(9F) converts microseconds, a platform-independent time, to clock ticks.

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

cv_timedwait_sig(9F) is like cv_timedwait(9F) and cv_wait_sig(9F), except that it returns -1 at time timeout if the condition has not occurred. If the calling thread receives a signal while cv_timedwait_sig(9F) is blocked, cv_timedwait_sig(9F) immediately returns zero. In all cases, cv_timedwait_sig(9F) reacquires the mutex before returning.

void mutex_init(kmutex_t *mp, char *name,  	
			kmutex_type_t type, void *arg);

mutex_init(9F) prepares the mutual exclusion lock pointed to by mp for use. MUTEX_DRIVER should be specified for type, and pass an interrupt block cookie of type ddi_iblock_cookie_t for arg. The interrupt block cookie is returned by ddi_get_iblock_cookie(9F).

void mutex_enter(kmutex_t *mp);

mutex_enter(9F) acquires the mutual exclusion lock pointed to by mp. If another thread holds the mutex, mutex_enter(9F) will either block or spin, waiting for the mutex to become available.

Mutexes are not reentrant; if a thread calls mutex_enter(9F) on a mutex it already holds, the system will panic.

mp is assumed to protect a certain set of data, often a single data structure, and all driver threads accessing those data must first acquire the mutex by calling mutex_enter(9F). This is accomplished by mutual agreement and consistency among all driver code paths that access the data in question; mutex_enter(9F) in no way prevents other threads from accessing the data. It is only when all driver code paths agree to acquire the mutex before accessing the data that the data are safe.

void mutex_exit(kmutex_t *mp);

mutex_exit(9F) releases the mutual exclusion lock pointed to by mp.

void mutex_destroy(kmutex_t *mp);

mutex_destroy(9F) releases the resources associated with the mutual exclusion lock pointed to by mp.

int mutex_owned(kmutex_t *mp);

mutex_owned(9F) returns nonzero if the mutual exclusion lock pointed to by mp is currently held; otherwise, it returns zero. Use mutex_owned(9F) only in an expression used in ASSERT(9F).

int mutex_tryenter(kmutex_t *mp);

mutex_tryenter(9F) is similar to mutex_enter(9F), but it does not block waiting for the mutex to become available. If the mutex is held by another thread, mutex_tryenter(9F) returns zero. Otherwise, mutex_tryenter(9F) acquires the mutex and returns nonzero.

void rw_destroy(krwlock_t *rwlp);

rw_destroy(9F) releases the resources associated with the readers or writer lock pointed to by rwlp.

void rw_downgrade(krwlock_t *rwlp);

If the calling thread holds the lock pointed to by rwlp for writing, rw_downgrade(9F) releases the lock for writing, but retains the lock for reading. This allows other readers to acquire the lock unless a thread is waiting to acquire the lock for writing.

void rw_enter(krwlock_t *rwlp, krw_t enter_type);

If enter_type is RW_READER, rw_enter(9F) acquires the lock pointed to by rwlp for reading if no thread currently holds the lock for writing, and if no thread is waiting to acquire the lock for writing. Otherwise, rw_enter(9F) blocks.

If enter_type is RW_WRITER, rw_enter(9F) acquires the lock for writing if no thread holds the lock for reading or writing, and if no other thread is waiting to acquire the lock for writing. Otherwise, rw_enter(9F) blocks.

void rw_exit(krwlock_t *rwlp);

rw_exit(9F) releases the lock pointed to by rwlp.

void rw_init(krwlock_t *rwlp, char *name,  	
			krw_type_t type, void *arg);

rw_init(9F) prepares the readers or writer lock pointed to by rwlp for use. RW_DRIVER should be passed for type.

int rw_read_locked(krwlock_t *rwlp);

The lock pointed to by rwlp must be held during a call to rw_read_locked(9F). If the calling thread holds the lock for reading, rw_read_locked(9F) returns a nonzero value. If the calling thread holds the lock for writing, rw_read_locked(9F) returns zero.

int rw_tryenter(krwlock_t *rwlp, krw_t enter_type);

rw_tryenter(9F) attempts to enter the lock, like rw_enter(9F), but never blocks. It returns a nonzero value if the lock was successfully entered, and zero otherwise.

int rw_tryupgrade(krwlock_t *rwlp);

If the calling thread holds the lock pointed to by rwlp for reading, rw_tryupgrade(9F) acquires the lock for writing if no other threads hold the lock, and no thread is waiting to acquire the lock for writing. If rw_tryupgrade(9F) cannot acquire the lock for writing, it returns zero.

void sema_init(ksema_t *sp, u_int val, char *name,  	
			ksema_type_t type, void *arg);

sema_init(9F) prepares the semaphore pointed to by sp for use. SEMA_DRIVER should be passed for type. count is the initial count for the semaphore, which usually should be 1 or 0. In almost all cases, drivers should pass 1 for count.

void sema_destroy(ksema_t *sp);

sema_destroy(9F) releases the resources associated with the semaphore pointed to by sp.

void sema_p(ksema_t *sp);

sema_p(9F) acquires the semaphore pointed to by sp by decrementing the counter if its value is greater than zero. If the semaphore counter is zero, sema_p(9F) blocks, waiting to acquire the semaphore.

int sema_p_sig(ksema_t *sp);

sema_p_sig(9F) is like sema_p(9F), except that if the calling thread has a signal pending, and the semaphore counter is zero, sema_p_sig(9F) returns zero without blocking.

void sema_v(ksema_t *sp);

sema_v(9F) releases the semaphore pointed to by sp by incrementing its counter.

int sema_tryp(ksema_t *sp);

sema_tryp(9F) is similar to sema_p(9F), but if the semaphore counter is zero, sema_tryp(9F) immediately returns zero.

Timing

These are delay and time value conversion routines.

void delay(clock_t ticks);

delay(9F) blocks the calling thread for at least ticks clock ticks (timeout(9F)).

void drv_usecwait(clock_t microsecs);

drv_usecwait(9F) busy-waits for microsecs microseconds.

clock_t drv_hztousec(clock_t hertz);

drv_hztousec(9F) converts hertz clock ticks to microseconds, and returns the number of microseconds.

clock_t drv_usectohz(clock_t microsecs);

drv_usectohz(9F) converts microsecs microseconds to clock ticks, and returns the number of clock ticks.

uio(9S) Handling

These interfaces all handle moving data using the uio(9S) data structure.

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

uiomove(9F) copies data between the address and the uio(9S) structure pointed to by uio_p. If rwflag is UIO_READ, data are transferred from address to a data buffer associated with the uio(9S) structure. If rwflag is UIO_WRITE, data are transferred from a data buffer associated with the uio(9S) structure to address.

int ureadc(int c, uio_t *uio_p);

ureadc(9F) appends the character c to a data buffer associated with the uio(9S) structure pointed to by uio_p.

int uwritec(uio_t *uio_p);

uwritec(9F) removes a character from a data buffer associated with the uio(9S) structure pointed to by uio_p, and returns the character.

Utility Functions

These interfaces are miscellaneous utilities that the driver may use.

void ASSERT(EX);

The ASSERT(9F) macro does nothing if EX evaluates to nonzero. If EX evaluates to zero, ASSERT(9F) panics the system. ASSERT(9F) is useful in debugging a driver, since it can be used to stop the system when an unexpected situation is encountered, such as an erroneously NULL pointer.

ASSERT(9F) exhibits this behavior only when the DEBUG preprocessor symbol is defined.

int bcmp(const void *s1, const void *s2, size_t len);

bcmp(9F) compares len bytes of the byte arrays starting at s1 and s2. If these bytes are identical, bcmp(9F) returns zero. Otherwise, bcmp(9F) returns a nonzero value.

unsigned long btop(unsigned long numbytes);

btop(9F) converts a size n expressed in bytes to a size expressed in terms of the main system MMU page size, rounded down to the nearest page.

unsigned long btopr(unsigned long numbytes);

btopr(9F) converts a size n expressed in bytes to a size expressed in terms of the main system MMU page size, rounded up to the nearest page.

void bzero(void *addr, size_t bytes);

bzero(9F) zeroes bytes starting at addr.

unsigned long ddi_btop(dev_info_t *dip, unsigned long bytes);

ddi_btop(9F) converts a size expressed in bytes to a size expressed in terms of the parent bus nexus page size, rounded down to the nearest page.

unsigned long ddi_btopr(dev_info_t *dip, unsigned long bytes);

ddi_btopr(9F) converts a size expressed in bytes to a size expressed in terms of the parent bus nexus page size, rounded up to the nearest page.

unsigned long ddi_ptob(dev_info_t *dip, unsigned long pages);

ddi_ptob(9F) converts a size expressed in terms of the parent bus nexus page size to a size expressed in bytes.

int ddi_ffs(long mask);

ddi_ffs(9F) returns the number of the first (least-significant) bit set in mask.

int ddi_fls(long mask);

ddi_ffs(9F) returns the number of the last (most-significant) bit set in mask.

caddr_t ddi_get_driver_private(dev_info_t *dip);

ddi_get_driver_private(9F) returns a pointer to the data stored in the driver-private area of the dev_info node identified by dip.

void ddi_set_driver_private(dev_info_t *dip, caddr_t data);

ddi_set_driver_private(9F) sets the driver-private data of the dev_info node identified by dip to the value data.

int ddi_peek8(dev_info_t *dip, int8_t *addr, int8_t *valuep);

ddi_peek8(9F) reads 8-bit from the address addr to the location pointed to by valuep.

int ddi_peek16(dev_info_t *dip, int16_t *addr, int16_t *valuep);

ddi_peek16(9F) reads 16-bit from the address addr to the location pointed to by valuep.

int ddi_peek32(dev_info_t *dip, int32_t *addr, int32_t *valuep);

ddi_peek32(9F) reads 32-bit from the address addr to the location pointed to by valuep.

int ddi_peek64(dev_info_t *dip, int64_t *addr, int64_t *valuep);

ddi_peek64(9F) reads 64-bit from the address addr to the location pointed to by valuep.

int ddi_poke8(dev_info_t *dip, int8_t *addr, int8_t value);

ddi_poke8(9F) writes the data in value to the address addr.

int ddi_poke16(dev_info_t *dip, int16_t *addr, int16_t value);

ddi_poke16(9F) writes the data in value to the address addr.

int ddi_poke32(dev_info_t *dip, int32_t *addr, int32_t value);

ddi_poke32(9F) writes the data in value to the address addr.

int ddi_poke64(dev_info_t *dip, int64_t *addr, int64_t value);

ddi_poke64(9F) writes the data in value to the address addr.

major_t getmajor(dev_t dev);

getmajor(9F) decodes the major device number from dev and returns it.

minor_t getminor(dev_t dev);

getminor(9F) decodes the minor device number from dev and returns it.

dev_t makedevice(major_t majnum, minor_t minnum);

makedevice(9F) constructs and returns a device number of type dev_t from the major device number majnum and the minor device number minnum.

int max(int int1, int int2);

max(9F) returns the larger of the integers int1 and int2.

int min(int int1, int int2);

min(9F) returns the lesser of the integers int1 and int2.

int nodev();

nodev(9F) returns an error. Use nodev(9F) as the entry in the cb_ops(9S) structure for any entry point for which the driver must always fail.

int nulldev();

nulldev(9F) always returns zero, a return which for many entry points implies success. See the manual pages in Section 9 of the Solaris 7 Reference Manual Collection to learn about entry point return semantics.

unsigned long ptob(unsigned long numpages);

ptob(9F) converts a size expressed in terms of the main system memory management unit (MMU) page size to a size expressed in bytes.