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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 14.7 KB
Line 
1/*
2 * Copyright (c) 2018 Michal Staruch, Ondrej Hlavaty, Jan Hrach
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
41/**
42 * Finds stream data with given stream ID if it exists.
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 */
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 */
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);
59
60 if (primary_stream_id >= ep->primary_stream_data_size) {
61 return NULL;
62 }
63
64 xhci_stream_data_t *primary_data =
65 &ep->primary_stream_data_array[primary_stream_id];
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
82/**
83 * Initializes primary stream data structures in endpoint.
84 * @param[in] xhci_ep Used XHCI bulk endpoint.
85 * @param[in] count Amount of primary streams.
86 */
87static errno_t initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
88{
89 usb_log_debug("Allocating primary stream context array of size %u "
90 "for endpoint " XHCI_EP_FMT, count, XHCI_EP_ARGS(*xhci_ep));
91
92 if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma,
93 count * sizeof(xhci_stream_ctx_t)))) {
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{
111 usb_log_debug("Deallocating primary stream structures for "
112 "endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
113
114 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
115 free(xhci_ep->primary_stream_data_array);
116
117 xhci_ep->primary_stream_data_array = NULL;
118 xhci_ep->primary_stream_data_size = 0;
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
137void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
138{
139 usb_log_debug("Freeing stream rings and context arrays of endpoint "
140 XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
141
142 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
143 clear_secondary_streams(xhci_ep, index);
144 }
145 clear_primary_structures(xhci_ep);
146}
147
148/**
149 * Initialize a single primary stream structure with given index.
150 * @param[in] hc Host controller of the endpoint.
151 * @param[in] xhci_ep XHCI bulk endpoint to use.
152 * @param[in] index index of the initialized stream structure.
153 */
154static errno_t initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
155 unsigned index)
156{
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
161 errno_t err = EOK;
162
163 /* Init and register TRB ring for the primary stream */
164 if ((err = xhci_trb_ring_init(&data->ring, 0))) {
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
175/**
176 * Initialize primary streams of XHCI bulk endpoint.
177 * @param[in] hc Host controller of the endpoint.
178 * @param[in] xhci_ep XHCI bulk endpoint to use.
179 */
180static errno_t initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
181{
182 errno_t err = EOK;
183 size_t index;
184 for (index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
185 err = initialize_primary_stream(hc, xhci_ep, index);
186 if (err) {
187 goto err_clean;
188 }
189 }
190
191 return EOK;
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;
198}
199
200/**
201 * Initialize secondary streams of XHCI bulk endpoint.
202 * @param[in] hc Host controller of the endpoint.
203 * @param[in] xhci_epi XHCI bulk endpoint to use.
204 * @param[in] idx Index to primary stream array
205 * @param[in] count Number of secondary streams to initialize.
206 */
207static errno_t initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep,
208 unsigned idx, unsigned count)
209{
210 if (count == 0) {
211 /*
212 * The primary stream context can still point to a single ring, not
213 * a secondary.
214 */
215 return initialize_primary_stream(hc, xhci_ep, idx);
216 }
217
218 if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
219 usb_log_error("The secondary stream array size must be a power of 2 "
220 "between 8 and 256.");
221 return EINVAL;
222 }
223
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];
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
234 if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma,
235 count * sizeof(xhci_stream_ctx_t)))) {
236 free(data->secondary_data);
237 return ENOMEM;
238 }
239 data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
240
241 XHCI_STREAM_DEQ_PTR_SET(*ctx, dma_buffer_phys_base(&data->secondary_stream_ctx_dma));
242 XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
243
244 /* Initialize all the rings. */
245 errno_t err = EOK;
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];
250 /* Init and register TRB ring for every secondary stream */
251 if ((err = xhci_trb_ring_init(&secondary_data->ring, 0))) {
252 goto err_init;
253 }
254
255 XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
256 /* Set to secondary stream array */
257 XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
258 }
259
260 return EOK;
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;
267}
268
269/**
270 * Configure XHCI bulk endpoint's stream context.
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.
274 * @param[in] lsa Specifies if the stream IDs point to primary stream array.
275 */
276static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx,
277 unsigned pstreams, unsigned lsa)
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);
285 XHCI_EP_TR_DPTR_SET(*ctx, dma_buffer_phys_base(&xhci_ep->primary_stream_ctx_dma));
286 XHCI_EP_LSA_SET(*ctx, lsa);
287}
288
289/**
290 * Verifies if all the common preconditions are satisfied.
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 */
296static errno_t verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
297 xhci_endpoint_t *xhci_ep, unsigned count)
298{
299 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
300 || dev->base.speed != USB_SPEED_SUPER) {
301 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
302 return EINVAL;
303 }
304
305 if (xhci_ep->max_streams <= 1) {
306 usb_log_error("Streams are not supported by endpoint "
307 XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
308 return EINVAL;
309 }
310
311 if (count < 2) {
312 usb_log_error("The minumum amount of primary streams is 2.");
313 return EINVAL;
314 }
315
316 /*
317 * The maximum amount of primary streams is 2 ^ (MaxPSA + 1)
318 * See table 26 of XHCI specification.
319 */
320 uint8_t max_psa_size = 1 << (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE) + 1);
321 if (count > max_psa_size) {
322 usb_log_error("Host controller only supports "
323 "%u primary streams.", max_psa_size);
324 return EINVAL;
325 }
326
327 if (count > xhci_ep->max_streams) {
328 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
329 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
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
341/**
342 * Cancels streams and reconfigures endpoint back to single ring no stream endpoint.
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 */
347errno_t xhci_endpoint_remove_streams(xhci_hc_t *hc, xhci_device_t *dev,
348 xhci_endpoint_t *xhci_ep)
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
355 hc_stop_endpoint(xhci_ep);
356 xhci_endpoint_free_transfer_ds(xhci_ep);
357
358 /* Streams are now removed, proceed with reconfiguring endpoint. */
359 errno_t err;
360 if ((err = xhci_trb_ring_init(&xhci_ep->ring, 0))) {
361 usb_log_error("Failed to initialize a transfer ring.");
362 return err;
363 }
364
365 return hc_update_endpoint(xhci_ep);
366}
367
368/**
369 * Initialize, setup and register primary streams.
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.
374 */
375errno_t xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
376 xhci_endpoint_t *xhci_ep, unsigned count)
377{
378 errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
379 if (err) {
380 return err;
381 }
382
383 /*
384 * We have passed the checks.
385 * Stop the endpoint, destroy the ring, and transition to streams.
386 */
387 hc_stop_endpoint(xhci_ep);
388 xhci_endpoint_free_transfer_ds(xhci_ep);
389
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));
396 err = initialize_primary_streams(hc, xhci_ep);
397 if (err) {
398 clear_primary_structures(xhci_ep);
399 return err;
400 }
401
402 xhci_ep_ctx_t ep_ctx;
403 /*
404 * Allowed values are 1-15, where 2 ^ pstreams is the actual amount of
405 * streams.
406 */
407 const size_t pstreams = fnzb32(count) - 1;
408 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
409
410 return hc_update_endpoint(xhci_ep);
411}
412
413/**
414 * Initialize, setup and register secondary streams.
415 * @param[in] hc Host controller of the endpoint.
416 * @param[in] dev Used device.
417 * @param[in] xhci_ep Associated XHCI bulk endpoint.
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.
421 * @param[in] count Amount of primary streams requested.
422 */
423errno_t xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
424 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
425{
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
432 errno_t err = verify_stream_conditions(hc, dev, xhci_ep, count);
433 if (err) {
434 return err;
435 }
436
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) {
455 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
456 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
457 return EINVAL;
458 }
459
460 /*
461 * We have passed all checks.
462 * Stop the endpoint, destroy the ring, and transition to streams.
463 */
464 hc_stop_endpoint(xhci_ep);
465 xhci_endpoint_free_transfer_ds(xhci_ep);
466
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));
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 }
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
485 return hc_update_endpoint(xhci_ep);
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;
493}
Note: See TracBrowser for help on using the repository browser.