Skip to end of metadata
Go to start of metadata

General

The Vendor class offers the following functions to communicate with the host. For more details about the parameters of the function, see USB Device Vendor API.

Function nameOperation
USBD_Vendor_Rd()Receives data from host through bulk OUT endpoint. This function is blocking.
USBD_Vendor_Wr()Sends data to host through bulk IN endpoint. This function is blocking.
USBD_Vendor_RdAsync()Receives data from host through bulk OUT endpoint. This function is non-blocking.
USBD_Vendor_WrAsync()Sends data to host through bulk IN endpoint. This function is non-blocking.
USBD_Vendor_IntrRd()Receives data from host through interrupt OUT endpoint. This function is blocking.
USBD_Vendor_IntrWr()Sends data to host through interrupt IN endpoint. This function is blocking.
USBD_Vendor_IntrRdAsync()Receives data from host through interrupt OUT endpoint. This function is non-blocking.
USBD_Vendor_IntrWrAsync()Sends data to host through interrupt IN endpoint. This function is non-blocking.
Table - Vendor Communication API Summary

The vendor requests are also another way to communicate with the host. When managing vendor requests sent by the host, the application can receive or send data from or to the host using the control endpoint; you will need to provide an application callback passed as a parameter of USBD_Vendor_Add().

Synchronous Communication

Synchronous communication means that the transfer is blocking. When a function is called, the application blocks until the transfer completes with or without an error. A timeout can be specified to avoid waiting forever.

Listing - Synchronous Bulk Read and Write Example shows a read and write example that receives data from the host using the bulk OUT endpoint and sends data to the host using the bulk IN endpoint.

CPU_INT08U  rx_buf[2];
CPU_INT08U  tx_buf[2];
RTOS_ERR    err;


(void)USBD_Vendor_Rd(         class_nbr,                                      (1)
                     (void *)&rx_buf[0],                                      (2)
                              2u,
                              0u,                                             (3)
                             &err);
if (err.Code != USBD_ERR_NONE) {
    /* $$$$ Handle the error. */
}

(void)USBD_Vendor_Wr(         class_nbr,                                      (1)
                     (void *)&tx_buf[0],                                      (4)
                              2u,
                              0u,                                             (3)
                              DEF_FALSE,                                      (5)
                             &err);
if (err.Code != USBD_ERR_NONE) {
    /* $$$$ Handle the error. */
}
Listing - Synchronous Bulk Read and Write Example

(1) The class instance number created with USBD_Vendor_Add() provides an internal reference to the Vendor class to route the transfer to the proper bulk OUT or IN endpoint.

(2) The application must ensure that the buffer provided to the function is large enough to accommodate all the data. Otherwise, synchronization issues might happen.

(3) In order to avoid an infinite blocking situation, a timeout expressed in milliseconds can be specified. A value of ‘0’ makes the application task wait forever.

(4) The application provides the initialized transmit buffer.

(5) If this flag is set to DEF_TRUE, and the transfer length is multiple of the endpoint maximum packet size, the device stack will send a zero-length packet to the host to signal the end of the transfer.

The use of interrupt endpoint communication functions, USBD_Vendor_IntrRd() and USBD_Vendor_IntrWr(), is similar to bulk endpoint communication functions presented in Listing - Synchronous Bulk Read and Write Example.

Asynchronous Communication

Asynchronous communication means that the transfer is non-blocking. When a function is called, the application passes the transfer information to the device stack and does not block. Other application processing can be done while the transfer is in progress over the USB bus. Once the transfer has completed, a callback function is called by the device stack to inform the application about the transfer completion.  Listing - Asynchronous Bulk Read and Write Example shows an example of asynchronous read and write.

void App_USBD_Vendor_Comm (CPU_INT08U  class_nbr)
{
    CPU_INT08U  rx_buf[2];
    CPU_INT08U  tx_buf[2];
    RTOS_ERR    err;


    USBD_Vendor_RdAsync(         class_nbr,                                   (1)
                        (void *)&rx_buf[0],                                   (2)
                                 2u,
                                 App_USBD_Vendor_RxCmpl,                      (3)
                                 DEF_NULL,                                    (4)
                                &err);
    if (err.Code != USBD_ERR_NONE) {
        /* $$$$ Handle the error. */
    }
    USBD_Vendor_WrAsync(         class_nbr,                                   (1)
                        (void *)&tx_buf[0],                                   (5)
                                 2u,
                                 App_USBD_Vendor_TxCmpl,                      (3)
                                 DEF_NULL,                                    (4)
                                 DEF_FALSE,                                   (6)
                                &err);
 
    if (err.Code != USBD_ERR_NONE) {
        /* $$$$ Handle the error. */
    }
}


