Contained Within
Find More Documentation
Featured Support Resources
| Scarica il manuale in formato PDF
Drivers for Character Devices
8
- This chapter describes the structure of a character device driver. The entry points of a character device driver are the main focus, and the use of physio(9F) in read(9E) and write(9E) is also explained.
Entry Points
- Associated with each device driver is a dev_ops(9S) structure, which in turn refers to a cb_ops(9S) structure. These structures contain pointers to the driver entry points, and must be set by the driver. Table 8-1 lists the character device driver entry points.
-
Table 8-1
| Entry Point | Description |
| _init(9E) | Initializes the loadable driver module. |
| _info(9E) | Returns the loadable driver module information. |
| _fini(9E) | Prepares a loadable driver module for unloading. |
| identify(9E) | Identifies if the device driver supports a physical device. |
| probe(9E) | Determines if a device is present. |
| attach(9E) | Performs device-specific initialization. |
| detach(9E) | Removes device-specific state. |
| getinfo(9E) | Gets device driver information. |
-
Table 8-1
| Entry Point | Description |
| open(9E) | Gains access to a device. |
| close(9E) | Relinquishes access to a device. |
| read(9E) | Read data from device. |
| write(9E) | Write data to device. |
| ioctl(9E) | Perform arbitrary operation. |
| prop_op(9E) | Manages arbitrary driver properties. |
| mmap(9E) | Checks virtual mapping for a memory mapped device. |
| segmap(9E) | Maps device memory into user space. |
| chpoll(9E) | Poll device for events. |
-
Note - Some of these entry points may be replaced by nodev(9F) or nulldev(9F) as appropriate.
Autoconfiguration
- The attach(9E) routine should perform the common initialization tasks that all devices require. Typically, these tasks include:
-
- Allocating per-instance state structures
- Mapping the device's registers
- Registering device interrupts
- Initializing mutex and condition variables
- Creating minor nodes
- Character device drivers create minor nodes of type S_IFCHR. This causes a character special file representing the node to eventually appear in the /devices hierarchy.
-
Code Example 8-1 Character driver attach(9E) routine
-
-
static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
-
-
switch (cmd) {
case DDI_ATTACH:
allocate a state structure and initialize it.
map the device's registers.
add the device driver's interrupt handler(s).
initialize any mutexes and condition variables.
/*
* Create the device's minor node. Note that the node_type
* argument is set to DDI_NT_TAPE.
*/
if (ddi_create_minor_node(dip, "minor_name", S_IFCHR,
minor_number, DDI_NT_TAPE, 0) == DDI_FAILURE) {
free resources allocated so far
/* Remove any previously allocated minor nodes */
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
...
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
- For a description of the autoconfiguration process see Chapter 5, "Autoconfiguration."
Controlling Device Access
- Access to a device by one or more application programs is controlled through the open(9E) and close(9E) entry points. The open(9E) routine of a character driver is always called whenever an open(2) system call is issued on a special file representing the device. For a particular minor device, open(9E) may be called many times, but the close(9E) routine is called only when the final reference to a device is removed. If the device is accessed through file descriptors, this is by a call to close(2) or exit(2). If the device is accessed through memory mapping, this could also be by a call to munmap(2).
open( )
-
-
int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp);
- The primary function of open(9E) is to verify that the open request is allowed.
-
Code Example 8-2 Character driver open(9E) routine.
-
-
static int
xxopen(dev_t *devp, int flag, int otyp, cred_t *credp)
{
int instance;
if (getminor(*devp) is invalid)
return (EINVAL);
instance = getminor(*devp); /* one-to-one example mapping */
/* Is the instance attached? */
if (ddi_get_soft_state(statep, instance) == NULL)
return (ENXIO);
/* verify that otyp is appropriate */
if (otyp != OTYP_CHR)
return (EINVAL);
if ((flag & FWRITE) && drv_priv(credp) == EPERM)
return (EPERM);
return (0);
}
-
devp is a pointer to a device number. The open(9E) routine is passed a pointer so that the driver can change the minor number. This allows drivers to dynamically create minor instances of the device. An example of this might be a pseudo-terminal driver that creates a new pseudo-terminal whenever the driver is opened. A driver that chooses the minor number dynamically, normally creates only one minor device node in attach(9E) with ddi_create_minor_node(9F), then changes the minor number component of *devp using makedevice(9F) and getmajor(9F):
-
-
*devp = makedevice(getmajor(*devp), new_minor);
- The driver must keep track of available minor numbers internally.
-
otyp indicates how open(9E) was called. The driver must check that the value of otyp is appropriate for the device. For character drivers, otyp should be OTYP_CHR (see the open(9E) manual page).
-
flag contains bits indicating whether the device is being opened for reading (FREAD), writing (FWRITE), or both. Threads issuing the open(2) can also request exclusive access to the device (FEXCL) or specify that the open should
- not block for any reason (FNDELAY), but it is up to the driver to enforce both cases. A driver for a write-only device such as a printer might consider an open for reading invalid.
-
credp is a pointer to a credential structure containing information about the caller, such as the user ID and group IDs. Drivers should not examine the structure directly, but should instead use drv_priv(9F) to check for the common case of root privileges. In this example, only root is allowed to open the device for writing.
close( )
-
-
int xxclose(dev_t dev, int flag, int otyp, cred_t *credp);
-
close(9E) should perform any cleanup necessary to finish using the minor device, and prepare the device (and driver) to be opened again. For example, the open routine might have been invoked with the exclusive access (FEXCL) flag. A call to close(9E) would allow further opens to continue. Other functions that close(9E) might perform are:
-
- Wait for I/O to drain from output buffers before returning.
- Rewind a tape (tape device).
- Hang up the phone line (modem device).
I/O Request Handling
- This section gives the details of I/O request processing: from the application to the kernel, the driver, the device, the interrupt handler, and back to the user.
User Addresses
- When a thread issues a write(2) system call, it passes the address of a buffer in user space:
-
-
char buffer[] = "python";
count = write(fd, buffer, strlen(buffer) + 1);
- The system builds a uio(9S) structure to describe this transfer by allocating an iovec(9S) structure and setting the iov_base field to the address passed to write(2); in this case, buffer. The uio(9S) structure is what is passed to the driver write(9E) routine (see "Vectored I/O" for more information about the uio(9S) structure).
- A problem is that this address is in user space, not kernel space, and so is not guaranteed to be currently in memory. It is not even guaranteed to be a valid address. In either case, accessing a user address directly could crash the system, so device drivers should never access user addresses directly. Instead, they should always use one of the data transfer routines in the Solaris 2.x DDI/DKI that transfer data into or out of the kernel; see "Copying Data" on page 313 and "uio(9S) Handling" on page 352 for a summary of the available routines. These routines are able to handle page faults, either by bringing the proper user page in and continuing the copy transparently, or by returning an error on an invalid access.
- Two routines commonly used are copyout(9F) to copy data from the driver to user space, and copyin(9F) to copy data from user space to the driver. ddi_copyout(9F) and ddi_copyin(9F) operate similarly but are to be used in the ioctl(9E) routine. copyin(9F) and copyout(9F) can be used on the buffer described by each iovec(9S) structure, or uiomove(9F) can perform the entire transfer to or from a contiguous area of driver (or device) memory.
Vectored I/O
- In character drivers, transfers are described by a uio(9S) structure. The uio(9S) structure contains information about the direction and size of the transfer, plus an array of buffers describing one end of the transfer (the other end is the device). Below is a list of uio(9S) structure members that are important to character drivers.
-
-
iovec_t *uio_iov; /* base address of the iovec buffer */
/* description array */
int uio_iovcnt;/* the number of iovec structures */
off_t uio_offset;/* offset into device where data is */
/* transferred from or to */
offset_t uio_loffset/* 64-bit offset into file where data */
/* is transferred from or to. See NOTES.*/
int uio_resid; /* amount (in bytes) not transferred on */
/* completion */
- A uio(9S) structure is passed to the driver read(9E) and write(9E) entry points. This structure is generalized to support what is called gather-write and scatter-read. When writing to a device, the data buffers to be written do not have to be contiguous in application memory. Similarly, when reading from a device into memory, the data comes off the device in a contiguous stream but can go into a noncontiguous area of application memory. See readv(2), writev(2), pread(2) and pwrite(2) for more information on scatter/gather I/O.
- Each buffer is described by an iovec(9S) structure. This structure contains a pointer to the data area and the number of bytes to be transferred.
-
-
caddr_t iov_base; /* address of buffer */
int iov_len; /* amount to transfer */
- The uio structure contains a pointer to an array of iovec(9S) structures. The base address of this array is held in uio_iov, and the number of elements is stored in uio_iovcnt.
- The uio_offset field contains the 32-bit offset into the device that the application wants to begin the transfer at. uio_loffset is used for 64-bit file offsets. If the device does not support the notion of an offset these fields can be safely ignored. The driver should intrepret either uio_offset or uio_loffset (but not both). The driver determines the offset used according to the settings of the flags field in the cp_ops(9S) structure.
- The uio_resid field starts out as the amount of data to be transferred (the sum of all the iov_len fields in uio_iov), and must be set by the driver to the amount of data not transferred before returning. The read(2) and write(2) system calls use the return value from the read(9E) and write(9E) entry points to determine if the transfer failed (and then return -1). If the return value indicates success, the system calls return the number of bytes requested minus uio_resid. If uio_resid is not changed by the driver, the read(2) and write(2) calls will return 0 (indicating end-of-file), even though all the data was transferred.
- The support routines uiomove(9F) and physio(9F) update the uio(9S) structure directly. If they are used, no driver adjustments are necessary.
Driver Operations
- The read(9E) or write(9E) entry point is called when a user thread issues the corresponding system call. It is the responsibility of these routines to perform the desired transfer, then return an indication of success or failure.
Programmed I/O Transfers.
- Programmed I/O (PIO) devices rely on the CPU to perform the data transfer. PIO data transfers are identical to other device register read and write operations. An assignment statement is used to move a value from one variable or structure to another.
-
uiomove( ) This kind of device may allow the driver to use uiomove(9F). uiomove(9F) transfers data between the user space (defined by the uio(9S) structure) and the kernel. uiomove(9F) can handle page faults so the memory to which data is transferred need not be locked down. It also updates the uio_resid field in the uio(9S) structure. The following example is one way to write the ramdisk read(9E) routine, and relies on the following fields being present in the ramdisk state structure:
-
-
caddr_t ram; /* base address of ramdisk */
int ramsize; /* size of the ramdisk */
-
Code Example 8-3 Ramdisk read(9E) routine using uiomove(9F)
-
-
static int
rd_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
int instance;
rd_devstate_t *rsp;
instance = getminor(dev);
rsp = ddi_get_soft_state(rd_statep, instance);
if (rsp == NULL)
return (ENXIO);
if (uiop->uio_offset >= rsp->ramsize)
return (EINVAL);
/*
* uiomove takes the offset into the kernel buffer,
* the data transfer count (minimum of the requested and
* the remaining data), the UIO_READ flag, and a pointer
-
-
* to the uio structure.
*/
return (uiomove(rsp->ram + uiop->uio_offset,
min(uiop->uio_resid, rsp->ramsize - uiop->uio_offset),
UIO_READ, uiop));
}
-
uwritec( )and ureadc( ) Another example might be a driver writing data directly to the device's memory, which must be performed one byte at a time. Each byte is retrieved from the uio(9S) structure using uwritec(9F), then sent to the device. read(9E) can use ureadc(9F) to transfer a byte from the device to the area described by the uio(9S) structure.
-
Code Example 8-4 Programmed I/O write(9E) routine using uwritec(9F).
-
-
static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{
int instance;
int value;
struct xxstate *xsp;
struct device_reg *regp;
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL)
return (ENXIO);
regp = xsp->regp;
while (uiop->uio_resid > 0) {
/*
* do the PIO access
*/
value = uwritec(uiop);
if (value == -1)
return (EFAULT);
regp->data = (u_char)value;
regp->csr = START_TRANSFER;
/* this device requires a ten microsecond delay */
/* between writes */
drv_usecwait(10);
}
return (0);
}
DMA Transfers
- Many character drivers (especially those with DMA capabilities) use physio(9F) to do most of the work.
-
-
int physio(int (*strat)(struct buf *), struct buf *bp,
dev_t dev, int rw, void (*mincnt)(struct buf *),
struct uio *uio);
-
physio(9F) requires the driver to provide a strategy(9E) entry point (though it does not get placed in the cb_ops(9S) structure). physio(9F) ensures that memory space is locked down (cannot be paged out) for the duration of the data transfer. This is necessary for DMA transfers because they cannot handle page faults. physio(9F) also provides an automated way of breaking a larger transfer into a series of smaller, more manageable ones.
-
Code Example 8-5 read(9E) and write(9E) routines using physio(9F)
-
-
static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
int instance;
struct xxstate *xsp;
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL)
return (ENXIO);
return (physio(xxstrategy, NULL, dev, B_READ, xxminphys, uiop));
}
static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{
int instance;
struct xxstate *xsp;
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL)
return (ENXIO);
return (physio(xxstrategy, NULL, dev, B_WRITE, xxminphys,uiop));
}
- In the call to physio(9F), xxstrategy is a pointer to the driver strategy routine. Passing NULL as the buf(9S) structure pointer tells physio(9F) to allocate a buf(9S) structure. If it is necessary for the driver to provide physio(9F) with a buf(9S) structure, getrbuf(9F) should be used to allocate one. physio(9F) returns zero if the transfer completes successfully, or an error number on failure. The return value of physio(9F) is determined by the strategy(9E) routine.
minphys( )
-
xxminphys is a pointer to a function to be called by physio(9F) to ensure that the size of the requested transfer does not exceed a driver-imposed limit. If the user requests a larger transfer, physio(9F) calls xxstrategy() repeatedly, requesting no more than the imposed limit at a time. This is important for DMA transfers because there is only finite amount of DMA resources available. Drivers for slow devices, such as printers, should be careful because they tie up resources for a long time.
- Usually, a driver passes a pointer to the kernel function minphys(9F), but it can define its own xxminphys() routine instead. The job of minphys(9F) is to keep the b_bcount field of the buf(9S) structure below a driver limit. There may be additional system limits that the driver should not circumvent, so the driver minphys routine should call the system minphys(9F) routine before returning.
-
Code Example 8-6 minphys(9F) routine
-
-
#define XXMINVAL (124 << 10)
static void
xxminphys(struct buf *bp)
{
if (bp->b_bcount > XXMINVAL)
bp->b_bcount = XXMINVAL
minphys(bp);
}
strategy( )
- The strategy(9E) routine originated in block drivers and is so called because it can implement a strategy for efficient queuing of I/O requests to a block device. A driver for a character-oriented device can also use a strategy(9E) routine. In the character I/O model presented here, strategy(9E) does not maintain a queue of requests, but rather services one request at a time.
- In this example, the strategy(9E) routine for a character-oriented DMA device allocates DMA resources for the data transfer and starts the command by programming the device register (see Chapter 7, "DMA," for a detailed description). Note that strategy(9E) does not receive a device number (dev_t) as a parameter, this is instead retrieved from the b_edev field of the buf(9S) structure.
-
Code Example 8-7 strategy(9E) routine supporting physio(9F)
-
-
static int
xxstrategy(struct buf *bp)
{
int instance;
struct xxstate *xsp;
ddi_dma_cookie_t cookie;
instance = getminor(bp->b_edev);
xsp = ddi_get_soft_state(statep, instance);
...
set up DMA resources with ddi_dma_buf_setup(9F)
xsp->bp = bp; /* remember bp */
-
program DMA engine and start command
-
-
return (0);
}
- Though strategy(9E) is declared to return an int, it should always return zero. strategy(9E) indicates an error to physio(9F) by setting the B_ERROR bit in the b_flags member of the buf(9S) structure, and placing the appropriate error number in the b_error field. physio(9F) will then return zero to indicate success, or the value of the b_error field if an error occurs.
- After calling strategy(9E), physio(9F) waits until the driver finishes with the buf(9S) structure (the transfer fails or is complete) by calling biowait(9F). A call to biodone(9F) must later be made by the driver or physio(9F) will wait forever. For example, strategy(9E) must call biodone(9F) when an error occurs.
Transfer Completion
- On completion of the DMA transfer, the device generates an interrupt. Eventually, the interrupt routine will be called. In this example, xxintr() receives a pointer to the state structure for the device that might have issued the interrupt. The most important function in the interrupt routine is to notify physio(9F) that the transfer is complete, which is accomplished by the call to biodone(9F). If the driver does not notify physio(9F) that the transfer is complete, physio(9F) will not return, and the thread will hang.
-
Code Example 8-8 Interrupt routine using physio(9F)
-
-
static u_int
xxintr(caddr_t arg)
{
struct xxstate *xsp = (struct xxstate *)arg;
-
if (device did not interrupt) {
-
-
return (DDI_INTR_UNCLAIMED);
}
if (error) {
-
error handling
-
-
}
-
release any resources used in the transfer, such as DMA resources (ddi_dma_free(9F))
-
-
/* wake up waiting thread in physio(9F) */
biodone (xsp->bp);
return (DDI_INTR_CLAIMED);
}
Mapping Device Memory
- Some devices, such as frame buffers, have memory that is directly accessible to user threads by way of memory mapping. Drivers for these devices typically do not support the read(9E) and write(9E) interfaces. Instead, these drivers
- support memory mapping with the mmap(9E) entry point. A typical example is a frame buffer driver that implements the mmap(9E) entry point to allow the frame buffer to be mapped in a user thread.
segmap( )
-
-
int xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp,
off_t len, unsigned int prot, unsigned int maxprot,
unsigned int flags, cred_t *credp);
-
segmap(9E) is the entry point responsible for actually setting up a memory mapping requested by the system on behalf of an mmap(2) system call. Drivers for all memory mapped devices usually use ddi_segmap(9F) as the entry point rather than define their own segmap(9E) routine.
mmap( )
-
-
int xxmmap(dev_t dev, off_t off, int prot);
- This routine is called as a result of an mmap(2) system call, and also as the result of a page fault. mmap(9E) is called to translate the device offset off to the corresponding page frame number. Code Example 8-9 allows a user thread to memory map the device registers.
-
Code Example 8-9 mmap(9E) routine
-
-
static int
xxmmap(dev_t dev, off_t off, int prot)
{
int instance;
struct xxstate *xsp;
if (prot & PROT_WRITE)
return (-1);
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL)
return (-1);
-
if (off is invalid)
-
-
return (-1);
return (hat_getkpfnum(xsp->regp->csr + off));
}
-
dev is the device number and off is the offset into the device's memory. prot specifies the kind of access requested, such as PROT_READ and PROT_WRITE A value of PROT_WRITE for prot would be invalid on a read-only device. See mmap(9E) and mmap(2).
-
hat_getkpfnum(9F) returns the page frame number for the memory that should be mapped. xsp->regp->csr is the kernel virtual address of the device memory determined in attach(9E) by calling ddi_map_regs(9F) and stored in the state structure.
- In Code Example 8-9 the whole address range up to off must be mapped using ddi_map_regs(9F). This can use a lot of system resources for devices that have a large mappable memory area, and is a waste of resources if the driver only needs the mapping so it can call hat_getkpfnum(9F). A better way to get the page frame number for a given offset is to just map that individual page, retrieve the page frame number, then unmap the page before returning. Since the page frame number refers to a page on the device, it will not change when the page is unmapped.
-
Code Example 8-10 mmap(9E) routine using less resources.
-
-
static int
xxmmap(dev_t dev, off_t off, int prot)
{
int kpfn = -1;
caddr_t kva;
...
if (ddi_map_regs(xsp->dip, rnumber, &kva, off, ptob(1)) ==
DDI_SUCCESS) {
kpfn = hat_getkpfnum(kva);
ddi_unmap_regs(xsp->dip, rnumber, &kva, off, ptob(1));
}
return (kpfn);
}
- If the mappable memory of the device is physically contiguous, converting off to the number of pages and adding it to the base page frame number will give the same result as getting the page frame number of a mapped page. In this case, only the first page of the device's memory needs to be mapped:
-
-
return (hat_getkpfnum(xsp->regp->csr) + btop(off));
- For an example showing how to access a memory mapped device from a user program see "Using Existing Drivers" on page 243.
Multiplexing I/O on File Descriptors
- A thread sometimes wants to handle I/O on more than one file descriptor. One example is an application program that wants to read the temperature from a temperature sensing device and then report the temperature to an interactive display. If the program makes a read request and there is no data available, it should not block waiting for the temperature before interacting with the user again.
- The poll(2) system call provides users with a mechanism for multiplexing I/O over a set of file descriptors that reference open files. poll(2) identifies those files on which a program can send or receive data without blocking, or on which certain events have occurred.
- To allow a program to poll a character driver, the driver must implement the chpoll(9E) entry point.
State Structure
- This section adds the following field to the state structure. See "State Structure" on page 57 for more information.
-
-
struct pollhead pollhead; /* for chpoll(9E)/pollwakeup(9F) */
chpoll( )
-
-
int xxchpoll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **phpp);
- The system calls chpoll(9E) when a user process issues a poll(2) system call on a file descriptor associated with the device. The chpoll(9E) entry point routine is used by non-STREAMS character device drivers that wish to support polling.
- In chpoll(9E), the driver must follow the following rules:
-
-
-
} else {
*reventsp = 0;
if (!anyyet)
-
*phpp = & local pollhead structure;
-
-
}
return (0);
-
xxchpoll( ) should check to see if certain events have occurred; see chpoll(9E). It should then return the mask of satisfied events by setting the return events in *reventsp.
- If no events have occurred, the return field for the events is cleared. If the anyyet field is not set, the driver must return an instance of the pollhead structure. It is usually allocated in a state structure and should be treated as opaque by the driver. None of its fields should be referenced.
-
-
Code Example 8-11 chpoll(9E) routine
-
-
static int
xxchpoll(dev_t dev, short events, int anyyet,
short *reventsp,struct pollhead **phpp)
{
int instance;
u_char status;
short revent;
struct xxstate *xsp;
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL)
return (ENXIO);
revent = 0;
/*
* Valid events are:
* POLLIN | POLLOUT | POLLPRI | POLLHUP | POLLERR
* This example checks only for POLLIN and POLLERR.
*/
status = xsp->regp->csr;
-
-
if ((events & POLLIN) && data available to read) {
revent |= POLLIN;
}
if ((events & POLLERR) && (status & DEVICE_ERROR)) {
revent |= POLLERR;
}
/* if nothing has occurred */
if (revent == 0) {
if (!anyyet) {
*phpp = &xsp->pollhead;
}
}
*reventsp = revent;
return (0);
}
- In this example, the driver can handle the POLLIN and POLLERR events (see chpoll(9E) for a detailed discussion of the available events). The driver first reads the status register to determine the current state of the device. The parameter events specifies which conditions the driver should check. If the appropriate conditions have occurred, the driver sets that bit in *reventsp. If none of the conditions have occurred and anyyet is not set, the address of the pollhead structure is returned in *phpp.
-
Code Example 8-12 Interrupt routine supporting chpoll(9E)
-
-
static u_int
xxintr(caddr_t arg)
{
struct xxstate *xsp = (struct xxstate *) arg;
u_char status, temp;
-
normal interrupt processing
-
-
...
status = xsp->regp->csr;
if (status & DEVICE_ERROR) {
pollwakeup(&xsp->pollhead, POLLERR);
}
if (just completed a read) {
pollwakeup(&xsp->pollhead, POLLIN);
}
...
return (DDI_INTR_CLAIMED);
}
-
pollwakeup(9F) is usually called in the interrupt routine when a supported condition has occurred. The interrupt routine reads the status from the status register and checks for the conditions. It then calls pollwakeup(9F) for each event to possibly notify polling threads that they should check again. Note that pollwakeup(9F) should not be called with any locks held, since it could cause the chpoll(9E) routine to be entered, causing deadlock if that routine tries to grab the same lock.
Miscellaneous I/O Control
- The ioctl(9E) routine is called when a user thread issues an ioctl(2) system call on a file descriptor associated with the device. The I/O control mechanism is a catchall for getting and setting device-specific parameters. It is frequently used to set a device specific mode, either by setting internal driver software flags or by writing commands to the device. It can also be used to return information to the user about the current device state. In short, it can do whatever the application and driver need it to do.
ioctl(9E)
-
-
int xxioctl(dev_t dev, int cmd, int arg, int mode,
cred_t *credp, int *rvalp);
- The cmd parameter indicates which command ioctl(9E) should perform. By convention, I/O control commands indicate the driver they belong to in bits 8- 15 of the command (usually given by the ASCII code of a character representing the driver), and the driver-specific command in bits 0-7. They are usually created in the following way:
-
-
#define XXIOC ('x' << 8)
#define XX_GET_STATE (XXIOC | 1) /* get status register */
#define XX_SET_CMD (XXIOC | 2) /* send command */
- The interpretation of arg depends on the command. I/O control commands should be documented (in the driver documentation, or a manual page) and defined in a public header file, so that applications know the names, what they do, and what they accept or return as arg. Any data transfer of arg (into or out of the driver) must be performed by the driver.
-
ioctl(9E) is usually a switch statement with a case for each supported ioctl(9E) request.
-
Code Example 8-13 ioctl(9E) routine
-
-
static int
xxioctl(dev_t dev, int cmd, int arg, int mode,
cred_t *credp,int *rvalp)
{
int instance;
u_char csr;
struct xxstate *xsp;
instance = getminor(dev);
xsp = ddi_get_soft_state(statep, instance);
if (xsp == NULL) {
return (ENXIO);
}
switch (cmd) {
case XX_GET_STATUS:
csr = xsp->regp->csr;
if (ddi_copyout(&csr, (caddr_t) arg, sizeof(u_char),
mode) != 0) {
return (EFAULT);
}
break;
case XX_SET_CMD:
if (ddi_copyin((caddr_t) arg, &csr, sizeof(u_char),
mode) != 0) {
return (EFAULT);
}
xsp->regp->csr = csr;
break;
default:
/* generic "ioctl unknown" error */
return (ENOTTY);
}
return (0);
}
- The cmd variable identifies a specific device control operation. If arg contains a user virtual address, ioctl(9E) must call ddi_copyin(9F) or ddi_copyout(9F) to transfer data between the data structure in the application program pointed to by arg and the driver. In Code Example 8-13, for the case of an XX_GET_STATUS request the contents of xsp->regp->csr is copied to the address in arg. When a request succeeds, ioctl(9E) can store in *rvalp any integer value to be the return value of the ioctl(2) system call
- that made the request. Negative return values, such as -1, should be avoided, as they usually indicate the system call failed, and many application programs assume negative values indicate failure.
- An application that uses the I/O controls above could look like the following:
-
Code Example 8-14 Using ioctl(2)
-
-
#include <sys/types.h>
#include "xxio.h"
int main(void)
{
u_char status;
.......
/*
* read the device status
*/
if (ioctl(fd, XX_GET_STATUS, &status) == -1) {
/* error handling */
}
printf("device status %x\n", status);
exit(0);
}
|
|