usb: Add end-point stall APIs and flow control APIS

USB class drivers may need to offload some work from
upcall interrupt context to a background fiber. This
requires some way to defer taking more data from host
till the offloaded work completes. Two APIs are added to
achieve this.

Further USB class drivers sometimes need to set STALL condition
on end-points to signal errors to host.These too are
added.

Change-Id: Ic973522c3394e23d7f9c4c67affc0cd050afc20f
Signed-off-by: Jithu Joseph <jithu.joseph@intel.com>
This commit is contained in:
Jithu Joseph 2016-10-14 13:14:25 -07:00 committed by Anas Nashif
commit 7eab305593
4 changed files with 169 additions and 4 deletions

View file

@ -944,8 +944,8 @@ int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
return 0;
}
int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
const uint32_t max_data_len, uint32_t * const read_bytes)
int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
uint32_t *read_bytes)
{
uint8_t ep_idx = USB_DW_EP_ADDR2IDX(ep);
uint32_t i, j, data_len, bytes_to_copy;
@ -1016,7 +1016,25 @@ int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
*read_bytes = bytes_to_copy;
}
/* Prepare ep for rx if all the data where read */
return 0;
}
int usb_dc_ep_read_continue(uint8_t ep)
{
uint8_t ep_idx = USB_DW_EP_ADDR2IDX(ep);
if (!usb_dw_ctrl.attached && !usb_dw_ep_is_valid(ep)) {
SYS_LOG_ERR("No valid endpoint");
return -EINVAL;
}
/* Check if OUT ep */
if (USB_DW_EP_ADDR2DIR(ep) != USB_EP_DIR_OUT) {
SYS_LOG_ERR("Wrong endpoint direction");
return -EINVAL;
}
if (!usb_dw_ctrl.out_ep_ctrl[ep_idx].data_len) {
usb_dw_prep_rx(ep_idx, 0);
}
@ -1024,6 +1042,27 @@ int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
return 0;
}
int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
const uint32_t max_data_len, uint32_t * const read_bytes)
{
if (usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes) != 0) {
return -EINVAL;
}
if (!data && !max_data_len) {
/* When both buffer and max data to read are zero the above
* call would fetch the data len and we simply return.
*/
return 0;
}
if (usb_dc_ep_read_continue(ep) != 0) {
return -EINVAL;
}
return 0;
}
int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
{
uint8_t ep_idx = USB_DW_EP_ADDR2IDX(ep);

View file

@ -266,7 +266,9 @@ int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
*
* This function is called by the Endpoint handler function, after an OUT
* interrupt has been received for that EP. The application must only call this
* function through the supplied usb_ep_callback function.
* function through the supplied usb_ep_callback function. This function clears
* the ENDPOINT NAK, if all data in the endpoint FIFO has been read,
* so as to accept more data from host.
*
* @param[in] ep Endpoint address corresponding to the one
* listed in the device configuration table
@ -296,4 +298,41 @@ int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
*/
int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb);
/**
* @brief read data from the specified endpoint
*
* This is similar to usb_dc_ep_read, the difference being that, it doesnt
* clear the endpoint NAKs so that the consumer is not bogged down by further
* upcalls till he is done with the processing of the data. The caller should
* reactivate ep by invoking usb_dc_ep_read_continue() do so.
*
* @param[in] ep Endpoint address corresponding to the one
* listed in the device configuration table
* @param[in] data pointer to data buffer to write to
* @param[in] max_data_len max length of data to read
* @param[out] read_bytes Number of bytes read. If data is NULL and
* max_data_len is 0 the number of bytes
* available for read should be returned.
*
* @return 0 on success, negative errno code on fail.
*/
int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
uint32_t *read_bytes);
/**
* @brief Continue reading data from the endpoint
*
* Clear the endpoint NAK and enable the endpoint to accept more data
* from the host. Usually called after usb_dc_ep_read_wait() when the consumer
* is fine to accept more data. Thus these calls together acts as flow control
* meachanism.
*
* @param[in] ep Endpoint address corresponding to the one
* listed in the device configuration table
*
* @return 0 on success, negative errno code on fail.
*/
int usb_dc_ep_read_continue(uint8_t ep);
#endif /* __USB_DC_H__ */

