source: mainline/uspace/drv/bus/usb/ohci/batch.c@ 90dd59dc

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

ohci: implement schedule function, finish batch initialization

  • Property mode set to 100644
File size: 18.2 KB
Line 
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 */
28/** @addtogroup drvusbohci
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
40#include "batch.h"
41#include "hcd_endpoint.h"
42#include "utils/malloc32.h"
43#include "hw_struct/endpoint_descriptor.h"
44#include "hw_struct/transfer_descriptor.h"
45/*
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);
54*/
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}
63
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
72
73/** OHCI specific data required for USB transfer */
74typedef struct ohci_transfer_batch {
75 /** Endpoint descriptor of the target endpoint. */
76 ed_t *ed;
77 /** List of TDs needed for the transfer */
78 td_t **tds;
79 /** Number of TDs used by the transfer */
80 size_t td_count;
81 /** Dummy TD to be left at the ED and used by the next transfer */
82 size_t leave_td;
83 /** Data buffer, must be accessible byb the OHCI hw. */
84 void *device_buffer;
85} ohci_transfer_batch_t;
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 */
95static void ohci_batch_dispose(void *ohci_batch)
96{
97 ohci_transfer_batch_t *instance = ohci_batch;
98 if (!instance)
99 return;
100 free32(instance->device_buffer);
101 unsigned i = 0;
102 if (instance->tds) {
103 for (; i< instance->td_count; ++i) {
104 if (i != instance->leave_td)
105 free32(instance->tds[i]);
106 }
107 free(instance->tds);
108 }
109 free(instance);
110}
111/*----------------------------------------------------------------------------*/
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 const hcd_endpoint_t *hcd_ep = hcd_endpoint_get(batch->ep);
125 assert(hcd_ep);
126
127 ohci_transfer_batch_t *data = calloc(sizeof(ohci_transfer_batch_t), 1);
128 CHECK_NULL_DISPOSE_RETURN(data, "Failed to allocate batch data.\n");
129
130 data->td_count = ((batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
131 / OHCI_TD_MAX_TRANSFER);
132 /* Control transfer need Setup and Status stage */
133 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
134 data->td_count += 2;
135 }
136
137 /* We need an extra place for TD that is currently assigned to hcd_ep*/
138 data->tds = calloc(sizeof(td_t*), data->td_count + 1);
139 CHECK_NULL_DISPOSE_RETURN(data->tds,
140 "Failed to allocate transfer descriptors.\n");
141
142 /* Add TD left over by the previous transfer */
143 data->tds[0] = hcd_ep->td;
144 data->leave_td = 0;
145 unsigned i = 1;
146 for (; i <= data->td_count; ++i) {
147 data->tds[i] = malloc32(sizeof(td_t));
148 CHECK_NULL_DISPOSE_RETURN(data->tds[i],
149 "Failed to allocate TD %d.\n", i );
150 }
151
152 data->ed = hcd_ep->ed;
153 batch->private_data = data;
154 batch->private_data_dtor = ohci_batch_dispose;
155
156 /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
157 * it is, however, not capable of handling buffer that occupies more
158 * than two pages (the first page is computed using start pointer, the
159 * other using the end pointer) */
160 if (batch->setup_size + batch->buffer_size > 0) {
161 data->device_buffer =
162 malloc32(batch->setup_size + batch->buffer_size);
163 CHECK_NULL_DISPOSE_RETURN(data->device_buffer,
164 "Failed to allocate device accessible buffer.\n");
165 memcpy(data->device_buffer, batch->setup_buffer,
166 batch->setup_size);
167 batch->data_buffer = data->device_buffer + batch->setup_size;
168 }
169
170 assert(batch_setup[batch->ep->transfer_type][batch->ep->direction]);
171 batch_setup[batch->ep->transfer_type][batch->ep->direction](batch);
172
173 return EOK;
174#undef CHECK_NULL_DISPOSE_RETURN
175}
176/*----------------------------------------------------------------------------*/
177/** Allocate memory initialize internal structures
178 *
179 * @param[in] fun DDF function to pass to callback.
180 * @param[in] ep Communication target
181 * @param[in] buffer Data source/destination.
182 * @param[in] buffer_size Size of the buffer.
183 * @param[in] setup_buffer Setup data source (if not NULL)
184 * @param[in] setup_size Size of setup_buffer (should be always 8)
185 * @param[in] func_in function to call on inbound transfer completion
186 * @param[in] func_out function to call on outbound transfer completion
187 * @param[in] arg additional parameter to func_in or func_out
188 * @return Valid pointer if all structures were successfully created,
189 * NULL otherwise.
190 *
191 * Allocates and initializes structures needed by the OHCI hw for the transfer.
192 */
193usb_transfer_batch_t * batch_get(ddf_fun_t *fun, endpoint_t *ep,
194 char *buffer, size_t buffer_size,
195 const char *setup_buffer, size_t setup_size,
196 usbhc_iface_transfer_in_callback_t func_in,
197 usbhc_iface_transfer_out_callback_t func_out, void *arg)
198{
199#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
200 if (ptr == NULL) { \
201 usb_log_error(message); \
202 if (instance) { \
203 usb_transfer_batch_dispose(instance); \
204 } \
205 return NULL; \
206 } else (void)0
207
208 usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
209 CHECK_NULL_DISPOSE_RETURN(instance,
210 "Failed to allocate batch instance.\n");
211 usb_transfer_batch_init(instance, ep, buffer, NULL, buffer_size,
212 NULL, setup_size, func_in, func_out, arg, fun, NULL,
213 ohci_batch_dispose);
214
215 const hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
216 assert(hcd_ep);
217
218 ohci_transfer_batch_t *data = calloc(sizeof(ohci_transfer_batch_t), 1);
219 CHECK_NULL_DISPOSE_RETURN(data, "Failed to allocate batch data.\n");
220 instance->private_data = data;
221
222 data->td_count =
223 ((buffer_size + OHCI_TD_MAX_TRANSFER - 1) / OHCI_TD_MAX_TRANSFER);
224 /* Control transfer need Setup and Status stage */
225 if (ep->transfer_type == USB_TRANSFER_CONTROL) {
226 data->td_count += 2;
227 }
228
229 /* We need an extra place for TD that is currently assigned to hcd_ep*/
230 data->tds = calloc(sizeof(td_t*), data->td_count + 1);
231 CHECK_NULL_DISPOSE_RETURN(data->tds,
232 "Failed to allocate transfer descriptors.\n");
233
234 /* Add TD left over by the previous transfer */
235 data->tds[0] = hcd_ep->td;
236 data->leave_td = 0;
237 unsigned i = 1;
238 for (; i <= data->td_count; ++i) {
239 data->tds[i] = malloc32(sizeof(td_t));
240 CHECK_NULL_DISPOSE_RETURN(data->tds[i],
241 "Failed to allocate TD %d.\n", i );
242 }
243
244 data->ed = hcd_ep->ed;
245
246 /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
247 * it is, however, not capable of handling buffer that occupies more
248 * than two pages (the first page is computed using start pointer, the
249 * other using the end pointer) */
250 if (setup_size + buffer_size > 0) {
251 data->device_buffer = malloc32(setup_size + buffer_size);
252 CHECK_NULL_DISPOSE_RETURN(data->device_buffer,
253 "Failed to allocate device accessible buffer.\n");
254 instance->setup_buffer = data->device_buffer;
255 instance->data_buffer = data->device_buffer + setup_size;
256 memcpy(instance->setup_buffer, setup_buffer, setup_size);
257 }
258
259 return instance;
260}
261/*----------------------------------------------------------------------------*/
262/** Check batch TDs' status.
263 *
264 * @param[in] instance Batch structure to use.
265 * @return False, if there is an active TD, true otherwise.
266 *
267 * Walk all TDs (usually there is just one). Stop with false if there is an
268 * active TD. Stop with true if an error is found. Return true if the walk
269 * completes with the last TD.
270 */
271bool batch_is_complete(usb_transfer_batch_t *instance)
272{
273 assert(instance);
274 ohci_transfer_batch_t *data = instance->private_data;
275 assert(data);
276 usb_log_debug("Batch(%p) checking %zu td(s) for completion.\n",
277 instance, data->td_count);
278 usb_log_debug("ED: %x:%x:%x:%x.\n",
279 data->ed->status, data->ed->td_head, data->ed->td_tail,
280 data->ed->next);
281 size_t i = 0;
282 instance->transfered_size = instance->buffer_size;
283 for (; i < data->td_count; ++i) {
284 assert(data->tds[i] != NULL);
285 usb_log_debug("TD %zu: %x:%x:%x:%x.\n", i,
286 data->tds[i]->status, data->tds[i]->cbp, data->tds[i]->next,
287 data->tds[i]->be);
288 if (!td_is_finished(data->tds[i])) {
289 return false;
290 }
291 instance->error = td_error(data->tds[i]);
292 if (instance->error != EOK) {
293 usb_log_debug("Batch(%p) found error TD(%zu):%x.\n",
294 instance, i, data->tds[i]->status);
295 /* Make sure TD queue is empty (one TD),
296 * ED should be marked as halted */
297 data->ed->td_tail =
298 (data->ed->td_head & ED_TDTAIL_PTR_MASK);
299 ++i;
300 break;
301 }
302 }
303 data->leave_td = i;
304 assert(data->leave_td <= data->td_count);
305 hcd_endpoint_t *hcd_ep = hcd_endpoint_get(instance->ep);
306 assert(hcd_ep);
307 hcd_ep->td = data->tds[i];
308 assert(i > 0);
309 for (--i;i < data->td_count; ++i)
310 instance->transfered_size -= td_remain_size(data->tds[i]);
311
312 /* Clear possible ED HALT */
313 data->ed->td_head &= ~ED_TDHEAD_HALTED_FLAG;
314 const uint32_t pa = addr_to_phys(hcd_ep->td);
315 assert(pa == (data->ed->td_head & ED_TDHEAD_PTR_MASK));
316 assert(pa == (data->ed->td_tail & ED_TDTAIL_PTR_MASK));
317
318 return true;
319}
320/*----------------------------------------------------------------------------*/
321/** Starts execution of the TD list
322 *
323 * @param[in] instance Batch structure to use
324 */
325void batch_commit(usb_transfer_batch_t *instance)
326{
327 assert(instance);
328 ohci_transfer_batch_t *data = instance->private_data;
329 assert(data);
330 ed_set_end_td(data->ed, data->tds[data->td_count]);
331}
332/*----------------------------------------------------------------------------*/
333/** Prepares control write transfer.
334 *
335 * @param[in] instance Batch structure to use.
336 *
337 * Uses generic control transfer using direction OUT(data stage) and
338 * IN(status stage).
339 */
340void batch_control_write(usb_transfer_batch_t *instance)
341{
342 assert(instance);
343 /* We are data out, we are supposed to provide data */
344 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
345 instance->next_step = usb_transfer_batch_call_out_and_dispose;
346 batch_control(instance, USB_DIRECTION_OUT, USB_DIRECTION_IN);
347 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
348}
349/*----------------------------------------------------------------------------*/
350/** Prepares control read transfer.
351 *
352 * @param[in] instance Batch structure to use.
353 *
354 * Uses generic control transfer using direction IN(data stage) and
355 * OUT(status stage).
356 */
357void batch_control_read(usb_transfer_batch_t *instance)
358{
359 assert(instance);
360 instance->next_step = usb_transfer_batch_call_in_and_dispose;
361 batch_control(instance, USB_DIRECTION_IN, USB_DIRECTION_OUT);
362 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
363}
364/*----------------------------------------------------------------------------*/
365/** Prepare interrupt in transfer.
366 *
367 * @param[in] instance Batch structure to use.
368 *
369 * Data transfer.
370 */
371void batch_interrupt_in(usb_transfer_batch_t *instance)
372{
373 assert(instance);
374 instance->next_step = usb_transfer_batch_call_in_and_dispose;
375 batch_data(instance);
376 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
377}
378/*----------------------------------------------------------------------------*/
379/** Prepare interrupt out transfer.
380 *
381 * @param[in] instance Batch structure to use.
382 *
383 * Data transfer.
384 */
385void batch_interrupt_out(usb_transfer_batch_t *instance)
386{
387 assert(instance);
388 /* We are data out, we are supposed to provide data */
389 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
390 instance->next_step = usb_transfer_batch_call_out_and_dispose;
391 batch_data(instance);
392 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
393}
394/*----------------------------------------------------------------------------*/
395/** Prepare bulk in transfer.
396 *
397 * @param[in] instance Batch structure to use.
398 *
399 * Data transfer.
400 */
401void batch_bulk_in(usb_transfer_batch_t *instance)
402{
403 assert(instance);
404 instance->next_step = usb_transfer_batch_call_in_and_dispose;
405 batch_data(instance);
406 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
407}
408/*----------------------------------------------------------------------------*/
409/** Prepare bulk out transfer.
410 *
411 * @param[in] instance Batch structure to use.
412 *
413 * Data transfer.
414 */
415void batch_bulk_out(usb_transfer_batch_t *instance)
416{
417 assert(instance);
418 /* We are data out, we are supposed to provide data */
419 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
420 instance->next_step = usb_transfer_batch_call_out_and_dispose;
421 batch_data(instance);
422 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
423}
424/*----------------------------------------------------------------------------*/
425/** Prepare generic control transfer
426 *
427 * @param[in] instance Batch structure to use.
428 * @param[in] data_dir Direction to use for data stage.
429 * @param[in] status_dir Direction to use for status stage.
430 *
431 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
432 * Data stage with alternating toggle and direction supplied by parameter.
433 * Status stage with toggle 1 and direction supplied by parameter.
434 */
435void batch_control(usb_transfer_batch_t *instance,
436 usb_direction_t data_dir, usb_direction_t status_dir)
437{
438 assert(instance);
439 ohci_transfer_batch_t *data = instance->private_data;
440 assert(data);
441 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
442 data->ed->status, data->ed->td_tail, data->ed->td_head,
443 data->ed->next);
444 int toggle = 0;
445 /* setup stage */
446 td_init(data->tds[0], USB_DIRECTION_BOTH, instance->setup_buffer,
447 instance->setup_size, toggle);
448 td_set_next(data->tds[0], data->tds[1]);
449 usb_log_debug("Created SETUP TD: %x:%x:%x:%x.\n", data->tds[0]->status,
450 data->tds[0]->cbp, data->tds[0]->next, data->tds[0]->be);
451
452 /* data stage */
453 size_t td_current = 1;
454 size_t remain_size = instance->buffer_size;
455 char *buffer = instance->data_buffer;
456 while (remain_size > 0) {
457 size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER ?
458 OHCI_TD_MAX_TRANSFER : remain_size;
459 toggle = 1 - toggle;
460
461 td_init(data->tds[td_current], data_dir, buffer,
462 transfer_size, toggle);
463 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
464 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
465 data->tds[td_current]->status, data->tds[td_current]->cbp,
466 data->tds[td_current]->next, data->tds[td_current]->be);
467
468 buffer += transfer_size;
469 remain_size -= transfer_size;
470 assert(td_current < data->td_count - 1);
471 ++td_current;
472 }
473
474 /* status stage */
475 assert(td_current == data->td_count - 1);
476 td_init(data->tds[td_current], status_dir, NULL, 0, 1);
477 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
478 usb_log_debug("Created STATUS TD: %x:%x:%x:%x.\n",
479 data->tds[td_current]->status, data->tds[td_current]->cbp,
480 data->tds[td_current]->next, data->tds[td_current]->be);
481}
482/*----------------------------------------------------------------------------*/
483/** Prepare generic data transfer
484 *
485 * @param[in] instance Batch structure to use.
486 *
487 * Direction is supplied by the associated ep and toggle is maintained by the
488 * OHCI hw in ED.
489 */
490void batch_data(usb_transfer_batch_t *instance)
491{
492 assert(instance);
493 ohci_transfer_batch_t *data = instance->private_data;
494 assert(data);
495 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
496 data->ed->status, data->ed->td_tail, data->ed->td_head,
497 data->ed->next);
498
499 size_t td_current = 0;
500 size_t remain_size = instance->buffer_size;
501 char *buffer = instance->data_buffer;
502 while (remain_size > 0) {
503 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
504 ? OHCI_TD_MAX_TRANSFER : remain_size;
505
506 td_init(data->tds[td_current], instance->ep->direction,
507 buffer, transfer_size, -1);
508 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
509 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
510 data->tds[td_current]->status, data->tds[td_current]->cbp,
511 data->tds[td_current]->next, data->tds[td_current]->be);
512
513 buffer += transfer_size;
514 remain_size -= transfer_size;
515 assert(td_current < data->td_count);
516 ++td_current;
517 }
518}
519/**
520 * @}
521 */
Note: See TracBrowser for help on using the repository browser.