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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ff6dd73 was ff6dd73, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

ohci: rename file batch ⇒ ohci_batch in reparation for further changes

  • Property mode set to 100644
File size: 15.0 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 */
[81dce9f]28/** @addtogroup drvusbohci
[41b96b4]29 * @{
30 */
31/** @file
32 * @brief OHCI driver USB transaction structure
33 */
34#include <errno.h>
35#include <str_error.h>
36
37#include <usb/usb.h>
38#include <usb/debug.h>
39
[ff6dd73]40#include "ohci_batch.h"
[e20eaed]41#include "ohci_endpoint.h"
[2bf8f8c]42#include "utils/malloc32.h"
[06c552c]43#include "hw_struct/endpoint_descriptor.h"
44#include "hw_struct/transfer_descriptor.h"
[09ace19]45
[90dd59dc]46static void batch_control_write(usb_transfer_batch_t *instance);
47static void batch_control_read(usb_transfer_batch_t *instance);
48
49static void batch_interrupt_in(usb_transfer_batch_t *instance);
50static void batch_interrupt_out(usb_transfer_batch_t *instance);
51
52static void batch_bulk_in(usb_transfer_batch_t *instance);
53static void batch_bulk_out(usb_transfer_batch_t *instance);
[09ace19]54
[90dd59dc]55static void batch_setup_control(usb_transfer_batch_t *batch)
56{
57 // TODO Find a better way to do this
58 if (batch->setup_buffer[0] & (1 << 7))
59 batch_control_read(batch);
60 else
61 batch_control_write(batch);
62}
[f974519]63/*----------------------------------------------------------------------------*/
[90dd59dc]64void (*batch_setup[4][3])(usb_transfer_batch_t*) =
65{
66 { NULL, NULL, batch_setup_control },
67 { NULL, NULL, NULL },
68 { batch_bulk_in, batch_bulk_out, NULL },
69 { batch_interrupt_in, batch_interrupt_out, NULL },
70};
71
[06c552c]72
[28d9c95]73/** OHCI specific data required for USB transfer */
[2cc6e97]74typedef struct ohci_transfer_batch {
[28d9c95]75 /** Endpoint descriptor of the target endpoint. */
[06c552c]76 ed_t *ed;
[28d9c95]77 /** List of TDs needed for the transfer */
[9a6fde4]78 td_t **tds;
[28d9c95]79 /** Number of TDs used by the transfer */
[06c552c]80 size_t td_count;
[28d9c95]81 /** Dummy TD to be left at the ED and used by the next transfer */
[9a6fde4]82 size_t leave_td;
[28d9c95]83 /** Data buffer, must be accessible byb the OHCI hw. */
84 void *device_buffer;
[2cc6e97]85} ohci_transfer_batch_t;
[28d9c95]86/*----------------------------------------------------------------------------*/
87static void batch_control(usb_transfer_batch_t *instance,
88 usb_direction_t data_dir, usb_direction_t status_dir);
89static void batch_data(usb_transfer_batch_t *instance);
90/*----------------------------------------------------------------------------*/
91/** Safely destructs ohci_transfer_batch_t structure
92 *
93 * @param[in] ohci_batch Instance to destroy.
94 */
[e2976bb]95static void ohci_batch_dispose(void *ohci_batch)
[2cc6e97]96{
97 ohci_transfer_batch_t *instance = ohci_batch;
[9a6fde4]98 if (!instance)
99 return;
100 unsigned i = 0;
101 if (instance->tds) {
102 for (; i< instance->td_count; ++i) {
103 if (i != instance->leave_td)
104 free32(instance->tds[i]);
105 }
106 free(instance->tds);
107 }
[f974519]108 free32(instance->device_buffer);
[9a6fde4]109 free(instance);
[2cc6e97]110}
[9a6fde4]111/*----------------------------------------------------------------------------*/
[e2976bb]112int batch_init_ohci(usb_transfer_batch_t *batch)
113{
114 assert(batch);
115#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
116 if (ptr == NULL) { \
117 usb_log_error(message); \
118 if (data) { \
119 ohci_batch_dispose(data); \
120 } \
121 return ENOMEM; \
122 } else (void)0
123
124 ohci_transfer_batch_t *data = calloc(sizeof(ohci_transfer_batch_t), 1);
125 CHECK_NULL_DISPOSE_RETURN(data, "Failed to allocate batch data.\n");
126
127 data->td_count = ((batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
128 / OHCI_TD_MAX_TRANSFER);
129 /* Control transfer need Setup and Status stage */
130 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
131 data->td_count += 2;
132 }
133
[f974519]134 /* We need an extra place for TD that was left at ED */
[e2976bb]135 data->tds = calloc(sizeof(td_t*), data->td_count + 1);
136 CHECK_NULL_DISPOSE_RETURN(data->tds,
137 "Failed to allocate transfer descriptors.\n");
138
139 /* Add TD left over by the previous transfer */
[f974519]140 data->ed = ohci_endpoint_get(batch->ep)->ed;
141 data->tds[0] = ohci_endpoint_get(batch->ep)->td;
[e2976bb]142 data->leave_td = 0;
143 unsigned i = 1;
144 for (; i <= data->td_count; ++i) {
145 data->tds[i] = malloc32(sizeof(td_t));
146 CHECK_NULL_DISPOSE_RETURN(data->tds[i],
147 "Failed to allocate TD %d.\n", i );
148 }
149
150
151 /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
152 * it is, however, not capable of handling buffer that occupies more
153 * than two pages (the first page is computed using start pointer, the
154 * other using the end pointer) */
155 if (batch->setup_size + batch->buffer_size > 0) {
156 data->device_buffer =
157 malloc32(batch->setup_size + batch->buffer_size);
158 CHECK_NULL_DISPOSE_RETURN(data->device_buffer,
159 "Failed to allocate device accessible buffer.\n");
[f974519]160 /* Copy setup data */
[e2976bb]161 memcpy(data->device_buffer, batch->setup_buffer,
162 batch->setup_size);
163 batch->data_buffer = data->device_buffer + batch->setup_size;
164 }
165
[f974519]166 batch->private_data = data;
167 batch->private_data_dtor = ohci_batch_dispose;
168
[90dd59dc]169 assert(batch_setup[batch->ep->transfer_type][batch->ep->direction]);
170 batch_setup[batch->ep->transfer_type][batch->ep->direction](batch);
171
[e2976bb]172 return EOK;
173#undef CHECK_NULL_DISPOSE_RETURN
174}
175/*----------------------------------------------------------------------------*/
[28d9c95]176/** Check batch TDs' status.
177 *
178 * @param[in] instance Batch structure to use.
179 * @return False, if there is an active TD, true otherwise.
180 *
181 * Walk all TDs (usually there is just one). Stop with false if there is an
182 * active TD. Stop with true if an error is found. Return true if the walk
183 * completes with the last TD.
184 */
[f98b8269]185bool batch_is_complete(usb_transfer_batch_t *instance)
186{
[f1be95c8]187 assert(instance);
[2cc6e97]188 ohci_transfer_batch_t *data = instance->private_data;
[f1be95c8]189 assert(data);
[4125b7d]190 usb_log_debug("Batch(%p) checking %zu td(s) for completion.\n",
[33d19a7]191 instance, data->td_count);
[78d4e1f]192 usb_log_debug("ED: %x:%x:%x:%x.\n",
193 data->ed->status, data->ed->td_head, data->ed->td_tail,
194 data->ed->next);
[f1be95c8]195 size_t i = 0;
[ad86349]196 instance->transfered_size = instance->buffer_size;
[33d19a7]197 for (; i < data->td_count; ++i) {
[9a6fde4]198 assert(data->tds[i] != NULL);
[4125b7d]199 usb_log_debug("TD %zu: %x:%x:%x:%x.\n", i,
[9a6fde4]200 data->tds[i]->status, data->tds[i]->cbp, data->tds[i]->next,
201 data->tds[i]->be);
202 if (!td_is_finished(data->tds[i])) {
[f1be95c8]203 return false;
[aa9ccf7]204 }
[9a6fde4]205 instance->error = td_error(data->tds[i]);
[f1be95c8]206 if (instance->error != EOK) {
[4125b7d]207 usb_log_debug("Batch(%p) found error TD(%zu):%x.\n",
[9a6fde4]208 instance, i, data->tds[i]->status);
[c9dc471]209 /* Make sure TD queue is empty (one TD),
210 * ED should be marked as halted */
211 data->ed->td_tail =
212 (data->ed->td_head & ED_TDTAIL_PTR_MASK);
213 ++i;
[9a6fde4]214 break;
[f1be95c8]215 }
216 }
[d6522dd]217 data->leave_td = i;
[9a6fde4]218 assert(data->leave_td <= data->td_count);
[e20eaed]219
220 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(instance->ep);
221 assert(ohci_ep);
[f974519]222 ohci_ep->td = data->tds[data->leave_td];
[33d19a7]223 assert(i > 0);
224 for (--i;i < data->td_count; ++i)
225 instance->transfered_size -= td_remain_size(data->tds[i]);
[ad86349]226
[c9dc471]227 /* Clear possible ED HALT */
228 data->ed->td_head &= ~ED_TDHEAD_HALTED_FLAG;
[e20eaed]229 const uint32_t pa = addr_to_phys(ohci_ep->td);
[c9dc471]230 assert(pa == (data->ed->td_head & ED_TDHEAD_PTR_MASK));
231 assert(pa == (data->ed->td_tail & ED_TDTAIL_PTR_MASK));
[d6522dd]232
[f1be95c8]233 return true;
[f98b8269]234}
235/*----------------------------------------------------------------------------*/
[28d9c95]236/** Starts execution of the TD list
237 *
238 * @param[in] instance Batch structure to use
239 */
[7013b14]240void batch_commit(usb_transfer_batch_t *instance)
241{
242 assert(instance);
243 ohci_transfer_batch_t *data = instance->private_data;
244 assert(data);
245 ed_set_end_td(data->ed, data->tds[data->td_count]);
246}
247/*----------------------------------------------------------------------------*/
[28d9c95]248/** Prepares control write transfer.
249 *
250 * @param[in] instance Batch structure to use.
251 *
252 * Uses generic control transfer using direction OUT(data stage) and
253 * IN(status stage).
254 */
[1387692]255void batch_control_write(usb_transfer_batch_t *instance)
[be7950e8]256{
[2bf8f8c]257 assert(instance);
[d328172]258 /* We are data out, we are supposed to provide data */
[d017cea]259 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
[2cc6e97]260 instance->next_step = usb_transfer_batch_call_out_and_dispose;
[e42dd32]261 batch_control(instance, USB_DIRECTION_OUT, USB_DIRECTION_IN);
[d328172]262 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
[be7950e8]263}
264/*----------------------------------------------------------------------------*/
[28d9c95]265/** Prepares control read transfer.
266 *
267 * @param[in] instance Batch structure to use.
268 *
269 * Uses generic control transfer using direction IN(data stage) and
270 * OUT(status stage).
271 */
[1387692]272void batch_control_read(usb_transfer_batch_t *instance)
[be7950e8]273{
[2bf8f8c]274 assert(instance);
[2cc6e97]275 instance->next_step = usb_transfer_batch_call_in_and_dispose;
[e42dd32]276 batch_control(instance, USB_DIRECTION_IN, USB_DIRECTION_OUT);
[81001f6]277 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
[be7950e8]278}
279/*----------------------------------------------------------------------------*/
[28d9c95]280/** Prepare interrupt in transfer.
281 *
282 * @param[in] instance Batch structure to use.
283 *
284 * Data transfer.
285 */
[1387692]286void batch_interrupt_in(usb_transfer_batch_t *instance)
[be7950e8]287{
[2bf8f8c]288 assert(instance);
[2cc6e97]289 instance->next_step = usb_transfer_batch_call_in_and_dispose;
[c6fe469]290 batch_data(instance);
[d328172]291 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
[be7950e8]292}
293/*----------------------------------------------------------------------------*/
[28d9c95]294/** Prepare interrupt out transfer.
295 *
296 * @param[in] instance Batch structure to use.
297 *
298 * Data transfer.
299 */
[1387692]300void batch_interrupt_out(usb_transfer_batch_t *instance)
[be7950e8]301{
[2bf8f8c]302 assert(instance);
[d328172]303 /* We are data out, we are supposed to provide data */
[d017cea]304 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
[2cc6e97]305 instance->next_step = usb_transfer_batch_call_out_and_dispose;
[c6fe469]306 batch_data(instance);
[d328172]307 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
[be7950e8]308}
309/*----------------------------------------------------------------------------*/
[28d9c95]310/** Prepare bulk in transfer.
311 *
312 * @param[in] instance Batch structure to use.
313 *
314 * Data transfer.
315 */
[1387692]316void batch_bulk_in(usb_transfer_batch_t *instance)
[be7950e8]317{
[2bf8f8c]318 assert(instance);
[2cc6e97]319 instance->next_step = usb_transfer_batch_call_in_and_dispose;
[c6fe469]320 batch_data(instance);
[d328172]321 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
[be7950e8]322}
323/*----------------------------------------------------------------------------*/
[28d9c95]324/** Prepare bulk out transfer.
325 *
326 * @param[in] instance Batch structure to use.
327 *
328 * Data transfer.
329 */
[1387692]330void batch_bulk_out(usb_transfer_batch_t *instance)
[be7950e8]331{
[2bf8f8c]332 assert(instance);
[6b082a1b]333 /* We are data out, we are supposed to provide data */
334 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
[a13ed97]335 instance->next_step = usb_transfer_batch_call_out_and_dispose;
[c6fe469]336 batch_data(instance);
[a13ed97]337 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
[d328172]338}
339/*----------------------------------------------------------------------------*/
[28d9c95]340/** Prepare generic control transfer
341 *
342 * @param[in] instance Batch structure to use.
343 * @param[in] data_dir Direction to use for data stage.
344 * @param[in] status_dir Direction to use for status stage.
345 *
346 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
347 * Data stage with alternating toggle and direction supplied by parameter.
348 * Status stage with toggle 1 and direction supplied by parameter.
349 */
[e42dd32]350void batch_control(usb_transfer_batch_t *instance,
351 usb_direction_t data_dir, usb_direction_t status_dir)
[7786cea]352{
353 assert(instance);
[2cc6e97]354 ohci_transfer_batch_t *data = instance->private_data;
[7786cea]355 assert(data);
[d6522dd]356 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
[8790650]357 data->ed->status, data->ed->td_tail, data->ed->td_head,
358 data->ed->next);
[e42dd32]359 int toggle = 0;
360 /* setup stage */
[9a6fde4]361 td_init(data->tds[0], USB_DIRECTION_BOTH, instance->setup_buffer,
[e42dd32]362 instance->setup_size, toggle);
[9a6fde4]363 td_set_next(data->tds[0], data->tds[1]);
[f974519]364 usb_log_debug("Created CONTROL SETUP TD: %x:%x:%x:%x.\n",
365 data->tds[0]->status, data->tds[0]->cbp, data->tds[0]->next,
366 data->tds[0]->be);
[e42dd32]367
368 /* data stage */
369 size_t td_current = 1;
370 size_t remain_size = instance->buffer_size;
[d017cea]371 char *buffer = instance->data_buffer;
[e42dd32]372 while (remain_size > 0) {
373 size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER ?
374 OHCI_TD_MAX_TRANSFER : remain_size;
375 toggle = 1 - toggle;
376
[9a6fde4]377 td_init(data->tds[td_current], data_dir, buffer,
[e42dd32]378 transfer_size, toggle);
[9a6fde4]379 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
[f974519]380 usb_log_debug("Created CONTROL DATA TD: %x:%x:%x:%x.\n",
[9a6fde4]381 data->tds[td_current]->status, data->tds[td_current]->cbp,
382 data->tds[td_current]->next, data->tds[td_current]->be);
[e42dd32]383
[d017cea]384 buffer += transfer_size;
[e42dd32]385 remain_size -= transfer_size;
[9a6fde4]386 assert(td_current < data->td_count - 1);
[e42dd32]387 ++td_current;
388 }
389
390 /* status stage */
[9a6fde4]391 assert(td_current == data->td_count - 1);
392 td_init(data->tds[td_current], status_dir, NULL, 0, 1);
393 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
[f974519]394 usb_log_debug("Created CONTROL STATUS TD: %x:%x:%x:%x.\n",
[9a6fde4]395 data->tds[td_current]->status, data->tds[td_current]->cbp,
396 data->tds[td_current]->next, data->tds[td_current]->be);
[f98b8269]397}
398/*----------------------------------------------------------------------------*/
[28d9c95]399/** Prepare generic data transfer
400 *
401 * @param[in] instance Batch structure to use.
402 *
403 * Direction is supplied by the associated ep and toggle is maintained by the
404 * OHCI hw in ED.
405 */
[c6fe469]406void batch_data(usb_transfer_batch_t *instance)
407{
408 assert(instance);
[2cc6e97]409 ohci_transfer_batch_t *data = instance->private_data;
[c6fe469]410 assert(data);
[d6522dd]411 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
[c6fe469]412 data->ed->status, data->ed->td_tail, data->ed->td_head,
413 data->ed->next);
414
[aa9ccf7]415 size_t td_current = 0;
[c6fe469]416 size_t remain_size = instance->buffer_size;
[d017cea]417 char *buffer = instance->data_buffer;
[c6fe469]418 while (remain_size > 0) {
[02cacce]419 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
420 ? OHCI_TD_MAX_TRANSFER : remain_size;
[c6fe469]421
[9a6fde4]422 td_init(data->tds[td_current], instance->ep->direction,
[d017cea]423 buffer, transfer_size, -1);
[9a6fde4]424 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
[c6fe469]425 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
[9a6fde4]426 data->tds[td_current]->status, data->tds[td_current]->cbp,
427 data->tds[td_current]->next, data->tds[td_current]->be);
[c6fe469]428
[d017cea]429 buffer += transfer_size;
[c6fe469]430 remain_size -= transfer_size;
431 assert(td_current < data->td_count);
432 ++td_current;
433 }
434}
[41b96b4]435/**
436 * @}
437 */
Note: See TracBrowser for help on using the repository browser.