static  void  App_USBD_Vendor_RxCmpl (CPU_INT08U   class_nbr,                 (3)
                                      void        *p_buf,
                                      CPU_INT32U   buf_len,
                                      CPU_INT32U   xfer_len,
                                      void        *p_callback_arg,
                                      USBD_ERR     err)
{
    if (err.Code == USBD_ERR_NONE) {
        /* $$$$ Do some processing. */
    } else {
        /* $$$$ Handle the error. */
    }
}
 

static  void  App_USBD_Vendor_TxCmpl (CPU_INT08U   class_nbr,                 (3)
                                      void        *p_buf,
                                      CPU_INT32U   buf_len,
                                      CPU_INT32U   xfer_len,
                                      void        *p_callback_arg,
                                      USBD_ERR     err)
{
    if (err.Code == USBD_ERR_NONE) {
        /* $$$$ Do some processing. */
    } else {
        /* $$$$ Handle the error. */
    }
}
Listing - Asynchronous Bulk Read and Write Example

(1) The class instance number provides an internal reference to the Vendor class to route the transfer to the proper bulk OUT or IN endpoint.

(2) The application must ensure that the buffer provided is large enough to accommodate all the data. Otherwise, there may be synchronization issues.

(3) The application provides a callback function pointer passed as a parameter. Upon completion of the transfer, the device stack calls this callback function so that the application can finalize the transfer by analyzing the transfer result. For instance, on completion of a read operation, the application might perform processing on the received data. Upon write completion, the application can indicate if the write was successful and how many bytes were sent.

(4) An argument associated with the callback can be also passed. Then in the callback context, some private information can be retrieved.

(5) The application provides the initialized transmit buffer.

(6) If this flag is set to DEF_TRUE, and the transfer length is a multiple of the endpoint maximum packet size, the device stack will send a zero-length packet to the host to signal the end of transfer.

The use of interrupt endpoint communication functions, USBD_Vendor_IntrRdAsync() and USBD_Vendor_IntrWrAsync(), is similar to the bulk endpoint communication functions presented in Listing - Asynchronous Bulk Read and Write Example.

Vendor Request

The USB 2.0 specification defines three types of requests: standard, class, and vendor. All standard requests are handled directly by the core layer, while class requests are managed by the proper associated class.

Vendor requests can be processed by the vendor class. To process vendor requests, you must provide an application callback as a parameter of USBD_Vendor_Add(). Once a vendor request is received by the USB device, it must be decoded properly.  Listing - Example of Vendor Request Decoding shows an example of vendor request decoding.

Certain requests may be required to receive from or send to the host during the data stage of a control transfer. If no data stage is present, you only have to decode the Setup packet. This example shows the three types of data stage management: no data, data OUT and data IN.

#define  APP_VENDOR_REQ_NO_DATA                     0x01u
#define  APP_VENDOR_REQ_RECEIVE_DATA_FROM_HOST      0x02u
#define  APP_VENDOR_REQ_SEND_DATA_TO_HOST           0x03u


#define  APP_VENDOR_REQ_DATA_BUF_SIZE                 50u

static  CPU_INT08U   AppVendorReqBuf[APP_VENDOR_REQ_DATA_BUF_SIZE];


