devmap(9E) 的语法如下所示:
int xxdevmap_map(devmap_cookie_t handle, dev_t dev, uint_t flags, offset_t offset, size_t len, void **new-devprivate);
驱动程序从其 devmap() 入口点返回并且系统已建立到设备内存的用户映射后,将会调用 devmap_map() 入口点。通过 devmap() 入口点,驱动程序可以执行其他处理操作或分配特定于映射的专用数据。例如,为了支持上下文切换,驱动程序必须分配上下文结构。然后,驱动程序必须将上下文结构与映射关联。
系统期望驱动程序在 *new-devprivate 中返回一个指向分配的专用数据的指针。驱动程序必须存储用于定义专用数据中的映射范围的 offset 和 len。然后当系统调用 devmap_unmap(9E) 时,驱动程序将使用此信息来确定要取消映射的映射量。
flags 指示驱动程序是否应为映射分配专用上下文。例如,如果 flags 设置为 MAP_PRIVATE,则驱动程序可以分配用于存储设备上下文的内存区域。如果设置了 MAP_SHARED,驱动程序将返回指向共享区域的指针。
以下示例说明了 devmap() 入口点。驱动程序分配了一个新的上下文结构。然后,驱动程序便可保存通过入口点传入的相关参数。接下来,将通过分配或通过将映射附加至已经存在的共享上下文来为映射指定新的上下文。映射访问设备的最短时间间隔设置为 1 毫秒。
static int int xxdevmap_map(devmap_cookie_t handle, dev_t dev, uint_t flags, offset_t offset, size_t len, void **new_devprivate) { struct xxstate *xsp = ddi_get_soft_state(statep, getminor(dev)); struct xxctx *newctx; /* create a new context structure */ newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP); newctx->xsp = xsp; newctx->handle = handle; newctx->offset = offset; newctx->flags = flags; newctx->len = len; mutex_enter(&xsp->ctx_lock); if (flags & MAP_PRIVATE) { /* allocate a private context and initialize it */ newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP); xxctxinit(newctx); } else { /* set a pointer to the shared context */ newctx->context = xsp->ctx_shared; } mutex_exit(&xsp->ctx_lock); /* give at least 1 ms access before context switching */ devmap_set_ctx_timeout(handle, drv_usectohz(1000)); /* return the context structure */ *new_devprivate = newctx; return(0); }
对转换无效的映射进行访问时,将会调用 devmap_access(9E) 入口点。映射转换在以下几种情况下无效:作为对 mmap(2) 的响应通过 devmap_devmem_setup(9F) 创建映射;通过 fork(2) 复制映射或通过调用 devmap_unload(9F) 显式使映射无效。
devmap_access() 的语法如下所示:
int xxdevmap_access(devmap_cookie_t handle, void *devprivate, offset_t offset, size_t len, uint_t type, uint_t rw);
其中:
用户进程所访问的映射的映射句柄。
指向与映射关联的驱动程序专用数据的指针。
所访问映射内的偏移。
所访问内存的长度(以字节为单位)。
访问操作的类型。
用于指定访问的方向。
系统期望 devmap_access(9E) 调用 devmap_do_ctxmgt(9F) 或 devmap_default_access(9F) 以便在 devmap_access() 返回前装入内存地址转换。对于支持上下文切换的映射,设备驱动程序应调用 devmap_do_ctxmgt()。系统会通过 devmap_access(9E) 向此例程传递所有参数以及指向驱动程序入口点 devmap_contextmgt(9E) 的指针,该指针用来处理上下文切换。对于不支持上下文切换的映射,驱动程序应调用 devmap_default_access(9F)。devmap_default_access() 的用途是调用 devmap_load(9F) 以装入用户转换。
以下示例说明了 devmap_access(9E) 入口点。该映射分为两个区域。在偏移 OFF_CTXMG 上开始并且长度为 CTXMGT_SIZE 字节的区域支持上下文管理。其余映射支持缺省访问。
#define OFF_CTXMG 0 #define CTXMGT_SIZE 0x20000 static int xxdevmap_access(devmap_cookie_t handle, void *devprivate, offset_t off, size_t len, uint_t type, uint_t rw) { offset_t diff; int error; if ((diff = off - OFF_CTXMG) >= 0 && diff < CTXMGT_SIZE) { error = devmap_do_ctxmgt(handle, devprivate, off, len, type, rw, xxdevmap_contextmgt); } else { error = devmap_default_access(handle, devprivate, off, len, type, rw); } return (error); }
devmap_contextmgt(9E) 的语法如下所示:
int xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate, offset_t offset, size_t len, uint_t type, uint_t rw);
devmap_contextmgt() 应使用当前对设备具有访问权限的映射的句柄调用 devmap_unload(9F)。此方法可使对于该映射的转换无效。通过此方法,可确保下次访问当前映射时针对该映射调用 devmap_access(9E)。对于引起访问事件发生的映射,需要验证其映射转换。相应地,驱动程序必须为进程请求访问恢复设备上下文。并且,驱动程序必须针对映射的 handle 调用 devmap_load(9F),该映射生成了对此入口点的调用。
访问已通过调用 devmap_load() 对映射转换进行验证的部分映射时不会导致调用 devmap_access()。对 devmap_unload() 的后续调用将使映射转换无效。通过此调用,可再次调用 devmap_access()。
如果 devmap_load() 或 devmap_unload() 返回错误,devmap_contextmgt() 应立即返回该错误。如果设备驱动程序在恢复设备上下文时遇到硬件故障,则应返回 -1。否则,成功处理访问请求后,devmap_contextmgt() 应返回零。如果从 devmap_contextmgt() 返回非零值,则会向进程发送 SIGBUS 或 SIGSEGV。
以下示例说明如何管理单页设备上下文。
xxctxsave() 和 xxctxrestore() 是与设备相关的上下文保存和恢复函数。xxctxsave() 从寄存器中读取数据并将数据保存在软状态结构中。xxctxrestore() 提取软状态结构中保存的数据并将数据写入设备寄存器。请注意,执行读取、写入和保存都需要使用 DDI/DKI 数据访问例程。
static int xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate, offset_t off, size_t len, uint_t type, uint_t rw) { int error; struct xxctx *ctxp = devprivate; struct xxstate *xsp = ctxp->xsp; mutex_enter(&xsp->ctx_lock); /* unload mapping for current context */ if (xsp->current_ctx != NULL) { if ((error = devmap_unload(xsp->current_ctx->handle, off, len)) != 0) { xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (error); } } /* Switch device context - device dependent */ if (xxctxsave(xsp->current_ctx, off, len) < 0) { xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (-1); } if (xxctxrestore(ctxp, off, len) < 0){ xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (-1); } xsp->current_ctx = ctxp; /* establish mapping for new context and return */ error = devmap_load(handle, off, len, type, rw); if (error) xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (error); }
复制设备映射(例如,由调用 fork(2) 的用户进程进行复制)时,将会调用 devmap_dup(9E) 入口点。驱动程序预期会为新映射生成新的驱动程序专用数据。
devmap_dup() 的语法如下所示:
int xxdevmap_dup(devmap_cookie_t handle, void *devprivate, devmap_cookie_t new-handle, void **new-devprivate);
其中:
正在复制的映射的映射句柄。
已复制的映射的映射句柄。
指向与正在复制的映射关联的驱动程序专用数据的指针。
应设置为指向用于新映射的新驱动程序专用数据的指针。
缺省情况下使用 devmap_dup() 所创建的映射会使其映射转换无效。第一次访问映射时,无效的映射转换会强制调用 devmap_access(9E) 入口点。
以下示例说明了一个典型的 devmap_dup() 例程。
static int xxdevmap_dup(devmap_cookie_t handle, void *devprivate, devmap_cookie_t new_handle, void **new_devprivate) { struct xxctx *ctxp = devprivate; struct xxstate *xsp = ctxp->xsp; struct xxctx *newctx; /* Create a new context for the duplicated mapping */ newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP); newctx->xsp = xsp; newctx->handle = new_handle; newctx->offset = ctxp->offset; newctx->flags = ctxp->flags; newctx->len = ctxp->len; mutex_enter(&xsp->ctx_lock); if (ctxp->flags & MAP_PRIVATE) { newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP); bcopy(ctxp->context, newctx->context, XXCTX_SIZE); } else { newctx->context = xsp->ctx_shared; } mutex_exit(&xsp->ctx_lock); *new_devprivate = newctx; return(0); }
对映射取消映射时,将会调用 devmap_unmap(9E) 入口点。用户进程退出或调用 munmap(2) 系统调用会导致取消映射。
devmap_unmap() 的语法如下所示:
void xxdevmap_unmap(devmap_cookie_t handle, void *devprivate, offset_t off, size_t len, devmap_cookie_t new-handle1, void **new-devprivate1, devmap_cookie_t new-handle2, void **new-devprivate2);
其中:
正在释放的映射的映射句柄。
指向与映射关联的驱动程序专用数据的指针。
逻辑设备内存中取消映射开始处的偏移。
所取消映射的内存的长度(以字节为单位)。
系统用来描述新区域的句柄,该新区域在 off - 1 位置结束。new-handle1 的值可以为 NULL。
要由驱动程序通过用于新区域的专用驱动程序映射数据进行填充的指针,该新区域在 off -1 位置结束。如果 new-handle1 为 NULL,则会忽略 new-devprivate1。
系统用来描述新区域的句柄,该新区域在 off + len 位置开始。new-handle2 的值可以为 NULL。
要由驱动程序通过用于新区域的驱动程序专用映射数据进行填充的指针,该新区域在 off + len 位置开始。如果 new-handle2 为 NULL,则会忽略 new-devprivate2。
devmap_unmap() 例程预期会释放通过 devmap_map(9E) 或 devmap_dup(9E) 创建此映射时分配的任何驱动程序专用资源。如果只是取消映射部分映射,则驱动程序必须在释放旧的专用数据之前为其余映射分配新的专用数据。不必针对已释放的映射的句柄调用 devmap_unload(9F),即使此句柄指向具有有效转换的映射时也是如此。不过,为了避免将来出现 devmap_access(9E) 问题,设备驱动程序应确保当前的映射表示形式设置为“无当前映射”。
以下示例说明了一个典型的 devmap_unmap() 例程。
static void xxdevmap_unmap(devmap_cookie_t handle, void *devprivate, offset_t off, size_t len, devmap_cookie_t new_handle1, void **new_devprivate1, devmap_cookie_t new_handle2, void **new_devprivate2) { struct xxctx *ctxp = devprivate; struct xxstate *xsp = ctxp->xsp; mutex_enter(&xsp->ctx_lock); /* * If new_handle1 is not NULL, we are unmapping * at the end of the mapping. */ if (new_handle1 != NULL) { /* Create a new context structure for the mapping */ newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP); newctx->xsp = xsp; if (ctxp->flags & MAP_PRIVATE) { /* allocate memory for the private context and copy it */ newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP); bcopy(ctxp->context, newctx->context, XXCTX_SIZE); } else { /* point to the shared context */ newctx->context = xsp->ctx_shared; } newctx->handle = new_handle1; newctx->offset = ctxp->offset; newctx->len = off - ctxp->offset; *new_devprivate1 = newctx; } /* * If new_handle2 is not NULL, we are unmapping * at the beginning of the mapping. */ if (new_handle2 != NULL) { /* Create a new context for the mapping */ newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP); newctx->xsp = xsp; if (ctxp->flags & MAP_PRIVATE) { newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP); bcopy(ctxp->context, newctx->context, XXCTX_SIZE); } else { newctx->context = xsp->ctx_shared; } newctx->handle = new_handle2; newctx->offset = off + len; newctx->flags = ctxp->flags; newctx->len = ctxp->len - (off + len - ctxp->off); *new_devprivate2 = newctx; } if (xsp->current_ctx == ctxp) xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); if (ctxp->flags & MAP_PRIVATE) kmem_free(ctxp->context, XXCTX_SIZE); kmem_free(ctxp, sizeof (struct xxctx)); }