source: mainline/uspace/drv/ohci/batch.c@ 8bb61e6

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

Doxygen and some minor code-style changes

  • Property mode set to 100644
File size: 15.1 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
46/** OHCI specific data required for USB transfer */
47typedef struct ohci_transfer_batch {
48 /** Endpoint descriptor of the target endpoint. */
49 ed_t *ed;
50 /** List of TDs needed for the transfer */
51 td_t **tds;
52 /** Number of TDs used by the transfer */
53 size_t td_count;
54 /** Dummy TD to be left at the ED and used by the next transfer */
55 size_t leave_td;
56 /** Data buffer, must be accessible byb the OHCI hw. */
57 void *device_buffer;
58} ohci_transfer_batch_t;
59/*----------------------------------------------------------------------------*/
60static void batch_control(usb_transfer_batch_t *instance,
61 usb_direction_t data_dir, usb_direction_t status_dir);
62static void batch_data(usb_transfer_batch_t *instance);
63/*----------------------------------------------------------------------------*/
64/** Safely destructs ohci_transfer_batch_t structure
65 *
66 * @param[in] ohci_batch Instance to destroy.
67 */
68static void ohci_transfer_batch_dispose(void *ohci_batch)
69{
70 ohci_transfer_batch_t *instance = ohci_batch;
71 if (!instance)
72 return;
73 free32(instance->device_buffer);
74 unsigned i = 0;
75 if (instance->tds) {
76 for (; i< instance->td_count; ++i) {
77 if (i != instance->leave_td)
78 free32(instance->tds[i]);
79 }
80 free(instance->tds);
81 }
82 free(instance);
83}
84/*----------------------------------------------------------------------------*/
85/** Allocate memory initialize internal structures
86 *
87 * @param[in] fun DDF function to pass to callback.
88 * @param[in] ep Communication target
89 * @param[in] buffer Data source/destination.
90 * @param[in] buffer_size Size of the buffer.
91 * @param[in] setup_buffer Setup data source (if not NULL)
92 * @param[in] setup_size Size of setup_buffer (should be always 8)
93 * @param[in] func_in function to call on inbound transfer completion
94 * @param[in] func_out function to call on outbound transfer completion
95 * @param[in] arg additional parameter to func_in or func_out
96 * @return Valid pointer if all structures were successfully created,
97 * NULL otherwise.
98 *
99 * Allocates and initializes structures needed by the OHCI hw for the transfer.
100 */
101usb_transfer_batch_t * batch_get(ddf_fun_t *fun, endpoint_t *ep,
102 char *buffer, size_t buffer_size,
103 const char *setup_buffer, size_t setup_size,
104 usbhc_iface_transfer_in_callback_t func_in,
105 usbhc_iface_transfer_out_callback_t func_out, void *arg)
106{
107#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
108 if (ptr == NULL) { \
109 usb_log_error(message); \
110 if (instance) { \
111 usb_transfer_batch_dispose(instance); \
112 } \
113 return NULL; \
114 } else (void)0
115
116 usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
117 CHECK_NULL_DISPOSE_RETURN(instance,
118 "Failed to allocate batch instance.\n");
119 usb_transfer_batch_init(instance, ep, buffer, NULL, buffer_size,
120 NULL, setup_size, func_in, func_out, arg, fun, NULL,
121 ohci_transfer_batch_dispose);
122
123 const hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
124 assert(hcd_ep);
125
126 ohci_transfer_batch_t *data = calloc(sizeof(ohci_transfer_batch_t), 1);
127 CHECK_NULL_DISPOSE_RETURN(data, "Failed to allocate batch data.\n");
128 instance->private_data = data;
129
130 data->td_count =
131 ((buffer_size + OHCI_TD_MAX_TRANSFER - 1) / OHCI_TD_MAX_TRANSFER);
132 /* Control transfer need Setup and Status stage */
133 if (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
154 /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
155 * it is, however, not capable of handling buffer that occupies more
156 * than two pages (the first page is computed using start pointer, the
157 * other using the end pointer) */
158 if (setup_size + buffer_size > 0) {
159 data->device_buffer = malloc32(setup_size + buffer_size);
160 CHECK_NULL_DISPOSE_RETURN(data->device_buffer,
161 "Failed to allocate device accessible buffer.\n");
162 instance->setup_buffer = data->device_buffer;
163 instance->data_buffer = data->device_buffer + setup_size;
164 memcpy(instance->setup_buffer, setup_buffer, setup_size);
165 }
166
167 return instance;
168}
169/*----------------------------------------------------------------------------*/
170/** Check batch TDs' status.
171 *
172 * @param[in] instance Batch structure to use.
173 * @return False, if there is an active TD, true otherwise.
174 *
175 * Walk all TDs (usually there is just one). Stop with false if there is an
176 * active TD. Stop with true if an error is found. Return true if the walk
177 * completes with the last TD.
178 */
179bool batch_is_complete(usb_transfer_batch_t *instance)
180{
181 assert(instance);
182 ohci_transfer_batch_t *data = instance->private_data;
183 assert(data);
184 usb_log_debug("Batch(%p) checking %zu td(s) for completion.\n",
185 instance, data->td_count);
186 usb_log_debug("ED: %x:%x:%x:%x.\n",
187 data->ed->status, data->ed->td_head, data->ed->td_tail,
188 data->ed->next);
189 size_t i = 0;
190 instance->transfered_size = instance->buffer_size;
191 for (; i < data->td_count; ++i) {
192 assert(data->tds[i] != NULL);
193 usb_log_debug("TD %zu: %x:%x:%x:%x.\n", i,
194 data->tds[i]->status, data->tds[i]->cbp, data->tds[i]->next,
195 data->tds[i]->be);
196 if (!td_is_finished(data->tds[i])) {
197 return false;
198 }
199 instance->error = td_error(data->tds[i]);
200 if (instance->error != EOK) {
201 usb_log_debug("Batch(%p) found error TD(%zu):%x.\n",
202 instance, i, data->tds[i]->status);
203 /* Make sure TD queue is empty (one TD),
204 * ED should be marked as halted */
205 data->ed->td_tail =
206 (data->ed->td_head & ED_TDTAIL_PTR_MASK);
207 ++i;
208 break;
209 }
210 }
211 data->leave_td = i;
212 assert(data->leave_td <= data->td_count);
213 hcd_endpoint_t *hcd_ep = hcd_endpoint_get(instance->ep);
214 assert(hcd_ep);
215 hcd_ep->td = data->tds[i];
216 assert(i > 0);
217 for (--i;i < data->td_count; ++i)
218 instance->transfered_size -= td_remain_size(data->tds[i]);
219
220 /* Clear possible ED HALT */
221 data->ed->td_head &= ~ED_TDHEAD_HALTED_FLAG;
222 const uint32_t pa = addr_to_phys(hcd_ep->td);
223 assert(pa == (data->ed->td_head & ED_TDHEAD_PTR_MASK));
224 assert(pa == (data->ed->td_tail & ED_TDTAIL_PTR_MASK));
225
226 return true;
227}
228/*----------------------------------------------------------------------------*/
229/** Starts execution of the TD list
230 *
231 * @param[in] instance Batch structure to use
232 */
233void batch_commit(usb_transfer_batch_t *instance)
234{
235 assert(instance);
236 ohci_transfer_batch_t *data = instance->private_data;
237 assert(data);
238 ed_set_end_td(data->ed, data->tds[data->td_count]);
239}
240/*----------------------------------------------------------------------------*/
241/** Prepares control write transfer.
242 *
243 * @param[in] instance Batch structure to use.
244 *
245 * Uses generic control transfer using direction OUT(data stage) and
246 * IN(status stage).
247 */
248void batch_control_write(usb_transfer_batch_t *instance)
249{
250 assert(instance);
251 /* We are data out, we are supposed to provide data */
252 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
253 instance->next_step = usb_transfer_batch_call_out_and_dispose;
254 batch_control(instance, USB_DIRECTION_OUT, USB_DIRECTION_IN);
255 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
256}
257/*----------------------------------------------------------------------------*/
258/** Prepares control read transfer.
259 *
260 * @param[in] instance Batch structure to use.
261 *
262 * Uses generic control transfer using direction IN(data stage) and
263 * OUT(status stage).
264 */
265void batch_control_read(usb_transfer_batch_t *instance)
266{
267 assert(instance);
268 instance->next_step = usb_transfer_batch_call_in_and_dispose;
269 batch_control(instance, USB_DIRECTION_IN, USB_DIRECTION_OUT);
270 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
271}
272/*----------------------------------------------------------------------------*/
273/** Prepare interrupt in transfer.
274 *
275 * @param[in] instance Batch structure to use.
276 *
277 * Data transfer.
278 */
279void batch_interrupt_in(usb_transfer_batch_t *instance)
280{
281 assert(instance);
282 instance->next_step = usb_transfer_batch_call_in_and_dispose;
283 batch_data(instance);
284 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
285}
286/*----------------------------------------------------------------------------*/
287/** Prepare interrupt out transfer.
288 *
289 * @param[in] instance Batch structure to use.
290 *
291 * Data transfer.
292 */
293void batch_interrupt_out(usb_transfer_batch_t *instance)
294{
295 assert(instance);
296 /* We are data out, we are supposed to provide data */
297 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
298 instance->next_step = usb_transfer_batch_call_out_and_dispose;
299 batch_data(instance);
300 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
301}
302/*----------------------------------------------------------------------------*/
303/** Prepare bulk in transfer.
304 *
305 * @param[in] instance Batch structure to use.
306 *
307 * Data transfer.
308 */
309void batch_bulk_in(usb_transfer_batch_t *instance)
310{
311 assert(instance);
312 instance->next_step = usb_transfer_batch_call_in_and_dispose;
313 batch_data(instance);
314 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
315}
316/*----------------------------------------------------------------------------*/
317/** Prepare bulk out transfer.
318 *
319 * @param[in] instance Batch structure to use.
320 *
321 * Data transfer.
322 */
323void batch_bulk_out(usb_transfer_batch_t *instance)
324{
325 assert(instance);
326 /* We are data out, we are supposed to provide data */
327 memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
328 instance->next_step = usb_transfer_batch_call_out_and_dispose;
329 batch_data(instance);
330 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
331}
332/*----------------------------------------------------------------------------*/
333/** Prepare generic control transfer
334 *
335 * @param[in] instance Batch structure to use.
336 * @param[in] data_dir Direction to use for data stage.
337 * @param[in] status_dir Direction to use for status stage.
338 *
339 * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
340 * Data stage with alternating toggle and direction supplied by parameter.
341 * Status stage with toggle 1 and direction supplied by parameter.
342 */
343void batch_control(usb_transfer_batch_t *instance,
344 usb_direction_t data_dir, usb_direction_t status_dir)
345{
346 assert(instance);
347 ohci_transfer_batch_t *data = instance->private_data;
348 assert(data);
349 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
350 data->ed->status, data->ed->td_tail, data->ed->td_head,
351 data->ed->next);
352 int toggle = 0;
353 /* setup stage */
354 td_init(data->tds[0], USB_DIRECTION_BOTH, instance->setup_buffer,
355 instance->setup_size, toggle);
356 td_set_next(data->tds[0], data->tds[1]);
357 usb_log_debug("Created SETUP TD: %x:%x:%x:%x.\n", data->tds[0]->status,
358 data->tds[0]->cbp, data->tds[0]->next, data->tds[0]->be);
359
360 /* data stage */
361 size_t td_current = 1;
362 size_t remain_size = instance->buffer_size;
363 char *buffer = instance->data_buffer;
364 while (remain_size > 0) {
365 size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER ?
366 OHCI_TD_MAX_TRANSFER : remain_size;
367 toggle = 1 - toggle;
368
369 td_init(data->tds[td_current], data_dir, buffer,
370 transfer_size, toggle);
371 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
372 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
373 data->tds[td_current]->status, data->tds[td_current]->cbp,
374 data->tds[td_current]->next, data->tds[td_current]->be);
375
376 buffer += transfer_size;
377 remain_size -= transfer_size;
378 assert(td_current < data->td_count - 1);
379 ++td_current;
380 }
381
382 /* status stage */
383 assert(td_current == data->td_count - 1);
384 td_init(data->tds[td_current], status_dir, NULL, 0, 1);
385 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
386 usb_log_debug("Created STATUS TD: %x:%x:%x:%x.\n",
387 data->tds[td_current]->status, data->tds[td_current]->cbp,
388 data->tds[td_current]->next, data->tds[td_current]->be);
389}
390/*----------------------------------------------------------------------------*/
391/** Prepare generic data transfer
392 *
393 * @param[in] instance Batch structure to use.
394 *
395 * Direction is supplied by the associated ep and toggle is maintained by the
396 * OHCI hw in ED.
397 */
398void batch_data(usb_transfer_batch_t *instance)
399{
400 assert(instance);
401 ohci_transfer_batch_t *data = instance->private_data;
402 assert(data);
403 usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
404 data->ed->status, data->ed->td_tail, data->ed->td_head,
405 data->ed->next);
406
407 size_t td_current = 0;
408 size_t remain_size = instance->buffer_size;
409 char *buffer = instance->data_buffer;
410 while (remain_size > 0) {
411 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
412 ? OHCI_TD_MAX_TRANSFER : remain_size;
413
414 td_init(data->tds[td_current], instance->ep->direction,
415 buffer, transfer_size, -1);
416 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
417 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
418 data->tds[td_current]->status, data->tds[td_current]->cbp,
419 data->tds[td_current]->next, data->tds[td_current]->be);
420
421 buffer += transfer_size;
422 remain_size -= transfer_size;
423 assert(td_current < data->td_count);
424 ++td_current;
425 }
426}
427/**
428 * @}
429 */
Note: See TracBrowser for help on using the repository browser.