编写设备驱动程序

设备通信

USB 设备的工作方式是通过称为管道的通信通道来传递请求。只有管道处于打开状态时您才能提交请求。也可以刷新、查询和关闭管道。本节讨论管道、数据传输和回调以及数据请求。

USB 端点

与四种类型的 USB 端点通信的四种类型的管道包括:

有关对应于这些端点的传输类型的更多信息,请参见 USB 2.0 规范的第 5 章或参见请求

缺省管道

每个 USB 设备都有称为缺省端点的特殊控制端点。其通信通道称为缺省管道。大多数(可能并非所有)设备的设置都通过此管道进行。许多 USB 设备使用此管道作为其唯一的控制管道。

usb_get_dev_data(9F) 函数为客户机驱动程序提供缺省控制管道。此管道将会被预先打开以适应在打开其他管道之前需要的任何特殊设置。此缺省控制管道的特殊性表现在以下方面:

其他管道(包括其他控制管道)必须明确打开且仅限独占打开。

管道状态

管道处于以下状态之一:

调用 usb_pipe_get_state(9F) 函数可检索管道的状态。

打开管道

要打开管道,请将对应于要打开的管道的端点描述符传递给 usb_pipe_open(9F) 函数。使用 usb_get_dev_data(9F)usb_lookup_ep_data(9F) 函数可检索描述符树中的端点描述符。usb_pipe_open(9F) 函数将句柄返回给管道。

打开管道时必须指定管道策略。管道策略包含并发异步操作的估计数量,这些并发异步操作要求使用此管道将需要的独立线程。线程的估计数量是回调期间可能发生的并行操作的数量。此估计值必须至少为 2。有关管道策略的更多信息,请参见 usb_pipe_open(9F) 手册页。

关闭管道

驱动程序必须使用 usb_pipe_close(9F) 函数关闭缺省管道之外的管道。usb_pipe_close(9F) 函数使管道中的所有剩余请求得以完成。然后该函数还留出一秒钟的时间让这些请求的所有回调得以完成。

数据传输

对于所有管道类型,编程模型如下:

  1. 分配请求。

  2. 使用管道传输函数之一提交该请求。请参见 usb_pipe_bulk_xfer(9F)usb_pipe_ctrl_xfer(9F)usb_pipe_intr_xfer(9F)usb_pipe_isoc_xfer(9F) 手册页。

  3. 等待完成通知。

  4. 释放请求。

有关请求的更多信息,请参见请求。以下各节介绍各种请求类型的特性。

同步传输、异步传输和回调

传输分为同步传输和异步传输。同步传输在完成之前将会一直阻塞。异步传输完成时,将向客户机驱动程序发送回调。在 flags 参数中设置了 USB_FLAGS_SLEEP 标志时调用的传输函数大多数是同步的。

连续传输(如轮询)和等时传输不能是同步的。为了进行连续传输而对传输函数进行的调用(设置了 USB_FLAGS_SLEEP 标志)将会阻塞,目的只是等待资源,然后开始传输。

同步传输是要设置的最简单的传输,因为同步传输不要求任何回调函数。同步传输函数将返回传输开始状态,即使同步传输函数在完成传输前一直阻塞也是如此。完成时,可以在请求的完成原因字段和回调标志字段中查找有关传输状态的其他信息。下面将讨论完成原因字段和回调标志字段。

如果未在 flags 参数中指定 USB_FLAGS_SLEEP 标志,则该传输操作是异步的。此规则的例外是等时传输。异步传输操作将设置并启动传输,然后在传输完成前返回。异步传输操作将返回传输开始状态。客户机驱动程序通过回调处理程序接收传输完成状态。

回调处理程序是在异步传输完成时调用的函数。不要设置不进行回调的异步传输。两种类型的回调处理程序是正常完成处理程序和异常处理程序。您可以指定一个在这两种情况下要调用的处理程序。