static  CPU_BOOLEAN  App_USBD_Vendor_VendorReq (       CPU_INT08U       class_nbr,
                                                       CPU_INT08U       dev_nbr,
                                                const  USBD_SETUP_REQ  *p_setup_req)    (1)
{
    CPU_BOOLEAN  valid;
    RTOS_ERR     err_usb;
    CPU_INT16U   req_len;
 
 
    (void)&class_nbr;

    switch(p_setup_req->bRequest) {                                                     (2)
        case APP_VENDOR_REQ_NO_DATA:                                                    (3)
             APP_TRACE_DBG(("Vendor request [0x%X]:\r\n", p_setup_req->bRequest));
             APP_TRACE_DBG(("wIndex  = %d\r\n",           p_setup_req->wIndex));
             APP_TRACE_DBG(("wLength = %d\r\n",           p_setup_req->wLength));
             APP_TRACE_DBG(("wValue  = %d\r\n",           p_setup_req->wValue));
             valid = DEF_OK;
             break;
             
        case APP_VENDOR_REQ_RECEIVE_DATA_FROM_HOST:                                     (4)
             req_len = p_setup_req->wLength;
             if (req_len > APP_VENDOR_REQ_DATA_BUF_SIZE) {
                 return (DEF_FAIL);                             /* Not enough room to receive data.           */
             }
             APP_TRACE_DBG(("Vendor request [0x%X]:\r\n", p_setup_req->bRequest));
                                                                /* Receive data via Control OUT EP.           */
             (void)USBD_CtrlRx(         dev_nbr,
                               (void *)&AppVendorReqBuf[0u],
                                        req_len,
                                        0u,                     /* Wait transfer completion forever.          */
                                       &err_usb);
             if (err_usb.Code != USBD_ERR_NONE) {
                 APP_TRACE_DBG(("Error receiving data from host: %d\r\n", err_usb));
                 valid = DEF_FAIL;
             } else {
                 APP_TRACE_DBG(("wIndex  = %d\r\n", p_setup_req->wIndex));
                 APP_TRACE_DBG(("wLength = %d\r\n", p_setup_req->wLength));
                 APP_TRACE_DBG(("wValue  = %d\r\n", p_setup_req->wValue));
                 APP_TRACE_DBG(("Received %d octets from host via Control EP OUT\r\n", req_len));
                 valid = DEF_OK;
             }
             break;
             
        case APP_VENDOR_REQ_SEND_DATA_TO_HOST:                                      (5)
             APP_TRACE_DBG(("Vendor request [0x%X]:\r\n", p_setup_req->bRequest));
             req_len = APP_VENDOR_REQ_DATA_BUF_SIZE;
             Mem_Set((void *)&AppVendorReqBuf[0u],              /* Fill buf with a pattern.                   */
                             'A',
                             req_len);
                                                                /* Send data via Control IN EP.               */
             (void)USBD_CtrlTx(         dev_nbr,
                               (void *)&AppVendorReqBuf[0u],
                                        req_len,
                                        0u,                     /* Wait transfer completion forever.          */
                                        DEF_NO,
                                       &err_usb);
             if (err_usb.Code != USBD_ERR_NONE) {
                 APP_TRACE_DBG(("Error sending data to host: %d\r\n", err_usb));
                 valid = DEF_FAIL;
             } else {
                 APP_TRACE_DBG(("wIndex  = %d\r\n", p_setup_req->wIndex));
                 APP_TRACE_DBG(("wLength = %d\r\n", p_setup_req->wLength));
                 APP_TRACE_DBG(("wValue  = %d\r\n", p_setup_req->wValue));
                 APP_TRACE_DBG(("Sent %d octets to host via Control EP IN\r\n", req_len));
                 valid = DEF_OK;
             }
             break;
             
        default:                                                                    (6)
             valid = DEF_FAIL;                                  /* Request is not supported.                  */
             break;
    }
    return (valid);
}
Listing - Example of Vendor Request Decoding

(1) The core will pass the Setup packet content to your application. The structure USBD_SETUP_REQ contains the same fields as defined by the USB 2.0 specification (refer to section "9.3 USB Device Requests" of the specification for more details):

typedef  struct  usbd_setup_req {
    CPU_INT08U  bmRequestType;  /* Characteristics of request.                          */
    CPU_INT08U  bRequest;       /* Specific request.                                    */
    CPU_INT16U  wValue;         /* Varies according to request.                         */
    CPU_INT16U  wIndex;         /* Varies according to request; typically used as index.*/
    CPU_INT16U  wLength;        /* Transfer length if data stage present.               */
} USBD_SETUP_REQ;

(2) Determine the request. You may use a switch statement if you are using different requests. In this example, there are three different requests corresponding to the three types of the data stage: APP_VENDOR_REQ_NO_DATA, APP_VENDOR_REQ_RECEIVE_DATA_FROM_HOST, and APP_VENDOR_REQ_SEND_DATA_TO_HOST.

(3) If no data stage is present, you only need to decode the other fields. The presence of a data stage or not is indicated by the field wLength being non-null or null.

(4) If the host sends data to the device, you must call the function USBD_CtrlRx(). The buffer provided should be able to contain up to wLength bytes. If any error occurs, return DEF_FAIL to the core that will stall the status stage of the control transfer, indicating to the host that the request cannot be processed. DEF_OK is returned in case of success.

(5) If the host receives data from the device, you must call the function USBD_CtrlTx(). If any error occurs, return DEF_FAIL to the core that will stall the status stage of the control transfer, indicating to the host that the request cannot be processed. DEF_OK is returned in case of success.

(6) In this example, all requests not recognized are marked by returning DEF_FAIL to the core. This one will stall the data or status stage of the control transfer indicating to the host that the request is not supported.

The host sends vendor requests using the function USBDev_CtrlReq(). Refer to the page USBDev_API for more details about how to send vendor requests on the host side. 

  • No labels