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

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

usb: unified logging

Use logger instead of printf. Logger adds newlines automatically.

  • Property mode set to 100644
File size: 13.5 KB
RevLine 
[41b96b4]1/*
2 * Copyright (c) 2011 Jan Vesely
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 */
[58563585]28
[81dce9f]29/** @addtogroup drvusbohci
[41b96b4]30 * @{
31 */
32/** @file
33 * @brief OHCI driver USB transaction structure
34 */
[0d4b110]35
36#include <assert.h>
[41b96b4]37#include <errno.h>
[3f162ab]38#include <macros.h>
[0d4b110]39#include <mem.h>
40#include <stdbool.h>
[41b96b4]41
42#include <usb/usb.h>
43#include <usb/debug.h>
[2dbfe44]44#include <usb/host/utils/malloc32.h>
[41b96b4]45
[ff6dd73]46#include "ohci_batch.h"
[e6b9182]47#include "ohci_bus.h"
[09ace19]48
[5fd9c30]49static void (*const batch_setup[])(ohci_transfer_batch_t*);
[76fbd9a]50
[28d9c95]51/** Safely destructs ohci_transfer_batch_t structure
52 *
53 * @param[in] ohci_batch Instance to destroy.
54 */
[5fd9c30]55void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch)
[2cc6e97]56{
[5fd9c30]57 assert(ohci_batch);
[9c10e51]58 if (ohci_batch->tds) {
[11cb0565]59 const ohci_endpoint_t *ohci_ep =
[9162b27]60 ohci_endpoint_get(ohci_batch->base.ep);
[11cb0565]61 assert(ohci_ep);
[9b8958b]62 for (unsigned i = 0; i < ohci_batch->td_count; ++i) {
[11cb0565]63 if (ohci_batch->tds[i] != ohci_ep->td)
[9c10e51]64 free32(ohci_batch->tds[i]);
[9a6fde4]65 }
[9c10e51]66 free(ohci_batch->tds);
[9a6fde4]67 }
[9c10e51]68 free32(ohci_batch->device_buffer);
69 free(ohci_batch);
[2cc6e97]70}
[76fbd9a]71
[6fa04db]72/** Allocate memory and initialize internal data structure.
73 *
[9162b27]74 * @param[in] ep Endpoint for which the batch will be created
[6fa04db]75 * @return Valid pointer if all structures were successfully created,
76 * NULL otherwise.
77 */
[5fd9c30]78ohci_transfer_batch_t * ohci_transfer_batch_create(endpoint_t *ep)
[9c10e51]79{
[5fd9c30]80 assert(ep);
[e2976bb]81
[9c10e51]82 ohci_transfer_batch_t *ohci_batch =
[8e3d17f]83 calloc(1, sizeof(ohci_transfer_batch_t));
[584aa38]84 if (!ohci_batch) {
85 usb_log_error("Failed to allocate OHCI batch data.");
[5fd9c30]86 return NULL;
[584aa38]87 }
[5fd9c30]88
89 usb_transfer_batch_init(&ohci_batch->base, ep);
[7bce1fc]90 link_initialize(&ohci_batch->link);
[5fd9c30]91
92 return ohci_batch;
93}
94
95/** Prepares a batch to be sent.
96 *
97 * Determines the number of needed transfer descriptors (TDs).
98 * Prepares a transport buffer (that is accessible by the hardware).
99 * Initializes parameters needed for the transfer and callback.
100 */
101int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch)
102{
103 assert(ohci_batch);
104
105 const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
106 ? USB_SETUP_PACKET_SIZE
107 : 0;
108
109 usb_transfer_batch_t *usb_batch = &ohci_batch->base;
110
111 ohci_batch->td_count = (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
[9c10e51]112 / OHCI_TD_MAX_TRANSFER;
[e2976bb]113 /* Control transfer need Setup and Status stage */
[9c10e51]114 if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
115 ohci_batch->td_count += 2;
[e2976bb]116 }
117
[f974519]118 /* We need an extra place for TD that was left at ED */
[9c10e51]119 ohci_batch->tds = calloc(ohci_batch->td_count + 1, sizeof(td_t*));
[584aa38]120 if (!ohci_batch->tds) {
121 usb_log_error("Failed to allocate OHCI transfer descriptors.");
[5fd9c30]122 return ENOMEM;
[584aa38]123 }
[e2976bb]124
125 /* Add TD left over by the previous transfer */
[9c10e51]126 ohci_batch->ed = ohci_endpoint_get(usb_batch->ep)->ed;
127 ohci_batch->tds[0] = ohci_endpoint_get(usb_batch->ep)->td;
[9b8958b]128
129 for (unsigned i = 1; i <= ohci_batch->td_count; ++i) {
[9c10e51]130 ohci_batch->tds[i] = malloc32(sizeof(td_t));
[584aa38]131 if (!ohci_batch->tds[i]) {
132 usb_log_error("Failed to allocate TD %d.", i);
[5fd9c30]133 return ENOMEM;
[584aa38]134 }
[e2976bb]135 }
136
137 /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
138 * it is, however, not capable of handling buffer that occupies more
139 * than two pages (the first page is computed using start pointer, the
140 * other using the end pointer) */
[5fd9c30]141 if (setup_size + usb_batch->buffer_size > 0) {
[9c10e51]142 /* Use one buffer for setup and data stage */
143 ohci_batch->device_buffer =
[5fd9c30]144 malloc32(setup_size + usb_batch->buffer_size);
[584aa38]145 if (!ohci_batch->device_buffer) {
146 usb_log_error("Failed to allocate device buffer");
[5fd9c30]147 return ENOMEM;
[584aa38]148 }
[f974519]149 /* Copy setup data */
[5fd9c30]150 memcpy(ohci_batch->device_buffer, usb_batch->setup.buffer, setup_size);
[9c10e51]151 /* Copy generic data */
152 if (usb_batch->ep->direction != USB_DIRECTION_IN)
153 memcpy(
[5fd9c30]154 ohci_batch->device_buffer + setup_size,
[9c10e51]155 usb_batch->buffer, usb_batch->buffer_size);
[e2976bb]156 }
157
[790318e]158 assert(batch_setup[usb_batch->ep->transfer_type]);
[5fd9c30]159 batch_setup[usb_batch->ep->transfer_type](ohci_batch);
[90dd59dc]160
[5fd9c30]161 return EOK;
[e2976bb]162}
[76fbd9a]163
[28d9c95]164/** Check batch TDs' status.
165 *
[5f57929]166 * @param[in] ohci_batch Batch structure to use.
[28d9c95]167 * @return False, if there is an active TD, true otherwise.
168 *
169 * Walk all TDs (usually there is just one). Stop with false if there is an
170 * active TD. Stop with true if an error is found. Return true if the walk
171 * completes with the last TD.
172 */
[5fd9c30]173bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *ohci_batch)
[f98b8269]174{
[9c10e51]175 assert(ohci_batch);
176
[a1732929]177 usb_log_debug("Batch %p checking %zu td(s) for completion.",
[9162b27]178 &ohci_batch->base, ohci_batch->td_count);
[a1732929]179 usb_log_debug2("ED: %08x:%08x:%08x:%08x.",
[9c10e51]180 ohci_batch->ed->status, ohci_batch->ed->td_head,
181 ohci_batch->ed->td_tail, ohci_batch->ed->next);
182
[d5abaf4]183 if (!ed_inactive(ohci_batch->ed) && ed_transfer_pending(ohci_batch->ed))
184 return false;
185
186 /* Now we may be sure that either the ED is inactive because of errors
187 * or all transfer descriptors completed successfully */
188
189 /* Assume all data got through */
[5fd9c30]190 ohci_batch->base.transfered_size = ohci_batch->base.buffer_size;
[dab3112]191
192 /* Assume we will leave the last(unused) TD behind */
[11cb0565]193 unsigned leave_td = ohci_batch->td_count;
[d5abaf4]194
195 /* Check all TDs */
196 for (size_t i = 0; i < ohci_batch->td_count; ++i) {
[9c10e51]197 assert(ohci_batch->tds[i] != NULL);
[a1732929]198 usb_log_debug("TD %zu: %08x:%08x:%08x:%08x.", i,
[9c10e51]199 ohci_batch->tds[i]->status, ohci_batch->tds[i]->cbp,
200 ohci_batch->tds[i]->next, ohci_batch->tds[i]->be);
[d5abaf4]201
[5fd9c30]202 ohci_batch->base.error = td_error(ohci_batch->tds[i]);
203 if (ohci_batch->base.error == EOK) {
[7f7e6f5]204 /* If the TD got all its data through, it will report
205 * 0 bytes remain, the sole exception is INPUT with
206 * data rounding flag (short), i.e. every INPUT.
207 * Nice thing is that short packets will correctly
208 * report remaining data, thus making this computation
209 * correct (short packets need to be produced by the
210 * last TD)
211 * NOTE: This also works for CONTROL transfer as
212 * the first TD will return 0 remain.
213 * NOTE: Short packets don't break the assumption that
214 * we leave the very last(unused) TD behind.
215 */
[5fd9c30]216 ohci_batch->base.transfered_size
[7f7e6f5]217 -= td_remain_size(ohci_batch->tds[i]);
218 } else {
[a1732929]219 usb_log_debug("Batch %p found error TD(%zu):%08x.",
[9162b27]220 &ohci_batch->base, i,
[9c10e51]221 ohci_batch->tds[i]->status);
[d5abaf4]222
223 /* ED should be stopped because of errors */
224 assert((ohci_batch->ed->td_head & ED_TDHEAD_HALTED_FLAG) != 0);
225
226 /* Now we have a problem: we don't know what TD
227 * the head pointer points to, the retiring rules
228 * described in specs say it should be the one after
229 * the failed one so set the tail pointer accordingly.
230 * It will be the one TD we leave behind.
231 */
[11cb0565]232 leave_td = i + 1;
[d5abaf4]233
234 /* Check TD assumption */
[65eac7b]235 assert(ed_head_td(ohci_batch->ed) ==
236 addr_to_phys(ohci_batch->tds[leave_td]));
[d5abaf4]237
[65eac7b]238 /* Set tail to the same TD */
[d5abaf4]239 ed_set_tail_td(ohci_batch->ed,
[11cb0565]240 ohci_batch->tds[leave_td]);
[d5abaf4]241
242 /* Clear possible ED HALT */
[65eac7b]243 ed_clear_halt(ohci_batch->ed);
[9a6fde4]244 break;
[f1be95c8]245 }
246 }
[5fd9c30]247 assert(ohci_batch->base.transfered_size <=
248 ohci_batch->base.buffer_size);
249
[9162b27]250 const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
251 ? USB_SETUP_PACKET_SIZE
252 : 0;
253
[5fd9c30]254 if (ohci_batch->base.dir == USB_DIRECTION_IN)
[9162b27]255 memcpy(ohci_batch->base.buffer,
256 ohci_batch->device_buffer + setup_size,
257 ohci_batch->base.transfered_size);
[e20eaed]258
[d5abaf4]259 /* Store the remaining TD */
[5fd9c30]260 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
[e20eaed]261 assert(ohci_ep);
[11cb0565]262 ohci_ep->td = ohci_batch->tds[leave_td];
[dab3112]263
264 /* Make sure that we are leaving the right TD behind */
[65eac7b]265 assert(addr_to_phys(ohci_ep->td) == ed_head_td(ohci_batch->ed));
266 assert(addr_to_phys(ohci_ep->td) == ed_tail_td(ohci_batch->ed));
[d6522dd]267
[f1be95c8]268 return true;
[f98b8269]269}
[76fbd9a]270
[28d9c95]271/** Starts execution of the TD list
272 *
[5f57929]273 * @param[in] ohci_batch Batch structure to use
[28d9c95]274 */
[11cb0565]275void ohci_transfer_batch_commit(const ohci_transfer_batch_t *ohci_batch)
[7013b14]276{
[9c10e51]277 assert(ohci_batch);
[9515f674]278 ed_set_tail_td(ohci_batch->ed, ohci_batch->tds[ohci_batch->td_count]);
[7013b14]279}
[76fbd9a]280
[28d9c95]281/** Prepare generic control transfer
282 *
[5f57929]283 * @param[in] ohci_batch Batch structure to use.
[058fd76]284 * @param[in] dir Communication direction
[28d9c95]285 *
286 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
287 * Data stage with alternating toggle and direction supplied by parameter.
288 * Status stage with toggle 1 and direction supplied by parameter.
289 */
[5fd9c30]290static void batch_control(ohci_transfer_batch_t *ohci_batch)
[7786cea]291{
[9c10e51]292 assert(ohci_batch);
[5fd9c30]293
294 usb_direction_t dir = ohci_batch->base.dir;
[058fd76]295 assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
[5fd9c30]296
[a1732929]297 usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_batch->ed,
[9c10e51]298 ohci_batch->ed->status, ohci_batch->ed->td_tail,
299 ohci_batch->ed->td_head, ohci_batch->ed->next);
[058fd76]300 static const usb_direction_t reverse_dir[] = {
301 [USB_DIRECTION_IN] = USB_DIRECTION_OUT,
302 [USB_DIRECTION_OUT] = USB_DIRECTION_IN,
303 };
[9c10e51]304
[e42dd32]305 int toggle = 0;
[058fd76]306 const char* buffer = ohci_batch->device_buffer;
307 const usb_direction_t data_dir = dir;
308 const usb_direction_t status_dir = reverse_dir[dir];
[9c10e51]309
[dab3112]310 /* Setup stage */
[70d72dd]311 td_init(
312 ohci_batch->tds[0], ohci_batch->tds[1], USB_DIRECTION_BOTH,
[5fd9c30]313 buffer, USB_SETUP_PACKET_SIZE, toggle);
[a1732929]314 usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.",
[9c10e51]315 ohci_batch->tds[0]->status, ohci_batch->tds[0]->cbp,
316 ohci_batch->tds[0]->next, ohci_batch->tds[0]->be);
[5fd9c30]317 buffer += USB_SETUP_PACKET_SIZE;
[e42dd32]318
[dab3112]319 /* Data stage */
[e42dd32]320 size_t td_current = 1;
[9162b27]321 size_t remain_size = ohci_batch->base.buffer_size;
[e42dd32]322 while (remain_size > 0) {
[9c10e51]323 const size_t transfer_size =
[3f162ab]324 min(remain_size, OHCI_TD_MAX_TRANSFER);
[e42dd32]325 toggle = 1 - toggle;
326
[70d72dd]327 td_init(ohci_batch->tds[td_current],
328 ohci_batch->tds[td_current + 1],
329 data_dir, buffer, transfer_size, toggle);
[a1732929]330 usb_log_debug("Created CONTROL DATA TD: %08x:%08x:%08x:%08x.",
[9c10e51]331 ohci_batch->tds[td_current]->status,
332 ohci_batch->tds[td_current]->cbp,
333 ohci_batch->tds[td_current]->next,
334 ohci_batch->tds[td_current]->be);
[e42dd32]335
[d017cea]336 buffer += transfer_size;
[e42dd32]337 remain_size -= transfer_size;
[9c10e51]338 assert(td_current < ohci_batch->td_count - 1);
[e42dd32]339 ++td_current;
340 }
341
[dab3112]342 /* Status stage */
[9c10e51]343 assert(td_current == ohci_batch->td_count - 1);
[70d72dd]344 td_init(ohci_batch->tds[td_current], ohci_batch->tds[td_current + 1],
345 status_dir, NULL, 0, 1);
[a1732929]346 usb_log_debug("Created CONTROL STATUS TD: %08x:%08x:%08x:%08x.",
[9c10e51]347 ohci_batch->tds[td_current]->status,
348 ohci_batch->tds[td_current]->cbp,
349 ohci_batch->tds[td_current]->next,
350 ohci_batch->tds[td_current]->be);
[058fd76]351 usb_log_debug2(
[a1732929]352 "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.", \
[9162b27]353 &ohci_batch->base,
354 usb_str_transfer_type(ohci_batch->base.ep->transfer_type),
[058fd76]355 usb_str_direction(dir),
[9162b27]356 USB_TRANSFER_BATCH_ARGS(ohci_batch->base));
[f98b8269]357}
[76fbd9a]358
[28d9c95]359/** Prepare generic data transfer
360 *
[5f57929]361 * @param[in] ohci_batch Batch structure to use.
[058fd76]362 * @paramp[in] dir Communication direction.
[28d9c95]363 *
364 * Direction is supplied by the associated ep and toggle is maintained by the
365 * OHCI hw in ED.
366 */
[5fd9c30]367static void batch_data(ohci_transfer_batch_t *ohci_batch)
[c6fe469]368{
[9c10e51]369 assert(ohci_batch);
[5fd9c30]370
371 usb_direction_t dir = ohci_batch->base.dir;
[058fd76]372 assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
[a1732929]373 usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_batch->ed,
[9c10e51]374 ohci_batch->ed->status, ohci_batch->ed->td_tail,
375 ohci_batch->ed->td_head, ohci_batch->ed->next);
[c6fe469]376
[aa9ccf7]377 size_t td_current = 0;
[9162b27]378 size_t remain_size = ohci_batch->base.buffer_size;
[9c10e51]379 char *buffer = ohci_batch->device_buffer;
[c6fe469]380 while (remain_size > 0) {
[02cacce]381 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
382 ? OHCI_TD_MAX_TRANSFER : remain_size;
[c6fe469]383
[70d72dd]384 td_init(
385 ohci_batch->tds[td_current], ohci_batch->tds[td_current + 1],
386 dir, buffer, transfer_size, -1);
[058fd76]387
[a1732929]388 usb_log_debug("Created DATA TD: %08x:%08x:%08x:%08x.",
[9c10e51]389 ohci_batch->tds[td_current]->status,
390 ohci_batch->tds[td_current]->cbp,
391 ohci_batch->tds[td_current]->next,
392 ohci_batch->tds[td_current]->be);
[c6fe469]393
[d017cea]394 buffer += transfer_size;
[c6fe469]395 remain_size -= transfer_size;
[9c10e51]396 assert(td_current < ohci_batch->td_count);
[c6fe469]397 ++td_current;
398 }
[5f57929]399 usb_log_debug2(
[a1732929]400 "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.", \
[9162b27]401 &ohci_batch->base,
402 usb_str_transfer_type(ohci_batch->base.ep->transfer_type),
[058fd76]403 usb_str_direction(dir),
[9162b27]404 USB_TRANSFER_BATCH_ARGS(ohci_batch->base));
[7bce1fc]405}
[76fbd9a]406
[6fa04db]407/** Transfer setup table. */
[5fd9c30]408static void (*const batch_setup[])(ohci_transfer_batch_t*) =
[7bce1fc]409{
[790318e]410 [USB_TRANSFER_CONTROL] = batch_control,
411 [USB_TRANSFER_BULK] = batch_data,
412 [USB_TRANSFER_INTERRUPT] = batch_data,
413 [USB_TRANSFER_ISOCHRONOUS] = NULL,
[7bce1fc]414};
[41b96b4]415/**
416 * @}
417 */
Note: See TracBrowser for help on using the repository browser.