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

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

xhci: stub for streams support

  • Property mode set to 100644
File size: 10.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
41xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id)
42{
43 if (stream_id == 0 || stream_id >= 65534) {
44 return NULL;
45 }
46
47 /* See 4.12.2.1 for the calculation of the IDs and dividing the stream_id */
48 uint32_t primary_stream_id = (uint32_t) (stream_id & (ep->primary_stream_data_size - 1));
49 uint32_t secondary_stream_id = (uint32_t) ((stream_id / ep->primary_stream_data_size) & 0xFF);
50
51 if (primary_stream_id >= ep->primary_stream_data_size) {
52 return NULL;
53 }
54
55 xhci_stream_data_t *primary_data = &ep->primary_stream_data_array[primary_stream_id];
56 if (secondary_stream_id != 0 && !primary_data->secondary_size) {
57 return NULL;
58 }
59
60 if (!primary_data->secondary_size) {
61 return primary_data;
62 }
63
64 xhci_stream_data_t *secondary_data = primary_data->secondary_data;
65 if (secondary_stream_id >= primary_data->secondary_size) {
66 return NULL;
67 }
68
69 return &secondary_data[secondary_stream_id];
70}
71
72void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
73{
74 usb_log_debug2("Freeing stream rings and context arrays of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
75
76 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
77 xhci_stream_data_t *primary_data = xhci_ep->primary_stream_data_array + index;
78 if (primary_data->secondary_size > 0) {
79 for (size_t index2 = 0; index2 < primary_data->secondary_size; ++index2) {
80 xhci_stream_data_t *secondary_data = primary_data->secondary_data + index2;
81 xhci_trb_ring_fini(&secondary_data->ring);
82 }
83 dma_buffer_free(&primary_data->secondary_stream_ctx_dma);
84 }
85 else {
86 xhci_trb_ring_fini(&primary_data->ring);
87 }
88 }
89 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
90}
91
92/** Initialize secondary streams of XHCI bulk endpoint.
93 * @param[in] hc Host controller of the endpoint.
94 * @param[in] xhci_epi XHCI bulk endpoint to use.
95 * @param[in] index Index to primary stream array
96 */
97static int initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index) {
98 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
99 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
100 memset(data, 0, sizeof(xhci_stream_data_t));
101
102 int err = EOK;
103
104 /* Init and register TRB ring for every primary stream */
105 if ((err = xhci_trb_ring_init(&data->ring))) {
106 return err;
107 }
108 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
109
110 /* Set to linear stream array */
111 XHCI_STREAM_SCT_SET(*ctx, 1);
112
113 return EOK;
114}
115
116/** Initialize primary streams of XHCI bulk endpoint.
117 * @param[in] hc Host controller of the endpoint.
118 * @param[in] xhci_epi XHCI bulk endpoint to use.
119 */
120static int initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
121{
122 int err = EOK;
123 for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
124 err = initialize_primary_stream(hc, xhci_ep, index);
125 if (err) {
126 return err;
127 }
128 }
129
130 // TODO: deinitialize if we got stuck in the middle
131
132 return EOK;
133}
134
135/** Initialize secondary streams of XHCI bulk endpoint.
136 * @param[in] hc Host controller of the endpoint.
137 * @param[in] xhci_epi XHCI bulk endpoint to use.
138 * @param[in] index Index to primary stream array
139 * @param[in] count Number of secondary streams to initialize.
140 */
141static int initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index, unsigned count)
142{
143 if (count == 0) {
144 return initialize_primary_stream(hc, xhci_ep, index);
145 }
146
147 if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
148 usb_log_error("The secondary stream array size must be a power of 2 between 8 and 256.");
149 return EINVAL;
150 }
151
152 xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
153 xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
154 memset(data, 0, sizeof(xhci_stream_data_t));
155
156 data->secondary_size = count;
157 data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
158 if (!data->secondary_size) {
159 return ENOMEM;
160 }
161
162 if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
163 return ENOMEM;
164 }
165 data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
166
167 XHCI_STREAM_DEQ_PTR_SET(*ctx, data->secondary_stream_ctx_dma.phys);
168 XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
169
170 int err = EOK;
171
172 for (size_t i = 0; i < count; ++i) {
173 xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[i];
174 xhci_stream_data_t *secondary_data = &data->secondary_data[i];
175 /* Init and register TRB ring for every secondary stream */
176 if ((err = xhci_trb_ring_init(&secondary_data->ring))) {
177 return err;
178 }
179
180 XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
181
182 /* Set to linear stream array */
183 XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
184 }
185
186 // TODO: deinitialize if we got stuck in the middle
187
188 return EOK;
189}
190
191/** Configure XHCI bulk endpoint's stream context.
192 * @param[in] xhci_ep Associated XHCI bulk endpoint.
193 * @param[in] ctx Endpoint context to configure.
194 * @param[in] pstreams The value of MaxPStreams.
195 */
196static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams, unsigned lsa)
197{
198 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
199 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
200 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
201 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
202
203 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
204 XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
205 // TODO: set HID?
206 XHCI_EP_LSA_SET(*ctx, lsa);
207}
208
209static int verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
210 xhci_endpoint_t *xhci_ep, unsigned count)
211{
212 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
213 || dev->base.speed != USB_SPEED_SUPER) {
214 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
215 return EINVAL;
216 }
217
218 if (xhci_ep->max_streams == 1) {
219 usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
220 return EINVAL;
221 }
222
223 uint8_t max_psa_size = 2 << XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE);
224 if (count > max_psa_size) {
225 usb_log_error("Host controller only supports %u primary streams.", max_psa_size);
226 return EINVAL;
227 }
228
229 if (count > xhci_ep->max_streams) {
230 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
231 XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
232 return EINVAL;
233 }
234
235 if ((count & (count - 1)) != 0) {
236 usb_log_error("The amount of primary streams must be a power of 2.");
237 return EINVAL;
238 }
239
240 return EOK;
241}
242
243static int initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
244{
245 usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
246 count, XHCI_EP_ARGS(*xhci_ep));
247
248 if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
249 return ENOMEM;
250 }
251
252 xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
253 xhci_ep->primary_stream_data_array = calloc(count, sizeof(xhci_stream_data_t));
254 if (!xhci_ep->primary_stream_data_array) {
255 dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
256 return ENOMEM;
257 }
258
259 xhci_ep->primary_stream_data_size = count;
260
261 return EOK;
262}
263
264/** Initialize primary streams
265 */
266int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
267 xhci_endpoint_t *xhci_ep, unsigned count)
268{
269 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
270 if (err) {
271 return err;
272 }
273
274 err = initialize_primary_structures(xhci_ep, count);
275 if (err) {
276 return err;
277 }
278
279 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
280 initialize_primary_streams(hc, xhci_ep);
281
282 xhci_ep_ctx_t ep_ctx;
283 const size_t pstreams = fnzb32(count) - 1;
284 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
285
286 // FIXME: do we add endpoint? do we need to destroy previous configuration?
287 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
288}
289
290/** Initialize secondary streams
291 * sizes - the size of each secondary stream context (an array)
292 * count - the amount of primary stream contexts
293 */
294int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
295 xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
296{
297 int err = verify_stream_conditions(hc, dev, xhci_ep, count);
298 if (err) {
299 return err;
300 }
301
302 // TODO: determine if count * secondary streams <= max_streams
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 err = initialize_primary_structures(xhci_ep, count);
310 if (err) {
311 return err;
312 }
313
314 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
315 for (size_t index = 0; index < count; ++index) {
316 initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
317 }
318
319 xhci_ep_ctx_t ep_ctx;
320 const size_t pstreams = fnzb32(count) - 1;
321 setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
322
323 // FIXME: do we add endpoint? do we need to destroy previous configuration?
324 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
325}
Note: See TracBrowser for help on using the repository browser.