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
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 "ohci_batch.h"
41#include "ohci_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 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 }
108 free32(instance->device_buffer);
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 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
134 /* We need an extra place for TD that was left at ED */
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 */
140 data->ed = ohci_endpoint_get(batch->ep)->ed;
141 data->tds[0] = ohci_endpoint_get(batch->ep)->td;
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");
160 /* Copy setup data */
161 memcpy(data->device_buffer, batch->setup_buffer,
162 batch->setup_size);
163 batch->data_buffer = data->device_buffer + batch->setup_size;
164 }
165
166 batch->private_data = data;
167 batch->private_data_dtor = ohci_batch_dispose;
168
169 assert(batch_setup[batch->ep->transfer_type][batch->ep->direction]);
170 batch_setup[batch->ep->transfer_type][batch->ep->direction](batch);
171
172 return EOK;
173#undef CHECK_NULL_DISPOSE_RETURN
174}
175/*----------------------------------------------------------------------------*/
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 */
185bool batch_is_complete(usb_transfer_batch_t *instance)
186{
187 assert(instance);
188 ohci_transfer_batch_t *data = instance->private_data;
189 assert(data);
190 usb_log_debug("Batch(%p) checking %zu td(s) for completion.\n",
191 instance, data->td_count);
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);
195 size_t i = 0;
196 instance->transfered_size = instance->buffer_size;
197 for (; i < data->td_count; ++i) {
198 assert(data->tds[i] != NULL);
199 usb_log_debug("TD %zu: %x:%x:%x:%x.\n", i,
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])) {
203 return false;
204 }
205 instance->error = td_error(data->tds[i]);
206 if (instance->error != EOK) {
207 usb_log_debug("Batch(%p) found error TD(%zu):%x.\n",
208 instance, i, data->tds[i]->status);
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;
214 break;
215 }
216 }
217 data->leave_td = i;
218 assert(data->leave_td <= data->td_count);
219
220 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(instance->ep);
221 assert(ohci_ep);
222 ohci_ep->td = data->tds[data->leave_td];
223 assert(i > 0);
224 for (--i;i < data->td_count; ++i)
225 instance->transfered_size -= td_remain_size(data->tds[i]);
226
227 /* Clear possible ED HALT */
228 data->ed->td_head &= ~ED_TDHEAD_HALTED_FLAG;
229 const uint32_t pa = addr_to_phys(ohci_ep->td);
230 assert(pa == (data->ed->td_head & ED_TDHEAD_PTR_MASK));
231 assert(pa == (data->ed->td_tail & ED_TDTAIL_PTR_MASK));
232
233 return true;
234}
235/*----------------------------------------------------------------------------*/
236/** Starts execution of the TD list
237 *
238 * @param[in] instance Batch structure to use
239 */
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/*----------------------------------------------------------------------------*/
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 */
255void batch_control_write(usb_transfer_batch_t *instance)
256{
257 assert(instance);
258 /* We are data out, we are supposed to provide data */
259 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
260 instance->next_step = usb_transfer_batch_call_out_and_dispose;
261 batch_control(instance, USB_DIRECTION_OUT, USB_DIRECTION_IN);
262 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
263}
264/*----------------------------------------------------------------------------*/
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 */
272void batch_control_read(usb_transfer_batch_t *instance)
273{
274 assert(instance);
275 instance->next_step = usb_transfer_batch_call_in_and_dispose;
276 batch_control(instance, USB_DIRECTION_IN, USB_DIRECTION_OUT);
277 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
278}
279/*----------------------------------------------------------------------------*/
280/** Prepare interrupt in transfer.
281 *
282 * @param[in] instance Batch structure to use.
283 *
284 * Data transfer.
285 */
286void batch_interrupt_in(usb_transfer_batch_t *instance)
287{
288 assert(instance);
289 instance->next_step = usb_transfer_batch_call_in_and_dispose;
290 batch_data(instance);
291 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
292}
293/*----------------------------------------------------------------------------*/
294/** Prepare interrupt out transfer.
295 *
296 * @param[in] instance Batch structure to use.
297 *
298 * Data transfer.
299 */
300void batch_interrupt_out(usb_transfer_batch_t *instance)
301{
302 assert(instance);
303 /* We are data out, we are supposed to provide data */
304 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
305 instance->next_step = usb_transfer_batch_call_out_and_dispose;
306 batch_data(instance);
307 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
308}
309/*----------------------------------------------------------------------------*/
310/** Prepare bulk in transfer.
311 *
312 * @param[in] instance Batch structure to use.
313 *
314 * Data transfer.
315 */
316void batch_bulk_in(usb_transfer_batch_t *instance)
317{
318 assert(instance);
319 instance->next_step = usb_transfer_batch_call_in_and_dispose;
320 batch_data(instance);
321 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
322}
323/*----------------------------------------------------------------------------*/
324/** Prepare bulk out transfer.
325 *
326 * @param[in] instance Batch structure to use.
327 *
328 * Data transfer.
329 */
330void batch_bulk_out(usb_transfer_batch_t *instance)
331{
332 assert(instance);
333 /* We are data out, we are supposed to provide data */
334 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
335 instance->next_step = usb_transfer_batch_call_out_and_dispose;
336 batch_data(instance);
337 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
338}
339/*----------------------------------------------------------------------------*/
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 */
350void batch_control(usb_transfer_batch_t *instance,
351 usb_direction_t data_dir, usb_direction_t status_dir)
352{
353 assert(instance);
354 ohci_transfer_batch_t *data = instance->private_data;
355 assert(data);
356 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
357 data->ed->status, data->ed->td_tail, data->ed->td_head,
358 data->ed->next);
359 int toggle = 0;
360 /* setup stage */
361 td_init(data->tds[0], USB_DIRECTION_BOTH, instance->setup_buffer,
362 instance->setup_size, toggle);
363 td_set_next(data->tds[0], data->tds[1]);
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);
367
368 /* data stage */
369 size_t td_current = 1;
370 size_t remain_size = instance->buffer_size;
371 char *buffer = instance->data_buffer;
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
377 td_init(data->tds[td_current], data_dir, buffer,
378 transfer_size, toggle);
379 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
380 usb_log_debug("Created CONTROL DATA TD: %x:%x:%x:%x.\n",
381 data->tds[td_current]->status, data->tds[td_current]->cbp,
382 data->tds[td_current]->next, data->tds[td_current]->be);
383
384 buffer += transfer_size;
385 remain_size -= transfer_size;
386 assert(td_current < data->td_count - 1);
387 ++td_current;
388 }
389
390 /* status stage */
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]);
394 usb_log_debug("Created CONTROL STATUS TD: %x:%x:%x:%x.\n",
395 data->tds[td_current]->status, data->tds[td_current]->cbp,
396 data->tds[td_current]->next, data->tds[td_current]->be);
397}
398/*----------------------------------------------------------------------------*/
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 */
406void batch_data(usb_transfer_batch_t *instance)
407{
408 assert(instance);
409 ohci_transfer_batch_t *data = instance->private_data;
410 assert(data);
411 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
412 data->ed->status, data->ed->td_tail, data->ed->td_head,
413 data->ed->next);
414
415 size_t td_current = 0;
416 size_t remain_size = instance->buffer_size;
417 char *buffer = instance->data_buffer;
418 while (remain_size > 0) {
419 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
420 ? OHCI_TD_MAX_TRANSFER : remain_size;
421
422 td_init(data->tds[td_current], instance->ep->direction,
423 buffer, transfer_size, -1);
424 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
425 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
426 data->tds[td_current]->status, data->tds[td_current]->cbp,
427 data->tds[td_current]->next, data->tds[td_current]->be);
428
429 buffer += transfer_size;
430 remain_size -= transfer_size;
431 assert(td_current < data->td_count);
432 ++td_current;
433 }
434}
435/**
436 * @}
437 */
Note: See TracBrowser for help on using the repository browser.