source: mainline/uspace/drv/bus/usb/ohci/ohci_batch.c@ 18b6a88

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 18b6a88 was 3bacee1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Make ccheck-fix again and commit more good files.

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup drvusbohci
31 * @{
32 */
33/** @file
34 * @brief OHCI driver USB transaction structure
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <macros.h>
40#include <mem.h>
41#include <stdbool.h>
42
43#include <usb/usb.h>
44#include <usb/debug.h>
45#include <usb/host/utils/malloc32.h>
46
47#include "ohci_batch.h"
48#include "ohci_bus.h"
49
50static void (*const batch_setup[])(ohci_transfer_batch_t *);
51
52/** Safely destructs ohci_transfer_batch_t structure
53 *
54 * @param[in] ohci_batch Instance to destroy.
55 */
56void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch)
57{
58 assert(ohci_batch);
59 dma_buffer_free(&ohci_batch->ohci_dma_buffer);
60 free(ohci_batch);
61}
62
63/** Allocate memory and initialize internal data structure.
64 *
65 * @param[in] ep Endpoint for which the batch will be created
66 * @return Valid pointer if all structures were successfully created,
67 * NULL otherwise.
68 */
69ohci_transfer_batch_t *ohci_transfer_batch_create(endpoint_t *ep)
70{
71 assert(ep);
72
73 ohci_transfer_batch_t *ohci_batch =
74 calloc(1, sizeof(ohci_transfer_batch_t));
75 if (!ohci_batch) {
76 usb_log_error("Failed to allocate OHCI batch data.");
77 return NULL;
78 }
79
80 usb_transfer_batch_init(&ohci_batch->base, ep);
81
82 return ohci_batch;
83}
84
85/** Prepares a batch to be sent.
86 *
87 * Determines the number of needed transfer descriptors (TDs).
88 * Prepares a transport buffer (that is accessible by the hardware).
89 * Initializes parameters needed for the transfer and callback.
90 */
91int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch)
92{
93 assert(ohci_batch);
94 usb_transfer_batch_t *usb_batch = &ohci_batch->base;
95
96 if (!batch_setup[usb_batch->ep->transfer_type])
97 return ENOTSUP;
98
99 ohci_batch->td_count = (usb_batch->size + OHCI_TD_MAX_TRANSFER - 1) /
100 OHCI_TD_MAX_TRANSFER;
101 /* Control transfer need Setup and Status stage */
102 if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
103 ohci_batch->td_count += 2;
104 }
105
106 /* Alloc one more to NULL terminate */
107 ohci_batch->tds = calloc(ohci_batch->td_count + 1, sizeof(td_t *));
108 if (!ohci_batch->tds)
109 return ENOMEM;
110
111 const size_t td_size = ohci_batch->td_count * sizeof(td_t);
112 const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) ?
113 USB_SETUP_PACKET_SIZE :
114 0;
115
116 if (dma_buffer_alloc(&ohci_batch->ohci_dma_buffer, td_size + setup_size)) {
117 usb_log_error("Failed to allocate OHCI DMA buffer.");
118 return ENOMEM;
119 }
120
121 td_t *tds = ohci_batch->ohci_dma_buffer.virt;
122
123 for (size_t i = 0; i < ohci_batch->td_count; i++)
124 ohci_batch->tds[i] = &tds[i];
125
126 /* Presence of this terminator makes TD initialization easier */
127 ohci_batch->tds[ohci_batch->td_count] = NULL;
128
129 ohci_batch->setup_buffer = (void *) (&tds[ohci_batch->td_count]);
130 memcpy(ohci_batch->setup_buffer, usb_batch->setup.buffer, setup_size);
131
132 ohci_batch->data_buffer = usb_batch->dma_buffer.virt;
133
134 batch_setup[usb_batch->ep->transfer_type](ohci_batch);
135
136 return EOK;
137}
138
139/** Check batch TDs' status.
140 *
141 * @param[in] ohci_batch Batch structure to use.
142 * @return False, if there is an active TD, true otherwise.
143 *
144 * Walk all TDs (usually there is just one). Stop with false if there is an
145 * active TD. Stop with true if an error is found. Return true if the walk
146 * completes with the last TD.
147 */
148bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *ohci_batch)
149{
150 assert(ohci_batch);
151
152 usb_transfer_batch_t *usb_batch = &ohci_batch->base;
153 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(usb_batch->ep);
154 assert(ohci_ep);
155
156 usb_log_debug("Batch %p checking %zu td(s) for completion.",
157 ohci_batch, ohci_batch->td_count);
158 usb_log_debug2("ED: %08x:%08x:%08x:%08x.",
159 ohci_ep->ed->status, ohci_ep->ed->td_head,
160 ohci_ep->ed->td_tail, ohci_ep->ed->next);
161
162 if (!ed_inactive(ohci_ep->ed) && ed_transfer_pending(ohci_ep->ed))
163 return false;
164
165 /* Now we may be sure that either the ED is inactive because of errors
166 * or all transfer descriptors completed successfully */
167
168 /* Assume all data got through */
169 usb_batch->transferred_size = usb_batch->size;
170
171 /* Check all TDs */
172 for (size_t i = 0; i < ohci_batch->td_count; ++i) {
173 assert(ohci_batch->tds[i] != NULL);
174 usb_log_debug("TD %zu: %08x:%08x:%08x:%08x.", i,
175 ohci_batch->tds[i]->status, ohci_batch->tds[i]->cbp,
176 ohci_batch->tds[i]->next, ohci_batch->tds[i]->be);
177
178 usb_batch->error = td_error(ohci_batch->tds[i]);
179 if (usb_batch->error == EOK) {
180 /* If the TD got all its data through, it will report
181 * 0 bytes remain, the sole exception is INPUT with
182 * data rounding flag (short), i.e. every INPUT.
183 * Nice thing is that short packets will correctly
184 * report remaining data, thus making this computation
185 * correct (short packets need to be produced by the
186 * last TD)
187 * NOTE: This also works for CONTROL transfer as
188 * the first TD will return 0 remain.
189 * NOTE: Short packets don't break the assumption that
190 * we leave the very last(unused) TD behind.
191 */
192 usb_batch->transferred_size -=
193 td_remain_size(ohci_batch->tds[i]);
194 } else {
195 usb_log_debug("Batch %p found error TD(%zu):%08x.",
196 ohci_batch, i, ohci_batch->tds[i]->status);
197
198 /* ED should be stopped because of errors */
199 assert((ohci_ep->ed->td_head & ED_TDHEAD_HALTED_FLAG) != 0);
200
201 /* We don't care where the processing stopped, we just
202 * need to make sure it's not using any of the TDs owned
203 * by the transfer.
204 *
205 * As the chain is terminated by a TD in ownership of
206 * the EP, set it.
207 */
208 ed_set_head_td(ohci_ep->ed, ohci_ep->tds[0]);
209
210 /* Clear the halted condition for the next transfer */
211 ed_clear_halt(ohci_ep->ed);
212 break;
213 }
214 }
215 assert(usb_batch->transferred_size <= usb_batch->size);
216
217 /* Make sure that we are leaving the right TD behind */
218 assert(addr_to_phys(ohci_ep->tds[0]) == ed_tail_td(ohci_ep->ed));
219 assert(ed_tail_td(ohci_ep->ed) == ed_head_td(ohci_ep->ed));
220
221 return true;
222}
223
224/** Starts execution of the TD list
225 *
226 * @param[in] ohci_batch Batch structure to use
227 */
228void ohci_transfer_batch_commit(const ohci_transfer_batch_t *ohci_batch)
229{
230 assert(ohci_batch);
231
232 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
233
234 usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_ep->ed,
235 ohci_ep->ed->status, ohci_ep->ed->td_tail,
236 ohci_ep->ed->td_head, ohci_ep->ed->next);
237
238 /*
239 * According to spec, we need to copy the first TD to the currently
240 * enqueued one.
241 */
242 memcpy(ohci_ep->tds[0], ohci_batch->tds[0], sizeof(td_t));
243 ohci_batch->tds[0] = ohci_ep->tds[0];
244
245 td_t *last = ohci_batch->tds[ohci_batch->td_count - 1];
246 td_set_next(last, ohci_ep->tds[1]);
247
248 ed_set_tail_td(ohci_ep->ed, ohci_ep->tds[1]);
249
250 /* Swap the EP TDs for the next transfer */
251 td_t *tmp = ohci_ep->tds[0];
252 ohci_ep->tds[0] = ohci_ep->tds[1];
253 ohci_ep->tds[1] = tmp;
254}
255
256/** Prepare generic control transfer
257 *
258 * @param[in] ohci_batch Batch structure to use.
259 * @param[in] dir Communication direction
260 *
261 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
262 * Data stage with alternating toggle and direction supplied by parameter.
263 * Status stage with toggle 1 and direction supplied by parameter.
264 */
265static void batch_control(ohci_transfer_batch_t *ohci_batch)
266{
267 assert(ohci_batch);
268
269 usb_direction_t dir = ohci_batch->base.dir;
270 assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
271
272 static const usb_direction_t reverse_dir[] = {
273 [USB_DIRECTION_IN] = USB_DIRECTION_OUT,
274 [USB_DIRECTION_OUT] = USB_DIRECTION_IN,
275 };
276
277 int toggle = 0;
278 const usb_direction_t data_dir = dir;
279 const usb_direction_t status_dir = reverse_dir[dir];
280
281 /* Setup stage */
282 td_init(
283 ohci_batch->tds[0], ohci_batch->tds[1], USB_DIRECTION_BOTH,
284 ohci_batch->setup_buffer, USB_SETUP_PACKET_SIZE, toggle);
285 usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.",
286 ohci_batch->tds[0]->status, ohci_batch->tds[0]->cbp,
287 ohci_batch->tds[0]->next, ohci_batch->tds[0]->be);
288
289 /* Data stage */
290 size_t td_current = 1;
291 const char *buffer = ohci_batch->data_buffer;
292 size_t remain_size = ohci_batch->base.size;
293 while (remain_size > 0) {
294 const size_t transfer_size =
295 min(remain_size, OHCI_TD_MAX_TRANSFER);
296 toggle = 1 - toggle;
297
298 td_init(ohci_batch->tds[td_current],
299 ohci_batch->tds[td_current + 1],
300 data_dir, buffer, transfer_size, toggle);
301 usb_log_debug("Created CONTROL DATA TD: %08x:%08x:%08x:%08x.",
302 ohci_batch->tds[td_current]->status,
303 ohci_batch->tds[td_current]->cbp,
304 ohci_batch->tds[td_current]->next,
305 ohci_batch->tds[td_current]->be);
306
307 buffer += transfer_size;
308 remain_size -= transfer_size;
309 assert(td_current < ohci_batch->td_count - 1);
310 ++td_current;
311 }
312
313 /* Status stage */
314 assert(td_current == ohci_batch->td_count - 1);
315 td_init(ohci_batch->tds[td_current], ohci_batch->tds[td_current + 1],
316 status_dir, NULL, 0, 1);
317 usb_log_debug("Created CONTROL STATUS TD: %08x:%08x:%08x:%08x.",
318 ohci_batch->tds[td_current]->status,
319 ohci_batch->tds[td_current]->cbp,
320 ohci_batch->tds[td_current]->next,
321 ohci_batch->tds[td_current]->be);
322 usb_log_debug2(
323 "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.",
324 &ohci_batch->base,
325 usb_str_transfer_type(ohci_batch->base.ep->transfer_type),
326 usb_str_direction(dir),
327 USB_TRANSFER_BATCH_ARGS(ohci_batch->base));
328}
329
330/** Prepare generic data transfer
331 *
332 * @param[in] ohci_batch Batch structure to use.
333 * @paramp[in] dir Communication direction.
334 *
335 * Direction is supplied by the associated ep and toggle is maintained by the
336 * OHCI hw in ED.
337 */
338static void batch_data(ohci_transfer_batch_t *ohci_batch)
339{
340 assert(ohci_batch);
341
342 usb_direction_t dir = ohci_batch->base.dir;
343 assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
344
345 size_t td_current = 0;
346 size_t remain_size = ohci_batch->base.size;
347 char *buffer = ohci_batch->data_buffer;
348 while (remain_size > 0) {
349 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER ?
350 OHCI_TD_MAX_TRANSFER : remain_size;
351
352 td_init(
353 ohci_batch->tds[td_current], ohci_batch->tds[td_current + 1],
354 dir, buffer, transfer_size, -1);
355
356 usb_log_debug("Created DATA TD: %08x:%08x:%08x:%08x.",
357 ohci_batch->tds[td_current]->status,
358 ohci_batch->tds[td_current]->cbp,
359 ohci_batch->tds[td_current]->next,
360 ohci_batch->tds[td_current]->be);
361
362 buffer += transfer_size;
363 remain_size -= transfer_size;
364 assert(td_current < ohci_batch->td_count);
365 ++td_current;
366 }
367 usb_log_debug2(
368 "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.",
369 &ohci_batch->base,
370 usb_str_transfer_type(ohci_batch->base.ep->transfer_type),
371 usb_str_direction(dir),
372 USB_TRANSFER_BATCH_ARGS(ohci_batch->base));
373}
374
375/** Transfer setup table. */
376static void (*const batch_setup[])(ohci_transfer_batch_t *) =
377 {
378 [USB_TRANSFER_CONTROL] = batch_control,
379 [USB_TRANSFER_BULK] = batch_data,
380 [USB_TRANSFER_INTERRUPT] = batch_data,
381 [USB_TRANSFER_ISOCHRONOUS] = NULL,
382};
383/**
384 * @}
385 */
Note: See TracBrowser for help on using the repository browser.