mirror of
https://github.com/sakumisu/CherryUSB.git
synced 2025-05-09 00:21:44 +08:00
docs: add vendor rst
This commit is contained in:
parent
8d2afc1540
commit
bfc1139d80
44
docs/source/demo/usbd_vendor.rst
Normal file
44
docs/source/demo/usbd_vendor.rst
Normal file
@ -0,0 +1,44 @@
|
||||
vendor device 驱动编写
|
||||
===========================
|
||||
|
||||
本节主要介绍如何编写一个 vendor device 驱动。
|
||||
|
||||
- 首先复制一份 class/template/usbd_xxx.c 文件
|
||||
- 实现以下三个回调函数,通常来说,vendor 驱动只需要实现 vendor_handler
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
intf->class_interface_handler = xxx_class_interface_request_handler;
|
||||
intf->class_endpoint_handler = NULL;
|
||||
intf->vendor_handler = NULL;
|
||||
intf->notify_handler = xxx_notify_handler;
|
||||
|
||||
- 举例如下
|
||||
|
||||
case1 演示对于主机 IN 数据的处理,将数据拷贝到 *data 中,并指定*len 的长度。协议栈会自动发送给主机,不需要用户手动调用发送 API。
|
||||
|
||||
case2 演示对于主机 OUT 数据的处理,当执行到此函数时,说明数据都已经接收完成,可以直接读取 *data 中的数据,长度为 *len。
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
static int xxx_vendor_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
|
||||
{
|
||||
USB_LOG_WRN("XXX Class request: "
|
||||
"bRequest 0x%02x\r\n",
|
||||
setup->bRequest);
|
||||
|
||||
switch (setup->bRequest) {
|
||||
case 1:
|
||||
memcpy(*data, xxx, sizeof(xxx));
|
||||
*len = sizeof(xxx);
|
||||
case 2:
|
||||
hexdump(*data, *len);
|
||||
default:
|
||||
USB_LOG_WRN("Unhandled XXX Class bRequest 0x%02x\r\n", setup->bRequest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- 最后使用形如 usbd_add_interface(busid, usbd_xxx_init_intf(&intf)) 注册接口
|
127
docs/source/demo/usbh_vendor.rst
Normal file
127
docs/source/demo/usbh_vendor.rst
Normal file
@ -0,0 +1,127 @@
|
||||
vendor host 驱动编写
|
||||
===========================
|
||||
|
||||
本节主要介绍如何编写一个 vendor host 驱动。
|
||||
|
||||
- 首先复制一份 class/template/usbh_xxx.c 文件
|
||||
|
||||
- 定义 class 驱动并使用 CLASS_INFO_DEFINE 前缀,这样,枚举完成后,协议栈自动通过 usbd_class_find_driver 来查找对应的驱动。
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
static const struct usbh_class_driver xxx_class_driver = {
|
||||
.driver_name = "xxx",
|
||||
.connect = usbh_xxx_connect,
|
||||
.disconnect = usbh_xxx_disconnect
|
||||
};
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info xxx_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
|
||||
.bInterfaceClass = 0,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.id_table = NULL,
|
||||
.class_driver = &xxx_class_driver
|
||||
};
|
||||
|
||||
|
||||
- 实现 connect 和 disconnect 函数, 在 connect 函数中,需要分配一个 xxx_class 结构体,在 disconnect 函数中释放 urb 和 xxx_class。
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct usbh_xxx {
|
||||
struct usbh_hubport *hport;
|
||||
struct usb_endpoint_descriptor *xxxin;
|
||||
struct usb_endpoint_descriptor *xxxout;
|
||||
struct usbh_urb xxxin_urb;
|
||||
struct usbh_urb xxxout_urb;
|
||||
|
||||
uint8_t intf; /* interface number */
|
||||
uint8_t minor;
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static int usbh_xxx_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
{
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
int ret;
|
||||
|
||||
struct usbh_xxx *xxx_class = usbh_xxx_class_alloc();
|
||||
if (xxx_class == NULL) {
|
||||
USB_LOG_ERR("Fail to alloc xxx_class\r\n");
|
||||
return -USB_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int usbh_xxx_disconnect(struct usbh_hubport *hport, uint8_t intf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct usbh_xxx *xxx_class = (struct usbh_xxx *)hport->config.intf[intf].priv;
|
||||
|
||||
if (xxx_class) {
|
||||
if (xxx_class->xxxin) {
|
||||
usbh_kill_urb(&xxx_class->xxxin_urb);
|
||||
}
|
||||
|
||||
if (xxx_class->xxxout) {
|
||||
usbh_kill_urb(&xxx_class->xxxout_urb);
|
||||
}
|
||||
|
||||
if (hport->config.intf[intf].devname[0] != '\0') {
|
||||
USB_LOG_INFO("Unregister xxx Class:%s\r\n", hport->config.intf[intf].devname);
|
||||
usbh_xxx_stop(xxx_class);
|
||||
}
|
||||
|
||||
usbh_xxx_class_free(xxx_class);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- 初始化端点
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
|
||||
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
|
||||
if (ep_desc->bEndpointAddress & 0x80) {
|
||||
USBH_EP_INIT(xxx_class->intin, ep_desc);
|
||||
} else {
|
||||
USBH_EP_INIT(xxx_class->intout, ep_desc);
|
||||
}
|
||||
}
|
||||
|
||||
- 最后设计收发 API,根据实际情况设计成同步 or 异步。
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
int usbh_xxx_in_transfer(struct usbh_xxx *xxx_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
|
||||
{
|
||||
int ret;
|
||||
struct usbh_urb *urb = &xxx_class->xxxin_urb;
|
||||
|
||||
usbh_xxx_urb_fill(urb, xxx_class->hport, xxx_class->xxxin, buffer, buflen, timeout, NULL, NULL);
|
||||
ret = usbh_submit_urb(urb);
|
||||
if (ret == 0) {
|
||||
ret = urb->actual_length;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_xxx_out_transfer(struct usbh_xxx *xxx_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
|
||||
{
|
||||
int ret;
|
||||
struct usbh_urb *urb = &xxx_class->xxxout_urb;
|
||||
|
||||
usbh_xxx_urb_fill(urb, xxx_class->hport, xxx_class->xxxout, buffer, buflen, timeout, NULL, NULL);
|
||||
ret = usbh_submit_urb(urb);
|
||||
if (ret == 0) {
|
||||
ret = urb->actual_length;
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -112,6 +112,8 @@ CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统的
|
||||
demo/usbh_net
|
||||
demo/usbh_bluetooth
|
||||
demo/usbh_wifi
|
||||
demo/usbd_vendor
|
||||
demo/usbh_vendor
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user