View file

@ -233,4 +233,68 @@ int usb_write(uint8_t ep, const uint8_t *data, uint32_t data_len,
int usb_read(uint8_t ep, uint8_t *data, uint32_t max_data_len,
uint32_t *ret_bytes);
/*
* @brief set STALL condition on the specified endpoint
*
* This function is called by USB device class handler code to set stall
* conditionin on endpoint.
*
* @param[in] ep Endpoint address corresponding to the one listed in
* the device configuration table
*
* @return 0 on success, negative errno code on fail
*/
int usb_ep_set_stall(uint8_t ep);
/*
* @brief clears STALL condition on the specified endpoint
*
* This function is called by USB device class handler code to clear stall
* conditionin on endpoint.
*
* @param[in] ep Endpoint address corresponding to the one listed in
* the device configuration table
*
* @return 0 on success, negative errno code on fail
*/
int usb_ep_clear_stall(uint8_t ep);
/**
* @brief read data from the specified endpoint
*
* This is similar to usb_ep_read, the difference being that, it doesnt
* clear the endpoint NAKs so that the consumer is not bogged down by further
* upcalls till he is done with the processing of the data. The caller should
* reactivate ep by invoking usb_ep_read_continue() do so.
*
* @param[in] ep Endpoint address corresponding to the one
* listed in the device configuration table
* @param[in] data pointer to data buffer to write to
* @param[in] max_data_len max length of data to read
* @param[out] read_bytes Number of bytes read. If data is NULL and
* max_data_len is 0 the number of bytes
* available for read should be returned.
*
* @return 0 on success, negative errno code on fail.
*/
int usb_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
uint32_t *read_bytes);
/**
* @brief Continue reading data from the endpoint
*
* Clear the endpoint NAK and enable the endpoint to accept more data
* from the host. Usually called after usb_ep_read_wait() when the consumer
* is fine to accept more data. Thus these calls together acts as flow control
* meachanism.
*
* @param[in] ep Endpoint address corresponding to the one
* listed in the device configuration table
*
* @return 0 on success, negative errno code on fail.
*/
int usb_ep_read_continue(uint8_t ep);
#endif /* USB_DEVICE_H_ */

View file

@ -627,6 +627,7 @@ static bool usb_handle_std_endpoint_req(struct usb_setup_packet *setup,
case REQ_CLEAR_FEATURE:
if (setup->wValue == FEA_ENDPOINT_HALT) {
/* clear HALT by unstalling */
SYS_LOG_INF("... EP clear halt %x\n", setup->wIndex);
usb_dc_ep_clear_stall(setup->wIndex);
break;
}
@ -636,6 +637,7 @@ static bool usb_handle_std_endpoint_req(struct usb_setup_packet *setup,
case REQ_SET_FEATURE:
if (setup->wValue == FEA_ENDPOINT_HALT) {
/* set HALT by stalling */
SYS_LOG_INF("--- EP SET halt %x\n", setup->wIndex);
usb_dc_ep_set_stall(setup->wIndex);
break;
}
@ -920,3 +922,24 @@ int usb_read(uint8_t ep, uint8_t *data, uint32_t max_data_len,
{
return usb_dc_ep_read(ep, data, max_data_len, ret_bytes);
}
int usb_ep_set_stall(uint8_t ep)
{
return usb_dc_ep_set_stall(ep);
}
int usb_ep_clear_stall(uint8_t ep)
{
return usb_dc_ep_clear_stall(ep);
}
int usb_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
uint32_t *ret_bytes)
{
return usb_dc_ep_read_wait(ep, data, max_data_len, ret_bytes);
}
int usb_ep_read_continue(uint8_t ep)
{
return usb_dc_ep_read_continue(ep);
}