diff --git a/drivers/usb/device/usb_dc_dw.c b/drivers/usb/device/usb_dc_dw.c index 9ecadaac367..db2e6f33753 100644 --- a/drivers/usb/device/usb_dc_dw.c +++ b/drivers/usb/device/usb_dc_dw.c @@ -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); diff --git a/include/drivers/usb/usb_dc.h b/include/drivers/usb/usb_dc.h index 750197db38c..e6a0f4492da 100644 --- a/include/drivers/usb/usb_dc.h +++ b/include/drivers/usb/usb_dc.h @@ -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__ */ diff --git a/include/usb/usb_device.h b/include/usb/usb_device.h index 4de42b976a2..c2f53292154 100644 --- a/include/usb/usb_device.h +++ b/include/usb/usb_device.h @@ -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_ */ diff --git a/subsys/usb/usb_device.c b/subsys/usb/usb_device.c index de40ba45fe6..c5c0f2e0121 100644 --- a/subsys/usb/usb_device.c +++ b/subsys/usb/usb_device.c @@ -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); +}