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

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

xhci: move HC semantics from endpoint/device to hc module

  • Property mode set to 100644
File size: 14.4 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(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 return hc_update_endpoint(xhci_ep);
340}
341
342/** Initialize, setup and register primary streams.
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 * @param[in] count Amount of primary streams requested.
347 */
348int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
349 xhci_endpoint_t *xhci_ep, unsigned count)
350{
351 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
352 if (err) {
353 return err;
354 }
355
356 /*
357 * We have passed the checks.
358 * Stop the endpoint, destroy the ring, and transition to streams.
359 */
360 hc_stop_endpoint(xhci_ep);
361 xhci_endpoint_free_transfer_ds(xhci_ep);
362
363 err = initialize_primary_structures(xhci_ep, count);
364 if (err) {
365 return err;
366 }
367
368 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
369 err = initialize_primary_streams(hc, xhci_ep);
370 if (err) {
371 clear_primary_structures(xhci_ep);
372 return err;
373 }
374
375 xhci_ep_ctx_t ep_ctx;
376 /* Allowed values are 1-15, where 2 ^ pstreams is the actual amount of streams. */
377 const size_t pstreams = fnzb32(count) - 1;
378 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
379
380 return hc_update_endpoint(xhci_ep);
381}
382
383/** Initialize, setup and register secondary streams.
384 * @param[in] hc Host controller of the endpoint.
385 * @param[in] dev Used device.
386 * @param[in] xhci_ep Associated XHCI bulk endpoint.
387 * @param[in] sizes Amount of secondary streams in each primary stream.
388 This array should have exactly count elements.
389 If the size is 0, then a primary ring is created with that index.
390 * @param[in] count Amount of primary streams requested.
391 */
392int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
393 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
394{
395 /* Check if HC supports secondary indexing */
396 if (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_NSS)) {
397 usb_log_error("The host controller doesn't support secondary streams.");
398 return ENOTSUP;
399 }
400
401 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
402 if (err) {
403 return err;
404 }
405
406 if (count > 256) {
407 usb_log_error("The amount of primary streams cannot be higher than 256.");
408 return EINVAL;
409 }
410
411 /*
412 * Find the largest requested secondary stream size,
413 * that one is the maximum ID that device can receive.
414 * We need to make sure the device can handle that ID.
415 */
416 unsigned max = 0;
417 for (size_t index = 0; index < count; ++index) {
418 if (sizes[count] > max) {
419 max = sizes[count];
420 }
421 }
422
423 if (max * count > xhci_ep->max_streams) {
424 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
425 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
426 return EINVAL;
427 }
428
429 /*
430 * We have passed all checks.
431 * Stop the endpoint, destroy the ring, and transition to streams.
432 */
433 hc_stop_endpoint(xhci_ep);
434 xhci_endpoint_free_transfer_ds(xhci_ep);
435
436 err = initialize_primary_structures(xhci_ep, count);
437 if (err) {
438 return err;
439 }
440
441 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
442 size_t index;
443 for (index = 0; index < count; ++index) {
444 err = initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
445 if (err) {
446 goto err_init;
447 }
448 }
449
450 xhci_ep_ctx_t ep_ctx;
451 const size_t pstreams = fnzb32(count) - 1;
452 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
453
454 return hc_update_endpoint(xhci_ep);
455
456err_init:
457 for (size_t i = 0; i < index; ++i) {
458 clear_secondary_streams(xhci_ep, i);
459 }
460 clear_primary_structures(xhci_ep);
461 return err;
462}
Note: See TracBrowser for help on using the repository browser.