HelenOS sources
This source file includes following definitions.
- xhci_get_stream_ctx_data
- initialize_primary_structures
- clear_primary_structures
- clear_secondary_streams
- xhci_stream_free_ds
- initialize_primary_stream
- initialize_primary_streams
- initialize_secondary_streams
- setup_stream_context
- verify_stream_conditions
- xhci_endpoint_remove_streams
- xhci_endpoint_request_primary_streams
- xhci_endpoint_request_secondary_streams
#include "endpoint.h"
#include "hc.h"
#include "hw_struct/regs.h"
#include "streams.h"
xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id)
{
if (stream_id == 0 || stream_id >= 65534) {
return NULL;
}
uint32_t primary_stream_id =
(uint32_t) (stream_id & (ep->primary_stream_data_size - 1));
uint32_t secondary_stream_id =
(uint32_t) ((stream_id / ep->primary_stream_data_size) & 0xFF);
if (primary_stream_id >= ep->primary_stream_data_size) {
return NULL;
}
xhci_stream_data_t *primary_data =
&ep->primary_stream_data_array[primary_stream_id];
if (secondary_stream_id != 0 && !primary_data->secondary_size) {
return NULL;
}
if (!primary_data->secondary_size) {
return primary_data;
}
xhci_stream_data_t *secondary_data = primary_data->secondary_data;
if (secondary_stream_id >= primary_data->secondary_size) {
return NULL;
}
return &secondary_data[secondary_stream_id];
}
static errno_t initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
{
usb_log_debug("Allocating primary stream context array of size %u "
"for endpoint " XHCI_EP_FMT, count, XHCI_EP_ARGS(*xhci_ep));
if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma,
count * sizeof(xhci_stream_ctx_t)))) {
return ENOMEM;
}
xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
xhci_ep->primary_stream_data_array = calloc(count, sizeof(xhci_stream_data_t));
if (!xhci_ep->primary_stream_data_array) {
dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
return ENOMEM;
}
xhci_ep->primary_stream_data_size = count;
return EOK;
}
static void clear_primary_structures(xhci_endpoint_t *xhci_ep)
{
usb_log_debug("Deallocating primary stream structures for "
"endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
free(xhci_ep->primary_stream_data_array);
xhci_ep->primary_stream_data_array = NULL;
xhci_ep->primary_stream_data_size = 0;
}
static void clear_secondary_streams(xhci_endpoint_t *xhci_ep, unsigned index)
{
xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
if (!data->secondary_size) {
xhci_trb_ring_fini(&data->ring);
return;
}
for (size_t i = 0; i < data->secondary_size; ++i) {
xhci_trb_ring_fini(&data->secondary_data[i].ring);
}
dma_buffer_free(&data->secondary_stream_ctx_dma);
free(data->secondary_data);
}
void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
{
usb_log_debug("Freeing stream rings and context arrays of endpoint "
XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
clear_secondary_streams(xhci_ep, index);
}
clear_primary_structures(xhci_ep);
}
static errno_t initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
unsigned index)
{
xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
memset(data, 0, sizeof(xhci_stream_data_t));
errno_t err = EOK;
if ((err = xhci_trb_ring_init(&data->ring, 0))) {
return err;
}
XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
XHCI_STREAM_SCT_SET(*ctx, 1);
return EOK;
}
static errno_t initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
{
errno_t err = EOK;
size_t index;
for (index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
err = initialize_primary_stream(hc, xhci_ep, index);
if (err) {
goto err_clean;
}
}
return EOK;
err_clean:
for (size_t i = 0; i < index; ++i) {
xhci_trb_ring_fini(&xhci_ep->primary_stream_data_array[i].ring);
}
return err;
}
static errno_t initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
unsigned idx, unsigned count)
{
if (count == 0) {
return initialize_primary_stream(hc, xhci_ep, idx);
}
if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
usb_log_error("The secondary stream array size must be a power of 2 "
"between 8 and 256.");
return EINVAL;
}
xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[idx];
xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[idx];
memset(data, 0, sizeof(xhci_stream_data_t));
data->secondary_size = count;
data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
if (!data->secondary_size) {
return ENOMEM;
}
if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma,
count * sizeof(xhci_stream_ctx_t)))) {
free(data->secondary_data);
return ENOMEM;
}
data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
XHCI_STREAM_DEQ_PTR_SET(*ctx, dma_buffer_phys_base(&data->secondary_stream_ctx_dma));
XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
errno_t err = EOK;
size_t index;
for (index = 0; index < count; ++index) {
xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[index];
xhci_stream_data_t *secondary_data = &data->secondary_data[index];
if ((err = xhci_trb_ring_init(&secondary_data->ring, 0))) {
goto err_init;
}
XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
}
return EOK;
err_init:
for (size_t i = 0; i < index; ++i) {
xhci_trb_ring_fini(&data->secondary_data[i].ring);
}
return err;
}
static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx,
unsigned pstreams, unsigned lsa)
{
XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
XHCI_EP_TR_DPTR_SET(*ctx, dma_buffer_phys_base(&xhci_ep->primary_stream_ctx_dma));
XHCI_EP_LSA_SET(*ctx, lsa);
}
static errno_t verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
xhci_endpoint_t *xhci_ep, unsigned count)
{
if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK ||
dev->base.speed != USB_SPEED_SUPER) {
usb_log_error("Streams are only supported by superspeed bulk endpoints.");
return EINVAL;
}
if (xhci_ep->max_streams <= 1) {
usb_log_error("Streams are not supported by endpoint "
XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
return EINVAL;
}
if (count < 2) {
usb_log_error("The minumum amount of primary streams is 2.");
return EINVAL;
}
uint8_t max_psa_size = 1 << (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE) + 1);
if (count > max_psa_size) {
usb_log_error("Host controller only supports "
"%u primary streams.", max_psa_size);
return EINVAL;
}
if (count > xhci_ep->max_streams) {
usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
return EINVAL;
}
if ((count & (count - 1)) != 0) {
usb_log_error("The amount of primary streams must be a power of 2.");
return EINVAL;
}
return EOK;
}
errno_t xhci_endpoint_remove_streams(xhci_hc_t *hc, xhci_device_t *dev,
xhci_endpoint_t *xhci_ep)
{
if (!xhci_ep->primary_stream_data_size) {
usb_log_warning("There are no streams enabled on the endpoint, doing nothing.");
return EOK;
}
hc_stop_endpoint(xhci_ep);
xhci_endpoint_free_transfer_ds(xhci_ep);
errno_t err;
if ((err = xhci_trb_ring_init(&xhci_ep->ring, 0))) {
usb_log_error("Failed to initialize a transfer ring.");
return err;
}
return hc_update_endpoint(xhci_ep);
}
errno_t xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
xhci_endpoint_t *xhci_ep, unsigned count)
{
errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
if (err) {
return err;
}
hc_stop_endpoint(xhci_ep);
xhci_endpoint_free_transfer_ds(xhci_ep);
err = initialize_primary_structures(xhci_ep, count);
if (err) {
return err;
}
memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
err = initialize_primary_streams(hc, xhci_ep);
if (err) {
clear_primary_structures(xhci_ep);
return err;
}
xhci_ep_ctx_t ep_ctx;
const size_t pstreams = fnzb32(count) - 1;
setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
return hc_update_endpoint(xhci_ep);
}
errno_t xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
{
if (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_NSS)) {
usb_log_error("The host controller doesn't support secondary streams.");
return ENOTSUP;
}
errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
if (err) {
return err;
}
if (count > 256) {
usb_log_error("The amount of primary streams cannot be higher than 256.");
return EINVAL;
}
unsigned max = 0;
for (size_t index = 0; index < count; ++index) {
if (sizes[count] > max) {
max = sizes[count];
}
}
if (max * count > xhci_ep->max_streams) {
usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
return EINVAL;
}
hc_stop_endpoint(xhci_ep);
xhci_endpoint_free_transfer_ds(xhci_ep);
err = initialize_primary_structures(xhci_ep, count);
if (err) {
return err;
}
memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
size_t index;
for (index = 0; index < count; ++index) {
err = initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
if (err) {
goto err_init;
}
}
xhci_ep_ctx_t ep_ctx;
const size_t pstreams = fnzb32(count) - 1;
setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
return hc_update_endpoint(xhci_ep);
err_init:
for (size_t i = 0; i < index; ++i) {
clear_secondary_streams(xhci_ep, i);
}
clear_primary_structures(xhci_ep);
return err;
}
HelenOS homepage, sources at GitHub