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

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

xhci: streams: documentation comments

  • Property mode set to 100644
File size: 13.5 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
114static void clear_secondary_streams(xhci_endpoint_t *xhci_ep, unsigned index)
115{
116 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
117 if (!data->secondary_size) {
118 xhci_trb_ring_fini(&data->ring);
119 return;
120 }
121
122 for (size_t i = 0; i < data->secondary_size; ++i) {
123 xhci_trb_ring_fini(&data->secondary_data[i].ring);
124 }
125
126 dma_buffer_free(&data->secondary_stream_ctx_dma);
127 free(data->secondary_data);
128}
129
130void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
131{
132 usb_log_debug2("Freeing stream rings and context arrays of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
133
134 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
135 clear_secondary_streams(xhci_ep, index);
136 }
137 clear_primary_structures(xhci_ep);
138}
139
140/** Initialize a single primary stream structure with given index.
141 * @param[in] hc Host controller of the endpoint.
142 * @param[in] xhci_ep XHCI bulk endpoint to use.
143 * @param[in] index index of the initialized stream structure.
144 */
145static int initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index) {
146 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
147 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
148 memset(data, 0, sizeof(xhci_stream_data_t));
149
150 int err = EOK;
151
152 /* Init and register TRB ring for the primary stream */
153 if ((err = xhci_trb_ring_init(&data->ring))) {
154 return err;
155 }
156 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
157
158 /* Set to linear stream array */
159 XHCI_STREAM_SCT_SET(*ctx, 1);
160
161 return EOK;
162}
163
164/** Initialize primary streams of XHCI bulk endpoint.
165 * @param[in] hc Host controller of the endpoint.
166 * @param[in] xhci_ep XHCI bulk endpoint to use.
167 */
168static int initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
169{
170 int err = EOK;
171 size_t index;
172 for (index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
173 err = initialize_primary_stream(hc, xhci_ep, index);
174 if (err) {
175 goto err_clean;
176 }
177 }
178
179 return EOK;
180
181err_clean:
182 for (size_t i = 0; i < index; ++i) {
183 xhci_trb_ring_fini(&xhci_ep->primary_stream_data_array[i].ring);
184 }
185 return err;
186}
187
188/** Initialize secondary streams of XHCI bulk endpoint.
189 * @param[in] hc Host controller of the endpoint.
190 * @param[in] xhci_epi XHCI bulk endpoint to use.
191 * @param[in] idx Index to primary stream array
192 * @param[in] count Number of secondary streams to initialize.
193 */
194static int initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned idx, unsigned count)
195{
196 if (count == 0) {
197 /* The primary stream context can still point to a single ring, not a secondary. */
198 return initialize_primary_stream(hc, xhci_ep, idx);
199 }
200
201 if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
202 usb_log_error("The secondary stream array size must be a power of 2 between 8 and 256.");
203 return EINVAL;
204 }
205
206 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[idx];
207 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[idx];
208 memset(data, 0, sizeof(xhci_stream_data_t));
209
210 data->secondary_size = count;
211 data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
212 if (!data->secondary_size) {
213 return ENOMEM;
214 }
215
216 if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
217 free(data->secondary_data);
218 return ENOMEM;
219 }
220 data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
221
222 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->secondary_stream_ctx_dma.phys);
223 XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
224
225 /* Initialize all the rings. */
226 int err = EOK;
227 size_t index;
228 for (index = 0; index < count; ++index) {
229 xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[index];
230 xhci_stream_data_t *secondary_data = &data->secondary_data[index];
231 /* Init and register TRB ring for every secondary stream */
232 if ((err = xhci_trb_ring_init(&secondary_data->ring))) {
233 goto err_init;
234 }
235
236 XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
237 /* Set to secondary stream array */
238 XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
239 }
240
241 return EOK;
242
243err_init:
244 for (size_t i = 0; i < index; ++i) {
245 xhci_trb_ring_fini(&data->secondary_data[i].ring);
246 }
247 return err;
248}
249
250/** Configure XHCI bulk endpoint's stream context.
251 * @param[in] xhci_ep Associated XHCI bulk endpoint.
252 * @param[in] ctx Endpoint context to configure.
253 * @param[in] pstreams The value of MaxPStreams.
254 * @param[in] lsa Specifies if the stream IDs point to primary stream array.
255 */
256static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams, unsigned lsa)
257{
258 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
259 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
260 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
261 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
262
263 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
264 XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
265 XHCI_EP_LSA_SET(*ctx, lsa);
266}
267
268/** Verifies if all the common preconditions are satisfied.
269 * @param[in] hc Host controller of the endpoint.
270 * @param[in] dev Used device.
271 * @param[in] xhci_ep Associated XHCI bulk endpoint.
272 * @param[in] count Amount of primary streams requested.
273 */
274static int verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
275 xhci_endpoint_t *xhci_ep, unsigned count)
276{
277 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
278 || dev->base.speed != USB_SPEED_SUPER) {
279 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
280 return EINVAL;
281 }
282
283 if (xhci_ep->max_streams <= 1) {
284 usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
285 return EINVAL;
286 }
287
288 if (count < 2) {
289 usb_log_error("The minumum amount of primary streams is 2.");
290 return EINVAL;
291 }
292
293 /* The maximum amount of primary streams is 2 ^ (MaxPSA + 1) (See table 26 of XHCI specification) */
294 uint8_t max_psa_size = 1 << (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE) + 1);
295 if (count > max_psa_size) {
296 usb_log_error("Host controller only supports %u primary streams.", max_psa_size);
297 return EINVAL;
298 }
299
300 if (count > xhci_ep->max_streams) {
301 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
302 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
303 return EINVAL;
304 }
305
306 if ((count & (count - 1)) != 0) {
307 usb_log_error("The amount of primary streams must be a power of 2.");
308 return EINVAL;
309 }
310
311 return EOK;
312}
313
314/** Initialize, setup and register primary streams.
315 * @param[in] hc Host controller of the endpoint.
316 * @param[in] dev Used device.
317 * @param[in] xhci_ep Associated XHCI bulk endpoint.
318 * @param[in] count Amount of primary streams requested.
319 */
320int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
321 xhci_endpoint_t *xhci_ep, unsigned count)
322{
323 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
324 if (err) {
325 return err;
326 }
327
328 err = initialize_primary_structures(xhci_ep, count);
329 if (err) {
330 return err;
331 }
332
333 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
334 err = initialize_primary_streams(hc, xhci_ep);
335 if (err) {
336 clear_primary_structures(xhci_ep);
337 return err;
338 }
339
340 xhci_ep_ctx_t ep_ctx;
341 /* Allowed values are 1-15, where 2 ^ pstreams is the actual amount of streams. */
342 const size_t pstreams = fnzb32(count) - 1;
343 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
344
345 // FIXME: do we add endpoint? do we need to destroy previous configuration?
346 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
347}
348
349/** Initialize, setup and register secondary streams.
350 * @param[in] hc Host controller of the endpoint.
351 * @param[in] dev Used device.
352 * @param[in] xhci_ep Associated XHCI bulk endpoint.
353 * @param[in] sizes Amount of secondary streams in each primary stream.
354 This array should have exactly count elements.
355 If the size is 0, then a primary ring is created with that index.
356 * @param[in] count Amount of primary streams requested.
357 */
358int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
359 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
360{
361 /* Check if HC supports secondary indexing */
362 if (XHCI_REG_RD(hc->cap_regs, XHCI_CAP_NSS)) {
363 usb_log_error("The host controller doesn't support secondary streams.");
364 return ENOTSUP;
365 }
366
367 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
368 if (err) {
369 return err;
370 }
371
372 if (count > 256) {
373 usb_log_error("The amount of primary streams cannot be higher than 256.");
374 return EINVAL;
375 }
376
377 /*
378 * Find the largest requested secondary stream size,
379 * that one is the maximum ID that device can receive.
380 * We need to make sure the device can handle that ID.
381 */
382 unsigned max = 0;
383 for (size_t index = 0; index < count; ++index) {
384 if (sizes[count] > max) {
385 max = sizes[count];
386 }
387 }
388
389 if (max * count > xhci_ep->max_streams) {
390 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
391 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
392 return EINVAL;
393 }
394
395 err = initialize_primary_structures(xhci_ep, count);
396 if (err) {
397 return err;
398 }
399
400 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
401 size_t index;
402 for (index = 0; index < count; ++index) {
403 err = initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
404 if (err) {
405 goto err_init;
406 }
407 }
408
409 xhci_ep_ctx_t ep_ctx;
410 const size_t pstreams = fnzb32(count) - 1;
411 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
412
413 // FIXME: do we add endpoint? do we need to destroy previous configuration?
414 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
415
416err_init:
417 for (size_t i = 0; i < index; ++i) {
418 clear_secondary_streams(xhci_ep, i);
419 }
420 clear_primary_structures(xhci_ep);
421 return err;
422}
Note: See TracBrowser for help on using the repository browser.