完成处理程序和异常处理程序将传输请求作为参数接收。异常处理程序在请求中使用完成原因和回调状态来了解所发生的情况。完成原因 (usb_cr_t) 指示原始事务是如何完成的。例如,完成原因 USB_CR_TIMEOUT 指示传输超时。又如,如果 USB 设备在使用时被移除,则客户机驱动程序可能接收 USB_CR_DEV_NOT_RESP 作为其未完成请求的完成原因。回调状态 (usb_cb_flags_t) 指示 USBA 框架为修正这种情况所执行的操作。例如,回调状态 USB_CB_STALL_CLEARED 指示 USBA 框架清除了运行延迟条件。有关完成原因的更多信息,请参见 usb_completion_reason(9S) 手册页。有关回调状态标志的更多信息,请参见 usb_callback_flags(9S) 手册页。

运行请求的回调上下文和管道策略会对在回调中可以执行的操作实施一些限制。

请求

本节讨论请求结构,以及分配和取消分配不同类型的请求。

分配和取消分配请求

请求以初始化的请求结构的形式实现。每种不同的端点类型接受不同类型的请求。每种类型的请求有不同的请求结构类型。下表显示了每种类型请求的结构类型。此表还列出了可用于分配和释放每种类型结构的函数。

表 20–1 请求初始化

管道或端点类型 

请求结构 

请求结构分配函数 

请求结构释放函数 

控制 

usb_ctrl_req_t(请参见 usb_ctrl_request(9S) 手册页)

usb_alloc_ctrl_req(9F)

usb_free_ctrl_req(9F)

批量传输 

usb_bulk_req_t(请参见 usb_bulk_request(9S) 手册页)

usb_alloc_bulk_req(9F)

usb_free_bulk_req(9F)

中断 

usb_intr_req_t(请参见 usb_intr_request(9S) 手册页)

usb_alloc_intr_req(9F)

usb_free_intr_req(9F)

等时 

usb_isoc_req_t(请参见 usb_isoc_request(9S) 手册页)

usb_alloc_isoc_req(9F)

usb_free_isoc_req(9F)

下表列出了可用于每种类型请求的传输函数。

表 20–2 请求传输设置

管道或端点类型 

传输函数 

控制 

usb_pipe_ctrl_xfer(9F)usb_pipe_ctrl_xfer_wait(9F)

批量传输 

usb_pipe_bulk_xfer(9F)

中断 

usb_pipe_intr_xfer(9F)usb_pipe_stop_intr_polling(9F)

等时 

usb_pipe_isoc_xfer(9F)usb_pipe_stop_isoc_polling(9F)

分配和取消分配请求的过程如下:

  1. 使用相应的分配函数为所需的请求类型分配请求结构。表 20–1 中列出了请求结构分配函数的手册页。

  2. 初始化结构中所需的任何字段。有关更多信息,请参见请求特性和字段或相应的请求结构手册页。表 20–1 中列出了请求结构的手册页。

  3. 完成数据传输时,使用相应的释放函数释放请求结构。表 20–1 中列出了请求结构释放函数的手册页。

请求特性和字段

所有请求的数据都以消息块的形式传递,这样,无论驱动程序是 STREAMS 驱动程序、字符驱动程序还是块驱动程序,都将统一地对数据进行处理。消息块类型 mblk_tmblk(9S) 手册页中进行了介绍。DDI 提供了多个用于处理消息块的例程。示例包括 allocb(9F)freemsg(9F)。要了解用于处理消息块的其他例程,请参见 allocb(9F) 和 freemsg(9F) 手册页的“另请参见”部分。此外,还可以参见《STREAMS Programming Guide》

所有传输类型中都包括以下请求字段。在每个字段名称中,xxxx 的可能值包括: ctrlbulkintrisoc

xxxx_client_private

此字段值是一个指针,适用于将与请求一起在客户机驱动程序中传递的内部数据。此指针不用于将数据传输到设备。 

xxxx_attributes

此字段值是一组传输属性。虽然此字段对所有请求结构通用,但对于每种传输类型,此字段的初始化稍有不同。有关更多信息,请参见相应的请求结构手册页。表 20–1 中列出了这些手册页。另请参见 usb_request_attributes(9S) 手册页。

xxxx_cb

此字段值是正常传输完成的回调函数。如果异步传输在没有错误的情况下完成,则会调用此函数。 

xxxx_exc_cb

