USB & XHCI: Refactor endpoint initialization to support SuperSpeed better.
SuperSpeed (USB3) devices have a "companion descriptor" along with the endpoint descriptors that describes certain other attributes they have, which are important for the controller to schedule transfers properly. Previously we were just using USB2 values; now we are properly using USB3 ones. Tested on an Intel Lynx Point controller. As far as I can tell, no regressions, but #15000 is not fixed anyway.
This commit is contained in:
parent
c8836afc0a
commit
086528f66a
@ -254,6 +254,22 @@ Device::Device(Object* parent, int8 hubAddress, uint8 hubPort,
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DESCRIPTOR_ENDPOINT_COMPANION: {
|
||||
usb_endpoint_descriptor* desc = currentInterface
|
||||
->endpoint[currentInterface->endpoint_count - 1].descr;
|
||||
if ((uint8*)desc != (&configData[descriptorStart
|
||||
- desc->length])) {
|
||||
TRACE_ERROR("found endpoint companion descriptor not immediately "
|
||||
"following endpoint descriptor, ignoring!\n");
|
||||
break;
|
||||
}
|
||||
// TODO: It'd be nicer if we could store the endpoint companion
|
||||
// descriptor along with the endpoint descriptor, but as the
|
||||
// interface struct is public API, that would be an ABI break.
|
||||
|
||||
// fall through
|
||||
}
|
||||
|
||||
default:
|
||||
TRACE("got generic descriptor\n");
|
||||
usb_generic_descriptor* genericDescriptor
|
||||
@ -472,6 +488,29 @@ Device::InitEndpoints(int32 interfaceIndex)
|
||||
usb_endpoint_info* endpoint = &interfaceInfo->endpoint[i];
|
||||
Pipe* pipe = NULL;
|
||||
|
||||
usb_endpoint_companion_descriptor* comp_descr = NULL;
|
||||
if (fSpeed == USB_SPEED_SUPER) {
|
||||
// We should have a companion descriptor for this device.
|
||||
// Let's find it: it'll be the "i"th one.
|
||||
size_t k = 0;
|
||||
for (size_t j = 0; j < interfaceInfo->generic_count; j++) {
|
||||
usb_descriptor* desc = interfaceInfo->generic[j];
|
||||
if (desc->endpoint.descriptor_type
|
||||
!= USB_DESCRIPTOR_ENDPOINT_COMPANION) {
|
||||
continue;
|
||||
}
|
||||
if (k == i) {
|
||||
comp_descr = (usb_endpoint_companion_descriptor*)desc;
|
||||
break;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
if (comp_descr == NULL) {
|
||||
TRACE_ERROR("SuperSpeed device without an endpoint companion "
|
||||
"descriptor!");
|
||||
}
|
||||
}
|
||||
|
||||
Pipe::pipeDirection direction = Pipe::Out;
|
||||
if ((endpoint->descr->endpoint_address & 0x80) != 0)
|
||||
direction = Pipe::In;
|
||||
@ -505,6 +544,10 @@ Device::InitEndpoints(int32 interfaceIndex)
|
||||
endpoint->descr->endpoint_address & 0x0f,
|
||||
fSpeed, direction, endpoint->descr->max_packet_size,
|
||||
endpoint->descr->interval, fHubAddress, fHubPort);
|
||||
if (comp_descr != NULL) {
|
||||
pipe->InitSuperSpeed(comp_descr->max_burst,
|
||||
comp_descr->bytes_per_interval);
|
||||
}
|
||||
endpoint->handle = pipe->USBID();
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,21 @@ Pipe::InitCommon(int8 deviceAddress, uint8 endpointAddress, usb_speed speed,
|
||||
fHubAddress = hubAddress;
|
||||
fHubPort = hubPort;
|
||||
|
||||
fMaxBurst = 0;
|
||||
fBytesPerInterval = 0;
|
||||
|
||||
GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
|
||||
{
|
||||
fMaxBurst = maxBurst;
|
||||
fBytesPerInterval = bytesPerInterval;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Pipe::SetHubInfo(int8 address, uint8 port)
|
||||
{
|
||||
|
@ -297,6 +297,8 @@ virtual void InitCommon(int8 deviceAddress,
|
||||
size_t maxPacketSize,
|
||||
uint8 interval,
|
||||
int8 hubAddress, uint8 hubPort);
|
||||
virtual void InitSuperSpeed(uint8 maxBurst,
|
||||
uint16 bytesPerInterval);
|
||||
|
||||
virtual uint32 Type() const { return USB_OBJECT_PIPE; }
|
||||
virtual const char * TypeName() const { return "pipe"; }
|
||||
@ -311,6 +313,12 @@ virtual const char * TypeName() const { return "pipe"; }
|
||||
{ return fMaxPacketSize; }
|
||||
uint8 Interval() const { return fInterval; }
|
||||
|
||||
// SuperSpeed-only parameters
|
||||
uint8 MaxBurst() const
|
||||
{ return fMaxBurst; }
|
||||
uint16 BytesPerInterval() const
|
||||
{ return fBytesPerInterval; }
|
||||
|
||||
// Hub port being the one-based logical port number on the hub
|
||||
void SetHubInfo(int8 address, uint8 port);
|
||||
int8 HubAddress() const
|
||||
@ -342,6 +350,8 @@ private:
|
||||
usb_speed fSpeed;
|
||||
size_t fMaxPacketSize;
|
||||
uint8 fInterval;
|
||||
uint8 fMaxBurst;
|
||||
uint16 fBytesPerInterval;
|
||||
int8 fHubAddress;
|
||||
uint8 fHubPort;
|
||||
bool fDataToggle;
|
||||
|
@ -71,6 +71,17 @@ struct usb_hub_descriptor {
|
||||
#define USB_DESCRIPTOR_HUB 0x29
|
||||
|
||||
|
||||
struct usb_endpoint_companion_descriptor {
|
||||
uint8 length;
|
||||
uint8 descriptor_type;
|
||||
uint8 max_burst;
|
||||
uint8 attributes;
|
||||
uint16 bytes_per_interval;
|
||||
} _PACKED;
|
||||
|
||||
#define USB_DESCRIPTOR_ENDPOINT_COMPANION 0x30
|
||||
|
||||
|
||||
// USB Spec 1.1 page 273
|
||||
struct usb_port_status
|
||||
{
|
||||
|
@ -1395,7 +1395,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
|
||||
|
||||
// configure the Control endpoint 0
|
||||
if (ConfigureEndpoint(slot, 0, USB_OBJECT_CONTROL_PIPE, false,
|
||||
device->trb_addr, 0, maxPacketSize, speed) != B_OK) {
|
||||
device->trb_addr, 0, maxPacketSize, speed, 0, 0) != B_OK) {
|
||||
TRACE_ERROR("unable to configure default control endpoint\n");
|
||||
delete_area(device->input_ctx_area);
|
||||
delete_area(device->device_ctx_area);
|
||||
@ -1645,7 +1645,8 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
|
||||
|
||||
status_t status = ConfigureEndpoint(device->slot, id, pipe->Type(),
|
||||
pipe->Direction() == Pipe::In, device->endpoints[id].trb_addr,
|
||||
pipe->Interval(), pipe->MaxPacketSize(), usbDevice->Speed());
|
||||
pipe->Interval(), pipe->MaxPacketSize(), usbDevice->Speed(),
|
||||
pipe->MaxBurst(), pipe->BytesPerInterval());
|
||||
if (status != B_OK) {
|
||||
TRACE_ERROR("unable to configure endpoint\n");
|
||||
return status;
|
||||
@ -1844,7 +1845,8 @@ XHCI::_UnlinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
|
||||
|
||||
status_t
|
||||
XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
|
||||
uint64 ringAddr, uint16 interval, uint16 maxPacketSize, usb_speed speed)
|
||||
uint64 ringAddr, uint16 interval, uint16 maxPacketSize, usb_speed speed,
|
||||
uint8 maxBurst, uint16 bytesPerInterval)
|
||||
{
|
||||
struct xhci_device* device = &fDevices[slot];
|
||||
|
||||
@ -1903,21 +1905,18 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
|
||||
|
||||
// For non-isochronous endpoints, we want the controller to retry failed
|
||||
// transfers, if possible. (XHCI 1.1 § 4.10.2.3 p189.)
|
||||
if (!(type & USB_OBJECT_ISO_PIPE))
|
||||
if ((type & USB_OBJECT_ISO_PIPE) == 0)
|
||||
dwendpoint1 |= ENDPOINT_1_CERR(3);
|
||||
|
||||
// Assign maximum burst size.
|
||||
// TODO: While computing the maximum burst this way is correct for USB2
|
||||
// devices, it is merely acceptable for USB3 devices, which have a more
|
||||
// correct value stored in the Companion Descriptor. (Further, this value
|
||||
// in the USB3 Companion Descriptor is to be used for *all* endpoints, not
|
||||
// just Interrupt and Isoch ones.)
|
||||
uint8 maxBurst = (maxPacketSize & 0x1800) >> 11;
|
||||
if (speed >= USB_SPEED_HIGHSPEED
|
||||
&& (((type & USB_OBJECT_INTERRUPT_PIPE) != 0)
|
||||
|| (type & USB_OBJECT_ISO_PIPE) != 0)) {
|
||||
dwendpoint1 |= ENDPOINT_1_MAXBURST(maxBurst);
|
||||
// Assign maximum burst size. For USB3 devices this is passed in; for
|
||||
// all other devices we compute it. (XHCI 1.1 § 4.8.2 p154.)
|
||||
if (speed == USB_SPEED_HIGHSPEED && (type & (USB_OBJECT_INTERRUPT_PIPE
|
||||
| USB_OBJECT_ISO_PIPE)) != 0) {
|
||||
maxBurst = (maxPacketSize & 0x1800) >> 11;
|
||||
} else if (speed != USB_SPEED_SUPER) {
|
||||
maxBurst = 0;
|
||||
}
|
||||
dwendpoint1 |= ENDPOINT_1_MAXBURST(maxBurst);
|
||||
|
||||
// Assign maximum packet size, set the ring address, and set the
|
||||
// "Dequeue Cycle State" bit. (XHCI 1.1 § 6.2.3 Table 6-10 p430.)
|
||||
@ -1934,10 +1933,15 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
|
||||
}
|
||||
|
||||
// Assign maximum ESIT payload. (XHCI 1.1 § 4.14.2 p250.)
|
||||
// TODO: This computation is *only* correct for USB2 devices.
|
||||
if (((type & USB_OBJECT_INTERRUPT_PIPE) != 0)
|
||||
|| ((type & USB_OBJECT_ISO_PIPE) != 0)) {
|
||||
dwendpoint4 |= ENDPOINT_4_MAXESITPAYLOAD((maxBurst + 1) * maxPacketSize);
|
||||
if ((type & (USB_OBJECT_INTERRUPT_PIPE | USB_OBJECT_ISO_PIPE)) != 0) {
|
||||
// TODO: For SuperSpeedPlus endpoints, there is yet another descriptor
|
||||
// for isochronous endpoints that specifies the maximum ESIT payload.
|
||||
// We don't fetch this yet, so just fall back to the USB2 computation
|
||||
// method if bytesPerInterval is 0.
|
||||
if (speed == USB_SPEED_SUPER && bytesPerInterval != 0)
|
||||
dwendpoint4 |= ENDPOINT_4_MAXESITPAYLOAD(bytesPerInterval);
|
||||
else if (speed >= USB_SPEED_HIGHSPEED)
|
||||
dwendpoint4 |= ENDPOINT_4_MAXESITPAYLOAD((maxBurst + 1) * maxPacketSize);
|
||||
}
|
||||
|
||||
_WriteContext(&device->input_ctx->endpoints[number].dwendpoint0,
|
||||
|
@ -137,7 +137,8 @@ private:
|
||||
status_t ConfigureEndpoint(uint8 slot, uint8 number,
|
||||
uint8 type, bool directionIn, uint64 ringAddr,
|
||||
uint16 interval, uint16 maxPacketSize,
|
||||
usb_speed speed);
|
||||
usb_speed speed, uint8 maxBurst,
|
||||
uint16 bytesPerInterval);
|
||||
status_t _InsertEndpointForPipe(Pipe *pipe);
|
||||
status_t _RemoveEndpointForPipe(Pipe *pipe);
|
||||
|
||||
|
@ -31,20 +31,11 @@ static usb_device_descriptor sXHCIRootHubDevice =
|
||||
};
|
||||
|
||||
|
||||
struct usb_endpoint_ss_comp_descriptor {
|
||||
uint8 length;
|
||||
uint8 descriptor_type;
|
||||
uint16 burst;
|
||||
uint8 attributes;
|
||||
uint16 internal;
|
||||
} _PACKED;
|
||||
|
||||
|
||||
struct xhci_root_hub_configuration_s {
|
||||
usb_configuration_descriptor configuration;
|
||||
usb_interface_descriptor interface;
|
||||
usb_endpoint_descriptor endpoint;
|
||||
usb_endpoint_ss_comp_descriptor endpc;
|
||||
usb_endpoint_companion_descriptor endpc;
|
||||
usb_hub_descriptor hub;
|
||||
} _PACKED;
|
||||
|
||||
@ -86,7 +77,7 @@ static xhci_root_hub_configuration_s sXHCIRootHubConfig =
|
||||
|
||||
{ // endpoint companion descriptor
|
||||
7,
|
||||
0x30,
|
||||
USB_DESCRIPTOR_ENDPOINT_COMPANION,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
|
Loading…
x
Reference in New Issue
Block a user