第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
一个线程有时需要处理多个文件描述符上的 I/O。需要从温度感应设备读取温度并将此温度报告给交互显示的应用程序就是一个示例。在与用户再次交互之前等待温度时,发出读取请求但没有可用数据的程序不会进入阻塞状态。
poll(2) 系统调用为用户提供了对一组引用打开的文件的文件描述符执行多路复用 I/O 操作的机制。poll(2) 识别那些在它们上面程序可以发送或接收数据而不会阻塞的文件描述符,或在它们上面特定事件已发生的文件描述符。
要允许程序轮询字符驱动程序,该驱动程序必须实现 chpoll(9E) 入口点。当用户进程对与设备相关的文件描述符发出 poll(2) 系统调用时,系统就会调用 chpoll(9E)。需要支持轮询的非 STREAMS 字符设备驱动程序使用 chpoll(9E) 入口点例程。
chpoll(9E) 函数使用以下语法:
int xxchpoll(dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp);
在 chpoll(9E) 入口点中,驱动程序必须遵循下列规则:
在调用 chpoll(9E) 入口点时执行以下算法:
if ( /* events are satisfied now */ ) { *reventsp = mask_of_satisfied_events } else { *reventsp = 0; if (!anyyet) *phpp = &local_pollhead_structure; } return (0);
有关要检查的事件的论述,请参见 chpoll(9E) 手册页。然后,chpoll(9E) 入口点应通过在 *reventsp 中设置返回事件,来返回满足要求的事件的掩码。
如果没有发生任何事件,则清除事件的返回字段。如果未设置 anyyet 字段,则驱动程序必须返回 pollhead 结构的实例。通常在状态结构中分配 pollhead 结构。驱动程序应该将 pollhead 结构视为不透明。不能引用任何 pollhead 字段。
只要出现示例 15-10 中列出的 events 类型的设备条件,就会调用 pollwakeup(9F)。一次只能对一个事件调用此函数。当出现这种条件时,可以在中断例程中调用 pollwakeup(9F)。
示例 15-10 和示例 15-11 说明了如何实现轮询规程以及如何使用 pollwakeup(9F)。
以下示例说明如何处理 POLLIN 和 POLLERR 事件。首先,驱动程序读取状态寄存器以确定设备的当前状态。参数 events 指定驱动程序应该检查哪些条件。如果出现适当的条件,驱动程序会在 *reventsp 中设置相应的位。如果未出现任何条件并且没有设置 anyyet,则会在 *phpp 中返回 pollhead 结构的地址。
示例 15-10 chpoll(9E) 例程
static int xxchpoll(dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp) { uint8_t status; short revent; struct xxstate *xsp; xsp = ddi_get_soft_state(statep, getminor(dev)); if (xsp == NULL) return (ENXIO); revent = 0; /* * Valid events are: * POLLIN | POLLOUT | POLLPRI | POLLHUP | POLLERR * This example checks only for POLLIN and POLLERR. */ status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); if ((events & POLLIN) && data available to read) { revent |= POLLIN; } if (status & DEVICE_ERROR) { revent |= POLLERR; } /* if nothing has occurred */ if (revent == 0) { if (!anyyet) { *phpp = &xsp->pollhead; } } *reventsp = revent; return (0); }
以下示例说明如何使用 pollwakeup(9F) 函数。当出现支持的条件时,通常会在中断例程中调用 pollwakeup(9F) 函数。中断例程从状态寄存器中读取状态并检查条件。然后,该例程为每个事件调用 pollwakeup(9F),以便有可能通知轮询线程再次进行检查。请注意,不能在持有任何锁定的情况下调用 pollwakeup(9F),这是因为如果另一个例程尝试进入 chpoll(9E) 并获取相同的锁,则会导致死锁。
示例 15-11 支持 chpoll(9E) 的中断例程
static u_int xxintr(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; uint8_t status; /* normal interrupt processing */ /* ... */ status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); if (status & DEVICE_ERROR) { pollwakeup(&xsp->pollhead, POLLERR); } if ( /* just completed a read */ ) { pollwakeup(&xsp->pollhead, POLLIN); } /* ... */ return (DDI_INTR_CLAIMED); }