此字段值是错误处理的回调函数。仅当异步传输完成且出现错误时,才会调用此函数。 

xxxx_completion_reason

此字段存放传输本身的完成状态。如果出现错误,此字段将显示出现错误的内容。有关更多信息,请参见 usb_completion_reason(9S) 手册页。此字段由 USBA 2.0 框架更新。

xxxx_cb_flags

此字段列出在调用回调处理程序之前 USBA 2.0 框架所采取的恢复操作。USB_CB_INTR_CONTEXT 标志指示回调是否在中断上下文中运行。有关更多信息,请参见 usb_callback_flags(9S) 手册页。此字段由 USBA 2.0 框架更新。

以下各节介绍针对四种不同传输类型的不同请求字段。其中介绍如何初始化这些结构字段,还介绍有关属性和参数的各种组合的限制。

控制请求

使用控制请求可沿控制管道向下启动消息传输。您可以手动设置传输,如下所示。也可以使用 usb_pipe_ctrl_xfer_wait(9F) 包装函数设置并发送同步传输。

客户机驱动程序必须初始化 ctrl_bmRequestType ctrl_bRequestctrl_wValue ctrl_wIndexctrl_wLength 字段,如 USB 2.0 规范中所述。

必须将请求的 ctrl_data 字段初始化为指向数据缓冲区。将一个正值作为缓冲区 len 传递时,usb_alloc_ctrl_req(9F) 函数将初始化此字段。当然,还必须初始化缓冲区以便进行任何外发传输。在所有情况下,完成传输时,客户机驱动程序必须释放请求。

可以对多个控制请求进行排队。排队的请求中可以包括同步请求和异步请求。

ctrl_timeout 字段定义等待要被处理的请求的最长时间,但不包括在队列中的等待时间。此字段适用于同步和异步请求。ctrl_timeout 字段中指定的值以秒为单位。

如果出现异常,ctrl_exc_cb 字段接受要调用的函数的地址。usb_ctrl_request(9S) 手册页中指定了此异常处理程序的参数。异常处理程序的第二个参数是 usb_ctrl_req_t 结构。通过将请求结构作为参数传递,异常处理程序可以检查该请求的 ctrl_completion_reasonctrl_cb_flags 字段,以确定最佳恢复操作。

USB_ATTRS_ONE_XFERUSB_ATTRS_ISOC_* 标志对所有控制请求而言都是无效属性。USB_ATTRS_SHORT_XFER_OK 标志仅对主机绑定的请求有效。

批量传输请求

使用批量传输请求可发送非时间关键数据。批量传输请求可以接纳多个要完成的 USB 帧,具体取决于总体总线负载。

所有请求必须接收已初始化的消息块。有关 mblk_t 消息块类型的说明,请参见 mblk(9S) 手册页。此消息块将提供数据或存储数据,具体取决于传输方向。有关更多详细信息,请参阅 usb_bulk_request(9S) 手册页。

USB_ATTRS_ONE_XFERUSB_ATTRS_ISOC_* 标志对所有批量传输请求而言都是无效属性。USB_ATTRS_SHORT_XFER_OK 标志仅对主机绑定的请求有效。

usb_pipe_get_max_bulk_transfer_size(9F) 函数指定每个请求的最大字节数。检索到的值可以是客户机驱动程序的 minphys(9F) 例程中使用的最大值。

可以对多个批量传输请求进行排队。

中断请求

中断请求通常用于定期传入数据。中断请求定期在设备中轮询数据。但是,USBA 2.0 框架支持一次性的传入中断数据请求以及外发中断数据请求。所有中断请求可以利用 USB 中断传输的及时与重试特性。

USB_ATTRS_ISOC_* 标志对所有中断请求而言都是无效属性。USB_ATTRS_SHORT_XFER_OKUSB_ATTRS_ONE_XFER 标志仅对主机绑定的请求有效。

只有一次性轮询可以作为同步中断传输执行。如果在请求中指定 USB_ATTRS_ONE_XFER 属性,将进行一次性轮询。

