source: mainline/uspace/drv/bus/usb/xhci/streams.c

Last change on this file was 3bacee1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Make ccheck-fix again and commit more good files.

  • Property mode set to 100644
File size: 14.7 KB
RevLine 
[47e9494]1/*
[e0a5d4c]2 * Copyright (c) 2018 Michal Staruch, Ondrej Hlavaty, Jan Hrach
[47e9494]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief Structures and functions for Superspeed bulk streams.
34 */
35
36#include "endpoint.h"
37#include "hc.h"
38#include "hw_struct/regs.h"
39#include "streams.h"
40
[8033f89]41/**
42 * Finds stream data with given stream ID if it exists.
[61e27e80]43 * Note that streams with ID 0, 65534 and 65535 are reserved.
44 * Splits the ID into primary and secondary context ID and searches the structures.
45 * @param[in] ep Affected endpoint.
46 * @param[in] stream_id Id of the stream.
47 */
[47e9494]48xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id)
49{
50 if (stream_id == 0 || stream_id >= 65534) {
51 return NULL;
52 }
53
54 /* See 4.12.2.1 for the calculation of the IDs and dividing the stream_id */
[8033f89]55 uint32_t primary_stream_id =
56 (uint32_t) (stream_id & (ep->primary_stream_data_size - 1));
57 uint32_t secondary_stream_id =
58 (uint32_t) ((stream_id / ep->primary_stream_data_size) & 0xFF);
[47e9494]59
60 if (primary_stream_id >= ep->primary_stream_data_size) {
61 return NULL;
62 }
63
[8033f89]64 xhci_stream_data_t *primary_data =
65 &ep->primary_stream_data_array[primary_stream_id];
[47e9494]66 if (secondary_stream_id != 0 && !primary_data->secondary_size) {
67 return NULL;
68 }
69
70 if (!primary_data->secondary_size) {
71 return primary_data;
72 }
73
74 xhci_stream_data_t *secondary_data = primary_data->secondary_data;
75 if (secondary_stream_id >= primary_data->secondary_size) {
76 return NULL;
77 }
78
79 return &secondary_data[secondary_stream_id];
80}
81
[8033f89]82/**
83 * Initializes primary stream data structures in endpoint.
[61e27e80]84 * @param[in] xhci_ep Used XHCI bulk endpoint.
85 * @param[in] count Amount of primary streams.
86 */
[45457265]87static errno_t initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
[5dab9ef0]88{
[8033f89]89 usb_log_debug("Allocating primary stream context array of size %u "
[3bacee1]90 "for endpoint " XHCI_EP_FMT, count, XHCI_EP_ARGS(*xhci_ep));
[5dab9ef0]91
[8033f89]92 if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma,
[3bacee1]93 count * sizeof(xhci_stream_ctx_t)))) {
[5dab9ef0]94 return ENOMEM;
95 }
96
97 xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
98 xhci_ep->primary_stream_data_array = calloc(count, sizeof(xhci_stream_data_t));
99 if (!xhci_ep->primary_stream_data_array) {
100 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
101 return ENOMEM;
102 }
103
104 xhci_ep->primary_stream_data_size = count;
105
106 return EOK;
107}
108
109static void clear_primary_structures(xhci_endpoint_t *xhci_ep)
110{
[8033f89]111 usb_log_debug("Deallocating primary stream structures for "
[3bacee1]112 "endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
[5dab9ef0]113
114 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
115 free(xhci_ep->primary_stream_data_array);
[1af4c00]116
117 xhci_ep->primary_stream_data_array = NULL;
118 xhci_ep->primary_stream_data_size = 0;
[5dab9ef0]119}
120
121static void clear_secondary_streams(xhci_endpoint_t *xhci_ep, unsigned index)
122{
123 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
124 if (!data->secondary_size) {
125 xhci_trb_ring_fini(&data->ring);
126 return;
127 }
128
129 for (size_t i = 0; i < data->secondary_size; ++i) {
130 xhci_trb_ring_fini(&data->secondary_data[i].ring);
131 }
132
133 dma_buffer_free(&data->secondary_stream_ctx_dma);
134 free(data->secondary_data);
135}
136
[47e9494]137void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
138{
[8033f89]139 usb_log_debug("Freeing stream rings and context arrays of endpoint "
[3bacee1]140 XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
[47e9494]141
142 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
[5dab9ef0]143 clear_secondary_streams(xhci_ep, index);
[47e9494]144 }
[5dab9ef0]145 clear_primary_structures(xhci_ep);
[47e9494]146}
147
[8033f89]148/**
149 * Initialize a single primary stream structure with given index.
[47e9494]150 * @param[in] hc Host controller of the endpoint.
[61e27e80]151 * @param[in] xhci_ep XHCI bulk endpoint to use.
[5dab9ef0]152 * @param[in] index index of the initialized stream structure.
[47e9494]153 */
[45457265]154static errno_t initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
[3bacee1]155 unsigned index)
[8033f89]156{
[47e9494]157 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
158 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
159 memset(data, 0, sizeof(xhci_stream_data_t));
160
[45457265]161 errno_t err = EOK;
[47e9494]162
[61e27e80]163 /* Init and register TRB ring for the primary stream */
[998773d]164 if ((err = xhci_trb_ring_init(&data->ring, 0))) {
[47e9494]165 return err;
166 }
167 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
168
169 /* Set to linear stream array */
170 XHCI_STREAM_SCT_SET(*ctx, 1);
171
172 return EOK;
173}
174
[8033f89]175/**
176 * Initialize primary streams of XHCI bulk endpoint.
[47e9494]177 * @param[in] hc Host controller of the endpoint.
[5dab9ef0]178 * @param[in] xhci_ep XHCI bulk endpoint to use.
[47e9494]179 */
[45457265]180static errno_t initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
[47e9494]181{
[45457265]182 errno_t err = EOK;
[5dab9ef0]183 size_t index;
184 for (index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
[47e9494]185 err = initialize_primary_stream(hc, xhci_ep, index);
186 if (err) {
[5dab9ef0]187 goto err_clean;
[47e9494]188 }
189 }
190
191 return EOK;
[5dab9ef0]192
193err_clean:
194 for (size_t i = 0; i < index; ++i) {
195 xhci_trb_ring_fini(&xhci_ep->primary_stream_data_array[i].ring);
196 }
197 return err;
[47e9494]198}
199
[8033f89]200/**
201 * Initialize secondary streams of XHCI bulk endpoint.
[47e9494]202 * @param[in] hc Host controller of the endpoint.
203 * @param[in] xhci_epi XHCI bulk endpoint to use.
[5dab9ef0]204 * @param[in] idx Index to primary stream array
[47e9494]205 * @param[in] count Number of secondary streams to initialize.
206 */
[45457265]207static errno_t initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
[3bacee1]208 unsigned idx, unsigned count)
[47e9494]209{
210 if (count == 0) {
[8033f89]211 /*
212 * The primary stream context can still point to a single ring, not
213 * a secondary.
214 */
[5dab9ef0]215 return initialize_primary_stream(hc, xhci_ep, idx);
[47e9494]216 }
217
218 if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
[8033f89]219 usb_log_error("The secondary stream array size must be a power of 2 "
[3bacee1]220 "between 8 and 256.");
[47e9494]221 return EINVAL;
222 }
223
[5dab9ef0]224 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[idx];
225 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[idx];
[47e9494]226 memset(data, 0, sizeof(xhci_stream_data_t));
227
228 data->secondary_size = count;
229 data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
230 if (!data->secondary_size) {
231 return ENOMEM;
232 }
233
[8033f89]234 if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma,
[3bacee1]235 count * sizeof(xhci_stream_ctx_t)))) {
[5dab9ef0]236 free(data->secondary_data);
[47e9494]237 return ENOMEM;
238 }
239 data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
240
[1d758fc]241 XHCI_STREAM_DEQ_PTR_SET(*ctx, dma_buffer_phys_base(&data->secondary_stream_ctx_dma));
[47e9494]242 XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
243
[61e27e80]244 /* Initialize all the rings. */
[45457265]245 errno_t err = EOK;
[5dab9ef0]246 size_t index;
247 for (index = 0; index < count; ++index) {
248 xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[index];
249 xhci_stream_data_t *secondary_data = &data->secondary_data[index];
[47e9494]250 /* Init and register TRB ring for every secondary stream */
[998773d]251 if ((err = xhci_trb_ring_init(&secondary_data->ring, 0))) {
[5dab9ef0]252 goto err_init;
[47e9494]253 }
254
255 XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
[5dab9ef0]256 /* Set to secondary stream array */
[47e9494]257 XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
258 }
259
260 return EOK;
[5dab9ef0]261
262err_init:
263 for (size_t i = 0; i < index; ++i) {
264 xhci_trb_ring_fini(&data->secondary_data[i].ring);
265 }
266 return err;
[47e9494]267}
268
[8033f89]269/**
270 * Configure XHCI bulk endpoint's stream context.
[47e9494]271 * @param[in] xhci_ep Associated XHCI bulk endpoint.
272 * @param[in] ctx Endpoint context to configure.
273 * @param[in] pstreams The value of MaxPStreams.
[0f803831]274 * @param[in] lsa Specifies if the stream IDs point to primary stream array.
[47e9494]275 */
[8033f89]276static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx,
[3bacee1]277 unsigned pstreams, unsigned lsa)
[47e9494]278{
279 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
280 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
281 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
282 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
283
284 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
[1d758fc]285 XHCI_EP_TR_DPTR_SET(*ctx, dma_buffer_phys_base(&xhci_ep->primary_stream_ctx_dma));
[47e9494]286 XHCI_EP_LSA_SET(*ctx, lsa);
287}
288
[8033f89]289/**
290 * Verifies if all the common preconditions are satisfied.
[61e27e80]291 * @param[in] hc Host controller of the endpoint.
292 * @param[in] dev Used device.
293 * @param[in] xhci_ep Associated XHCI bulk endpoint.
294 * @param[in] count Amount of primary streams requested.
295 */
[45457265]296static errno_t verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
[3bacee1]297 xhci_endpoint_t *xhci_ep, unsigned count)
[47e9494]298{
[3bacee1]299 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK ||
300 dev->base.speed != USB_SPEED_SUPER) {
[47e9494]301 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
302 return EINVAL;
303 }
304
[0f803831]305 if (xhci_ep->max_streams <= 1) {
[8033f89]306 usb_log_error("Streams are not supported by endpoint "
307 XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
[47e9494]308 return EINVAL;
309 }
310
[0f803831]311 if (count < 2) {
312 usb_log_error("The minumum amount of primary streams is 2.");
313 return EINVAL;
314 }
315
[8033f89]316 /*
317 * The maximum amount of primary streams is 2 ^ (MaxPSA + 1)
318 * See table 26 of XHCI specification.
319 */
[61e27e80]320 uint8_t max_psa_size = 1 << (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE) + 1);
[47e9494]321 if (count > max_psa_size) {
[8033f89]322 usb_log_error("Host controller only supports "
[3bacee1]323 "%u primary streams.", max_psa_size);
[47e9494]324 return EINVAL;
325 }
326
327 if (count > xhci_ep->max_streams) {
328 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
[3bacee1]329 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
[47e9494]330 return EINVAL;
331 }
332
333 if ((count & (count - 1)) != 0) {
334 usb_log_error("The amount of primary streams must be a power of 2.");
335 return EINVAL;
336 }
337
338 return EOK;
339}
340
[8033f89]341/**
342 * Cancels streams and reconfigures endpoint back to single ring no stream endpoint.
[1af4c00]343 * @param[in] hc Host controller of the endpoint.
344 * @param[in] dev Used device.
345 * @param[in] xhci_ep Associated XHCI bulk endpoint.
346 */
[45457265]347errno_t xhci_endpoint_remove_streams(xhci_hc_t *hc, xhci_device_t *dev,
[3bacee1]348 xhci_endpoint_t *xhci_ep)
[1af4c00]349{
350 if (!xhci_ep->primary_stream_data_size) {
351 usb_log_warning("There are no streams enabled on the endpoint, doing nothing.");
352 return EOK;
353 }
354
[51c1d500]355 hc_stop_endpoint(xhci_ep);
[1af4c00]356 xhci_endpoint_free_transfer_ds(xhci_ep);
357
358 /* Streams are now removed, proceed with reconfiguring endpoint. */
[45457265]359 errno_t err;
[998773d]360 if ((err = xhci_trb_ring_init(&xhci_ep->ring, 0))) {
[1af4c00]361 usb_log_error("Failed to initialize a transfer ring.");
362 return err;
363 }
364
[51c1d500]365 return hc_update_endpoint(xhci_ep);
[1af4c00]366}
367
[8033f89]368/**
369 * Initialize, setup and register primary streams.
[61e27e80]370 * @param[in] hc Host controller of the endpoint.
371 * @param[in] dev Used device.
372 * @param[in] xhci_ep Associated XHCI bulk endpoint.
373 * @param[in] count Amount of primary streams requested.
[47e9494]374 */
[45457265]375errno_t xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
[3bacee1]376 xhci_endpoint_t *xhci_ep, unsigned count)
[47e9494]377{
[45457265]378 errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
[47e9494]379 if (err) {
380 return err;
381 }
382
[1af4c00]383 /*
384 * We have passed the checks.
385 * Stop the endpoint, destroy the ring, and transition to streams.
386 */
[51c1d500]387 hc_stop_endpoint(xhci_ep);
[1af4c00]388 xhci_endpoint_free_transfer_ds(xhci_ep);
389
[47e9494]390 err = initialize_primary_structures(xhci_ep, count);
391 if (err) {
392 return err;
393 }
394
395 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
[5dab9ef0]396 err = initialize_primary_streams(hc, xhci_ep);
397 if (err) {
398 clear_primary_structures(xhci_ep);
399 return err;
400 }
[47e9494]401
402 xhci_ep_ctx_t ep_ctx;
[8033f89]403 /*
404 * Allowed values are 1-15, where 2 ^ pstreams is the actual amount of
405 * streams.
406 */
[47e9494]407 const size_t pstreams = fnzb32(count) - 1;
408 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
409
[51c1d500]410 return hc_update_endpoint(xhci_ep);
[47e9494]411}
412
[8033f89]413/**
414 * Initialize, setup and register secondary streams.
[61e27e80]415 * @param[in] hc Host controller of the endpoint.
416 * @param[in] dev Used device.
417 * @param[in] xhci_ep Associated XHCI bulk endpoint.
[8033f89]418 * @param[in] sizes Amount of secondary streams in each of the primary streams.
419 * This array should have exactly count elements. If the size
420 * is 0, then a primary ring is created with that index.
[61e27e80]421 * @param[in] count Amount of primary streams requested.
[47e9494]422 */
[45457265]423errno_t xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
[3bacee1]424 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
[47e9494]425{
[0f803831]426 /* Check if HC supports secondary indexing */
427 if (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_NSS)) {
428 usb_log_error("The host controller doesn't support secondary streams.");
429 return ENOTSUP;
430 }
431
[45457265]432 errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
[47e9494]433 if (err) {
434 return err;
435 }
436
[0f803831]437 if (count > 256) {
438 usb_log_error("The amount of primary streams cannot be higher than 256.");
439 return EINVAL;
440 }
441
442 /*
443 * Find the largest requested secondary stream size,
444 * that one is the maximum ID that device can receive.
445 * We need to make sure the device can handle that ID.
446 */
447 unsigned max = 0;
448 for (size_t index = 0; index < count; ++index) {
449 if (sizes[count] > max) {
450 max = sizes[count];
451 }
452 }
453
454 if (max * count > xhci_ep->max_streams) {
[47e9494]455 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
[3bacee1]456 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
[47e9494]457 return EINVAL;
458 }
459
[1af4c00]460 /*
461 * We have passed all checks.
462 * Stop the endpoint, destroy the ring, and transition to streams.
463 */
[51c1d500]464 hc_stop_endpoint(xhci_ep);
[1af4c00]465 xhci_endpoint_free_transfer_ds(xhci_ep);
466
[47e9494]467 err = initialize_primary_structures(xhci_ep, count);
468 if (err) {
469 return err;
470 }
471
472 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
[5dab9ef0]473 size_t index;
474 for (index = 0; index < count; ++index) {
475 err = initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
476 if (err) {
477 goto err_init;
478 }
[47e9494]479 }
480
481 xhci_ep_ctx_t ep_ctx;
482 const size_t pstreams = fnzb32(count) - 1;
483 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
484
[51c1d500]485 return hc_update_endpoint(xhci_ep);
[5dab9ef0]486
487err_init:
488 for (size_t i = 0; i < index; ++i) {
489 clear_secondary_streams(xhci_ep, i);
490 }
491 clear_primary_structures(xhci_ep);
492 return err;
[47e9494]493}
Note: See TracBrowser for help on using the repository browser.