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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fb28cde was 1af4c00, checked in by Salmelu <salmelu@…>, 8 years ago

xhci: fixed transition to and from streams

Added remove streams function to transition the endpoint back to single ring no streams mode.
Requesting streams now stops the endpoint, clears the ring and executes update endpoint command.

  • Property mode set to 100644
File size: 14.8 KB
Line 
1/*
2 * Copyright (c) 2017 Michal Staruch
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/** Finds stream data with given stream ID if it exists.
42 * Note that streams with ID 0, 65534 and 65535 are reserved.
43 * Splits the ID into primary and secondary context ID and searches the structures.
44 * @param[in] ep Affected endpoint.
45 * @param[in] stream_id Id of the stream.
46 */
47xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id)
48{
49 if (stream_id == 0 || stream_id >= 65534) {
50 return NULL;
51 }
52
53 /* See 4.12.2.1 for the calculation of the IDs and dividing the stream_id */
54 uint32_t primary_stream_id = (uint32_t) (stream_id & (ep->primary_stream_data_size - 1));
55 uint32_t secondary_stream_id = (uint32_t) ((stream_id / ep->primary_stream_data_size) & 0xFF);
56
57 if (primary_stream_id >= ep->primary_stream_data_size) {
58 return NULL;
59 }
60
61 xhci_stream_data_t *primary_data = &ep->primary_stream_data_array[primary_stream_id];
62 if (secondary_stream_id != 0 && !primary_data->secondary_size) {
63 return NULL;
64 }
65
66 if (!primary_data->secondary_size) {
67 return primary_data;
68 }
69
70 xhci_stream_data_t *secondary_data = primary_data->secondary_data;
71 if (secondary_stream_id >= primary_data->secondary_size) {
72 return NULL;
73 }
74
75 return &secondary_data[secondary_stream_id];
76}
77
78/** Initializes primary stream data structures in endpoint.
79 * @param[in] xhci_ep Used XHCI bulk endpoint.
80 * @param[in] count Amount of primary streams.
81 */
82static int initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
83{
84 usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
85 count, XHCI_EP_ARGS(*xhci_ep));
86
87 if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
88 return ENOMEM;
89 }
90
91 xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
92 xhci_ep->primary_stream_data_array = calloc(count, sizeof(xhci_stream_data_t));
93 if (!xhci_ep->primary_stream_data_array) {
94 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
95 return ENOMEM;
96 }
97
98 xhci_ep->primary_stream_data_size = count;
99
100 return EOK;
101}
102
103/**
104 *
105 */
106static void clear_primary_structures(xhci_endpoint_t *xhci_ep)
107{
108 usb_log_debug2("Deallocating primary stream structures for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
109
110 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
111 free(xhci_ep->primary_stream_data_array);
112
113 xhci_ep->primary_stream_data_array = NULL;
114 xhci_ep->primary_stream_data_size = 0;
115}
116
117static void clear_secondary_streams(xhci_endpoint_t *xhci_ep, unsigned index)
118{
119 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
120 if (!data->secondary_size) {
121 xhci_trb_ring_fini(&data->ring);
122 return;
123 }
124
125 for (size_t i = 0; i < data->secondary_size; ++i) {
126 xhci_trb_ring_fini(&data->secondary_data[i].ring);
127 }
128
129 dma_buffer_free(&data->secondary_stream_ctx_dma);
130 free(data->secondary_data);
131}
132
133void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
134{
135 usb_log_debug2("Freeing stream rings and context arrays of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
136
137 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
138 clear_secondary_streams(xhci_ep, index);
139 }
140 clear_primary_structures(xhci_ep);
141}
142
143/** Initialize a single primary stream structure with given index.
144 * @param[in] hc Host controller of the endpoint.
145 * @param[in] xhci_ep XHCI bulk endpoint to use.
146 * @param[in] index index of the initialized stream structure.
147 */
148static int initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index) {
149 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
150 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
151 memset(data, 0, sizeof(xhci_stream_data_t));
152
153 int err = EOK;
154
155 /* Init and register TRB ring for the primary stream */
156 if ((err = xhci_trb_ring_init(&data->ring))) {
157 return err;
158 }
159 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
160
161 /* Set to linear stream array */
162 XHCI_STREAM_SCT_SET(*ctx, 1);
163
164 return EOK;
165}
166
167/** Initialize primary streams of XHCI bulk endpoint.
168 * @param[in] hc Host controller of the endpoint.
169 * @param[in] xhci_ep XHCI bulk endpoint to use.
170 */
171static int initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
172{
173 int err = EOK;
174 size_t index;
175 for (index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
176 err = initialize_primary_stream(hc, xhci_ep, index);
177 if (err) {
178 goto err_clean;
179 }
180 }
181
182 return EOK;
183
184err_clean:
185 for (size_t i = 0; i < index; ++i) {
186 xhci_trb_ring_fini(&xhci_ep->primary_stream_data_array[i].ring);
187 }
188 return err;
189}
190
191/** Initialize secondary streams of XHCI bulk endpoint.
192 * @param[in] hc Host controller of the endpoint.
193 * @param[in] xhci_epi XHCI bulk endpoint to use.
194 * @param[in] idx Index to primary stream array
195 * @param[in] count Number of secondary streams to initialize.
196 */
197static int initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned idx, unsigned count)
198{
199 if (count == 0) {
200 /* The primary stream context can still point to a single ring, not a secondary. */
201 return initialize_primary_stream(hc, xhci_ep, idx);
202 }
203
204 if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
205 usb_log_error("The secondary stream array size must be a power of 2 between 8 and 256.");
206 return EINVAL;
207 }
208
209 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[idx];
210 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[idx];
211 memset(data, 0, sizeof(xhci_stream_data_t));
212
213 data->secondary_size = count;
214 data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
215 if (!data->secondary_size) {
216 return ENOMEM;
217 }
218
219 if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
220 free(data->secondary_data);
221 return ENOMEM;
222 }
223 data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
224
225 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->secondary_stream_ctx_dma.phys);
226 XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
227
228 /* Initialize all the rings. */
229 int err = EOK;
230 size_t index;
231 for (index = 0; index < count; ++index) {
232 xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[index];
233 xhci_stream_data_t *secondary_data = &data->secondary_data[index];
234 /* Init and register TRB ring for every secondary stream */
235 if ((err = xhci_trb_ring_init(&secondary_data->ring))) {
236 goto err_init;
237 }
238
239 XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
240 /* Set to secondary stream array */
241 XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
242 }
243
244 return EOK;
245
246err_init:
247 for (size_t i = 0; i < index; ++i) {
248 xhci_trb_ring_fini(&data->secondary_data[i].ring);
249 }
250 return err;
251}
252
253/** Configure XHCI bulk endpoint's stream context.
254 * @param[in] xhci_ep Associated XHCI bulk endpoint.
255 * @param[in] ctx Endpoint context to configure.
256 * @param[in] pstreams The value of MaxPStreams.
257 * @param[in] lsa Specifies if the stream IDs point to primary stream array.
258 */
259static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams, unsigned lsa)
260{
261 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
262 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
263 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
264 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
265
266 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
267 XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
268 XHCI_EP_LSA_SET(*ctx, lsa);
269}
270
271/** Verifies if all the common preconditions are satisfied.
272 * @param[in] hc Host controller of the endpoint.
273 * @param[in] dev Used device.
274 * @param[in] xhci_ep Associated XHCI bulk endpoint.
275 * @param[in] count Amount of primary streams requested.
276 */
277static int verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
278 xhci_endpoint_t *xhci_ep, unsigned count)
279{
280 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
281 || dev->base.speed != USB_SPEED_SUPER) {
282 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
283 return EINVAL;
284 }
285
286 if (xhci_ep->max_streams <= 1) {
287 usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
288 return EINVAL;
289 }
290
291 if (count < 2) {
292 usb_log_error("The minumum amount of primary streams is 2.");
293 return EINVAL;
294 }
295
296 /* The maximum amount of primary streams is 2 ^ (MaxPSA + 1) (See table 26 of XHCI specification) */
297 uint8_t max_psa_size = 1 << (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE) + 1);
298 if (count > max_psa_size) {
299 usb_log_error("Host controller only supports %u primary streams.", max_psa_size);
300 return EINVAL;
301 }
302
303 if (count > xhci_ep->max_streams) {
304 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
305 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
306 return EINVAL;
307 }
308
309 if ((count & (count - 1)) != 0) {
310 usb_log_error("The amount of primary streams must be a power of 2.");
311 return EINVAL;
312 }
313
314 return EOK;
315}
316
317/** Cancels streams and reconfigures endpoint back to single ring no stream endpoint.
318 * @param[in] hc Host controller of the endpoint.
319 * @param[in] dev Used device.
320 * @param[in] xhci_ep Associated XHCI bulk endpoint.
321 */
322int xhci_endpoint_remove_streams(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *xhci_ep)
323{
324 if (!xhci_ep->primary_stream_data_size) {
325 usb_log_warning("There are no streams enabled on the endpoint, doing nothing.");
326 return EOK;
327 }
328
329 hc_stop_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep));
330 xhci_endpoint_free_transfer_ds(xhci_ep);
331
332 /* Streams are now removed, proceed with reconfiguring endpoint. */
333 int err;
334 if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
335 usb_log_error("Failed to initialize a transfer ring.");
336 return err;
337 }
338
339 xhci_ep_ctx_t ep_ctx;
340 memset(&ep_ctx, 0, sizeof(ep_ctx));
341 xhci_setup_endpoint_context(xhci_ep, &ep_ctx);
342 return hc_update_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
343}
344
345/** Initialize, setup and register primary streams.
346 * @param[in] hc Host controller of the endpoint.
347 * @param[in] dev Used device.
348 * @param[in] xhci_ep Associated XHCI bulk endpoint.
349 * @param[in] count Amount of primary streams requested.
350 */
351int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
352 xhci_endpoint_t *xhci_ep, unsigned count)
353{
354 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
355 if (err) {
356 return err;
357 }
358
359 /*
360 * We have passed the checks.
361 * Stop the endpoint, destroy the ring, and transition to streams.
362 */
363 hc_stop_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep));
364 xhci_endpoint_free_transfer_ds(xhci_ep);
365
366 err = initialize_primary_structures(xhci_ep, count);
367 if (err) {
368 return err;
369 }
370
371 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
372 err = initialize_primary_streams(hc, xhci_ep);
373 if (err) {
374 clear_primary_structures(xhci_ep);
375 return err;
376 }
377
378 xhci_ep_ctx_t ep_ctx;
379 /* Allowed values are 1-15, where 2 ^ pstreams is the actual amount of streams. */
380 const size_t pstreams = fnzb32(count) - 1;
381 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
382
383 return hc_update_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
384}
385
386/** Initialize, setup and register secondary streams.
387 * @param[in] hc Host controller of the endpoint.
388 * @param[in] dev Used device.
389 * @param[in] xhci_ep Associated XHCI bulk endpoint.
390 * @param[in] sizes Amount of secondary streams in each primary stream.
391 This array should have exactly count elements.
392 If the size is 0, then a primary ring is created with that index.
393 * @param[in] count Amount of primary streams requested.
394 */
395int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
396 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
397{
398 /* Check if HC supports secondary indexing */
399 if (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_NSS)) {
400 usb_log_error("The host controller doesn't support secondary streams.");
401 return ENOTSUP;
402 }
403
404 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
405 if (err) {
406 return err;
407 }
408
409 if (count > 256) {
410 usb_log_error("The amount of primary streams cannot be higher than 256.");
411 return EINVAL;
412 }
413
414 /*
415 * Find the largest requested secondary stream size,
416 * that one is the maximum ID that device can receive.
417 * We need to make sure the device can handle that ID.
418 */
419 unsigned max = 0;
420 for (size_t index = 0; index < count; ++index) {
421 if (sizes[count] > max) {
422 max = sizes[count];
423 }
424 }
425
426 if (max * count > xhci_ep->max_streams) {
427 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
428 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
429 return EINVAL;
430 }
431
432 /*
433 * We have passed all checks.
434 * Stop the endpoint, destroy the ring, and transition to streams.
435 */
436 hc_stop_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep));
437 xhci_endpoint_free_transfer_ds(xhci_ep);
438
439 err = initialize_primary_structures(xhci_ep, count);
440 if (err) {
441 return err;
442 }
443
444 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
445 size_t index;
446 for (index = 0; index < count; ++index) {
447 err = initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
448 if (err) {
449 goto err_init;
450 }
451 }
452
453 xhci_ep_ctx_t ep_ctx;
454 const size_t pstreams = fnzb32(count) - 1;
455 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
456
457 return hc_update_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
458
459err_init:
460 for (size_t i = 0; i < index; ++i) {
461 clear_secondary_streams(xhci_ep, i);
462 }
463 clear_primary_structures(xhci_ep);
464 return err;
465}
Note: See TracBrowser for help on using the repository browser.