定期轮询是作为异步中断传输启动。原始中断请求将被传递到 usb_pipe_intr_xfer(9F)。当轮询操作找到要返回的新数据时,将从原始请求克隆一个新的 usb_intr_req_t 结构,并使用已初始化的数据块填充该结构。分配请求时,请为 usb_alloc_intr_req(9F) 函数的 len 参数指定零。len 参数为零是因为 USBA 2.0 框架将为每个回调分配新请求,并填充该请求。分配请求结构后,请填充 intr_len 字段,以指定希望框架为每个轮询分配的字节数。将不会返回超出 intr_len 字节的数据。

客户机驱动程序必须释放它接收的每个请求。如果逆向发送消息块,请在逆向发送消息块之前从请求中分离该消息块。要从请求中分离消息块,请将该请求的数据指针设置为 NULL。将请求的数据指针设置为 NULL,可在取消分配请求时阻塞释放消息块。

调用 usb_pipe_stop_intr_polling(9F) 函数可取消定期轮询。停止轮询或关闭管道时,将通过异常回调返回原始请求结构。此返回的请求结构的完成原因将被设置为 USB_CR_STOPPED_POLLING

请不要在轮询进行过程中启动轮询。也不要在调用 usb_pipe_stop_intr_polling(9F) 的过程中启动轮询。

等时请求

等时请求用于速率恒定、与时间相关的流数据。出现错误时不会进行重试。等时请求具有以下特定于请求的字段:

isoc_frame_no

当必须从特定的帧编号启动总体传输时,请指定此字段。此字段的值必须大于当前帧编号。使用 usb_get_current_frame_number(9F) 可查找当前帧编号。请注意,当前帧编号为活动目标。对于低速和全速总线,将每 1 毫秒更新一次当前帧。对于高速总线,将每 0.125 毫秒更新一次当前帧。应设置 USB_ATTR_ISOC_START_FRAME 属性以便可以识别 isoc_frame_no 字段。

要忽略此帧编号字段并尽快启动,请设置 USB_ATTR_ISOC_XFER_ASAP 标志。

isoc_pkts_count

此字段是请求中的包数。此值受由 usb_get_max_pkts_per_isoc_request(9F) 函数返回的值和 isoc_pkt_descr 数组(参见下面的内容)的大小限制。该请求中可传输的字节数等于此 isoc_pkts_count 值与端点的 wMaxPacketSize 值的乘积。

isoc_pkts_length

此字段是请求的所有包的长度之和。此值由启动器设置。应将此值设置为零,以便自动使用 isoc_pkt_descr 列表中 isoc_pkts_length 的和,而不对此元素应用任何检查。

isoc_error_count

此字段是完成时出现错误的包数。此值由 USBA 2.0 框架设置。 

isoc_pkt_descr

此字段指向定义每个包传输数据量的包描述符的数组。对于传出请求,此值定义要处理的子请求的专用队列。对于传入请求,此值描述数据块的到达方式。客户机驱动程序将为传出请求分配这些描述符。框架将为传入请求分配和初始化这些描述符。此数组中的描述符包含框架初始化的字段,这些字段存储实际传输的字节数和传输的状态。有关更多详细信息,请参见 usb_isoc_request(9S) 手册页。

所有请求都必须接收已初始化的消息块。此消息块提供数据或存储数据。有关 mblk_t 消息块类型的说明,请参见 mblk(9S) 手册页。

USB_ATTR_ONE_XFER 标志是非法属性,因为系统将决定如何通过可用包数改变数据量。USB_ATTR_SHORT_XFER_OK 标志仅对主机绑定的数据有效。

无论是否设置 USB_FLAGS_SLEEP 标志,usb_pipe_isoc_xfer(9F) 函数都会使所有等时传输成为异步传输。所有等时输入请求都将启动轮询。

调用 usb_pipe_stop_isoc_polling(9F) 函数可取消定期轮询。停止轮询或关闭管道时,将通过异常回调返回原始请求结构。此返回的请求结构的完成原因将被设置为 USB_CR_STOPPED_POLLING

轮询会一直继续,直到发生以下某个事件:

刷新管道

您可能需要在出现错误后清理管道,或者可能想要等待管道清除。可使用下列方法之一刷新或清除管道: