usb: audio: Fixed switching to alternate interface

For USB audio class interface may have alternate interfaces.
Those alternates may have different endpoint configurations.

When Standard Request SET_INTERFACE is served it choses between
alternate interfaces and configuring associated endpoints.

When switching between alternate interfaces, endpoints
associated with them must be disabled/enabled accordingly.

Signed-off-by: Emil Obalski <emil.obalski@nordicsemi.no>
This commit is contained in:
Emil Obalski 2020-01-03 11:45:59 +01:00 committed by Johan Hedberg
parent e6ba235376
commit d290dccfec

View File

@ -484,6 +484,16 @@ static bool usb_get_descriptor(u16_t type_index, u16_t lang_id,
return found;
}
/*
* @brief configure and enable endpoint
*
* This function sets endpoint configuration according to one specified in USB
* endpoint descriptor and then enables it for data transfers.
*
* @param [in] ep_desc Endpoint descriptor byte array
*
* @return true if successfully configured and enabled
*/
static bool set_endpoint(const struct usb_ep_descriptor *ep_desc)
{
struct usb_dc_ep_cfg_data ep_cfg;
@ -497,15 +507,15 @@ static bool set_endpoint(const struct usb_ep_descriptor *ep_desc)
ep_cfg.ep_type = ep_desc->bmAttributes;
LOG_DBG("Configure endpoint 0x%x type %u MPS %u",
LOG_DBG("Set endpoint 0x%x type %u MPS %u",
ep_cfg.ep_addr, ep_cfg.ep_type, ep_cfg.ep_mps);
if (usb_dc_ep_configure(&ep_cfg) < 0) {
LOG_WRN("Failed to configure endpoint %x", ep_cfg.ep_addr);
LOG_WRN("Failed to configure endpoint 0x%02x", ep_cfg.ep_addr);
}
if (usb_dc_ep_enable(ep_cfg.ep_addr) < 0) {
LOG_WRN("Failed to enable endpoint %x", ep_cfg.ep_addr);
LOG_WRN("Failed to enable endpoint 0x%02x", ep_cfg.ep_addr);
}
usb_dev.configured = true;
@ -513,6 +523,57 @@ static bool set_endpoint(const struct usb_ep_descriptor *ep_desc)
return true;
}
/*
* @brief Disable endpoint for transferring data
*
* This function cancels transfers that are associated with endpoint and
* disabled endpoint itself.
*
* @param [in] ep_desc Endpoint descriptor byte array
*
* @return true if successfully deconfigured and disabled
*/
static bool reset_endpoint(const struct usb_ep_descriptor *ep_desc)
{
struct usb_dc_ep_cfg_data ep_cfg;
ep_cfg.ep_addr = ep_desc->bEndpointAddress;
ep_cfg.ep_type = ep_desc->bmAttributes;
if (ep_desc->bmAttributes > USB_DC_EP_INTERRUPT) {
return false;
}
LOG_DBG("Reset endpoint 0x%02x type %u",
ep_cfg.ep_addr, ep_cfg.ep_type);
usb_cancel_transfer(ep_cfg.ep_addr);
if (usb_dc_ep_disable(ep_cfg.ep_addr) < 0) {
LOG_ERR("Failed to disable endpoint 0x%02x", ep_cfg.ep_addr);
return false;
}
return true;
}
static bool usb_eps_reconfigure(struct usb_ep_descriptor *ep_desc,
u8_t cur_alt_setting,
u8_t alt_setting)
{
bool ret;
if (cur_alt_setting != alt_setting) {
LOG_DBG("Disable endpoint 0x%02x", ep_desc->bEndpointAddress);
ret = reset_endpoint(ep_desc);
} else {
LOG_DBG("Enable endpoint 0x%02x", ep_desc->bEndpointAddress);
ret = set_endpoint(ep_desc);
}
return ret;
}
/*
* @brief set USB configuration
*
@ -592,9 +653,10 @@ static bool usb_set_interface(u8_t iface, u8_t alt_setting)
{
const u8_t *p = usb_dev.descriptors;
const u8_t *if_desc = NULL;
struct usb_ep_descriptor *ep;
u8_t cur_alt_setting = 0xFF;
u8_t cur_iface = 0xFF;
bool found = false;
bool ret = false;
LOG_DBG("iface %u alt_setting %u", iface, alt_setting);
@ -610,14 +672,15 @@ static bool usb_set_interface(u8_t iface, u8_t alt_setting)
if_desc = (void *)p;
}
LOG_DBG("iface_num %u alt_set %u", iface, alt_setting);
LOG_DBG("Current iface %u alt setting %u",
cur_iface, cur_alt_setting);
break;
case USB_ENDPOINT_DESC:
if ((cur_iface != iface) ||
(cur_alt_setting != alt_setting)) {
if (cur_iface == iface) {
ep = (struct usb_ep_descriptor *)p;
ret = usb_eps_reconfigure(ep, cur_alt_setting,
alt_setting);
}
found = set_endpoint((struct usb_ep_descriptor *)p);
break;
default:
break;
@ -631,7 +694,7 @@ static bool usb_set_interface(u8_t iface, u8_t alt_setting)
usb_dev.status_callback(USB_DC_INTERFACE, if_desc);
}
return found;
return ret;
}
/*