Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usbus: Add support for endpoint halt condition #17090

Merged
merged 2 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions sys/include/usb/descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ extern "C" {
#define USB_TYPE_DESCRIPTOR_INTERFACE_ASSOC 0x0b /**< Interface association */
/** @} */

/**
* @name USB standard feature selectors
* @{
*/
#define USB_FEATURE_ENDPOINT_HALT 0x00 /**< Endpoint halt */
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 0x01 /**< Device remote wakeup */
#define USB_FEATURE_TEST_MODE 0x02 /**< Test mode feature */
/** @} */

/**
* @name USB configuration attributes
* @anchor USB_CONF_ATTR
Expand Down
17 changes: 17 additions & 0 deletions sys/include/usb/usbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ typedef struct usbus_endpoint {
uint8_t interval; /**< Poll interval for interrupt endpoints */
bool active; /**< If the endpoint should be activated after
reset */
bool halted; /**< Endpoint is halted */
} usbus_endpoint_t;

/**
Expand Down Expand Up @@ -676,6 +677,22 @@ void usbus_urb_submit(usbus_t *usbus, usbus_endpoint_t *endpoint, usbus_urb_t *u
*/
int usbus_urb_cancel(usbus_t *usbus, usbus_endpoint_t *endpoint, usbus_urb_t *urb);

/**
* @brief Set the halt condition on an endpoint.
*
* The endpoint will respond with stall to all packets and must explicitly be
* cleared by the host by clearing the halt condition or switching interfaces
*/
void usbus_endpoint_halt(usbus_endpoint_t *ep);

/**
* @brief Clear the halt condition on an endpoint
*
* @note Must only be used when the endpoint is halted and when the host issues
* a SetInterface request on the interface containing the endpoint
*/
void usbus_endpoint_clear_halt(usbus_endpoint_t *ep);

/**
* @brief Enable an endpoint
*
Expand Down
29 changes: 29 additions & 0 deletions sys/usb/usbus/usbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,34 @@ static void *_usbus_thread(void *args)
return NULL;
}

void usbus_endpoint_halt(usbus_endpoint_t *ep)
{
assert(ep->ep->num != 0); /* Not valid for endpoint 0 */
DEBUG("Endpoint %u halted\n", ep->ep->num);
ep->halted = 1;
usbdev_ep_stall(ep->ep, true);
}

void usbus_endpoint_clear_halt(usbus_endpoint_t *ep)
{
assert(ep->ep->num != 0); /* Not valid for endpoint 0 */
DEBUG("Endpoint %u unhalted\n", ep->ep->num);
ep->halted = 0;
usbdev_ep_stall(ep->ep, false);
}

/**
* @brief Reset the halted status on USB reset condition
*/
static void _usbus_endpoint_reset_halt(usbus_t *usbus)
{
/* Clear halted state. No need to notify usbdev, USB reset already resets those */
for (size_t i = 0; i < USBDEV_NUM_ENDPOINTS; i++) {
usbus->ep_out[i].halted = 0;
usbus->ep_in[i].halted = 0;
}
}

/* USB event callback */
static void _event_cb(usbdev_t *usbdev, usbdev_event_t event)
{
Expand All @@ -487,6 +515,7 @@ static void _event_cb(usbdev_t *usbdev, usbdev_event_t event)
usbus->addr = 0;
usbdev_set(usbus->dev, USBOPT_ADDRESS, &usbus->addr,
sizeof(uint8_t));
_usbus_endpoint_reset_halt(usbus);
flag = USBUS_HANDLER_FLAG_RESET;
msg = USBUS_EVENT_USB_RESET;
DEBUG("usbus: USB reset detected\n");
Expand Down
60 changes: 57 additions & 3 deletions sys/usb/usbus/usbus_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,39 @@ static usbus_string_t *_get_descriptor(usbus_t *usbus, uint16_t idx)

static int _req_status(usbus_t *usbus)
{
uint8_t status[2];
/* Signal self powered */
uint16_t status = (CONFIG_USB_SELF_POWERED) ? 1 : 0;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&status, sizeof(status));
return sizeof(status);
}

static int _req_iface_status(usbus_t *usbus)
{
uint16_t status = 0; /* always zero */
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&status, sizeof(status));
return sizeof(status);
}

memset(status, 0, sizeof(status));
usbus_control_slicer_put_bytes(usbus, status, sizeof(status));
static int _req_endpoint_status(usbus_t *usbus, usbus_endpoint_t *ep)
{
uint16_t status = ep->halted ? 1 : 0;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&status, sizeof(status));
return sizeof(status);
}

static int _req_endpoint_feature(usbus_endpoint_t *ep, uint16_t feature, bool enable)
{
switch (feature) {
case USB_FEATURE_ENDPOINT_HALT:
enable ? usbus_endpoint_halt(ep) : usbus_endpoint_clear_halt(ep);
break;
default:
DEBUG("usbus: unknown endpoint feature request: %u\n", feature);
return -1;
}
return 1;
}

static int _req_str(usbus_t *usbus, uint16_t idx)
{
/* Return an error condition by default */
Expand Down Expand Up @@ -238,6 +264,11 @@ static int _recv_interface_setup(usbus_t *usbus, usb_setup_t *pkt)
(usbus_control_handler_t *)usbus->control;
uint16_t destination = pkt->index & 0x0f;

/* Globally handle the iface get status request */
if (pkt->request == USB_SETUP_REQ_GET_STATUS) {
return _req_iface_status(usbus);
}

/* Find interface handler */
for (usbus_interface_t *iface = usbus->iface; iface; iface = iface->next) {
if (destination == iface->idx &&
Expand All @@ -251,6 +282,26 @@ static int _recv_interface_setup(usbus_t *usbus, usb_setup_t *pkt)
return -1;
}

static int _recv_endpoint_setup(usbus_t *usbus, usb_setup_t *pkt)
{
uint8_t destination = pkt->index & 0x0f;
bool in = pkt->index & (1 << 7); /* Bit seven is 1 for IN, 0 for OUT */
usbus_endpoint_t *ep = in ? &usbus->ep_in[destination] :
&usbus->ep_out[destination];

switch (pkt->request) {
case USB_SETUP_REQ_GET_STATUS:
return _req_endpoint_status(usbus, ep);
case USB_SETUP_REQ_SET_FEATURE:
return _req_endpoint_feature(ep, pkt->value, true);
case USB_SETUP_REQ_CLEAR_FEATURE:
return _req_endpoint_feature(ep, pkt->value, false);
default:
DEBUG("usbus: Unknown endpoint request %u\n", pkt->request);
return -1;
}
}

static void _recv_setup(usbus_t *usbus, usbus_control_handler_t *handler)
{
usb_setup_t *pkt = &handler->setup;
Expand All @@ -272,6 +323,9 @@ static void _recv_setup(usbus_t *usbus, usbus_control_handler_t *handler)
case USB_SETUP_REQUEST_RECIPIENT_INTERFACE:
res = _recv_interface_setup(usbus, pkt);
break;
case USB_SETUP_REQUEST_RECIPIENT_ENDPOINT:
res = _recv_endpoint_setup(usbus, pkt);
break;
default:
DEBUG("usbus_control: Unhandled setup request\n");
}
Expand Down