source: mainline/uspace/drv/uhci-hcd/batch.c@ 87305bb

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

Drop usage of deprecated deice-keeper_t toggle interface

  • Property mode set to 100644
File size: 14.9 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 drvusbuhcihc
29 * @{
30 */
31/** @file
32 * @brief UHCI 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 "transfer_list.h"
42#include "hw_struct/transfer_descriptor.h"
43#include "utils/malloc32.h"
44
45#define DEFAULT_ERROR_COUNT 3
46
47typedef struct uhci_batch {
48 qh_t *qh;
49 td_t *tds;
50 size_t transfers;
51} uhci_batch_t;
52
53static void batch_control(usb_transfer_batch_t *instance,
54 usb_packet_id data_stage, usb_packet_id status_stage);
55static void batch_data(usb_transfer_batch_t *instance, usb_packet_id pid);
56static void batch_call_in_and_dispose(usb_transfer_batch_t *instance);
57static void batch_call_out_and_dispose(usb_transfer_batch_t *instance);
58
59
60/** Allocate memory and initialize internal data structure.
61 *
62 * @param[in] fun DDF function to pass to callback.
63 * @param[in] target Device and endpoint target of the transaction.
64 * @param[in] transfer_type Interrupt, Control or Bulk.
65 * @param[in] max_packet_size maximum allowed size of data transfers.
66 * @param[in] speed Speed of the transaction.
67 * @param[in] buffer Data source/destination.
68 * @param[in] size Size of the buffer.
69 * @param[in] setup_buffer Setup data source (if not NULL)
70 * @param[in] setup_size Size of setup_buffer (should be always 8)
71 * @param[in] func_in function to call on inbound transaction completion
72 * @param[in] func_out function to call on outbound transaction completion
73 * @param[in] arg additional parameter to func_in or func_out
74 * @param[in] ep Pointer to endpoint toggle management structure.
75 * @return Valid pointer if all substructures were successfully created,
76 * NULL otherwise.
77 *
78 * Determines the number of needed transfers (TDs). Prepares a transport buffer
79 * (that is accessible by the hardware). Initializes parameters needed for the
80 * transaction and callback.
81 */
82usb_transfer_batch_t * batch_get(ddf_fun_t *fun, usb_target_t target,
83 usb_transfer_type_t transfer_type, size_t max_packet_size,
84 usb_speed_t speed, char *buffer, size_t buffer_size,
85 char* setup_buffer, size_t setup_size,
86 usbhc_iface_transfer_in_callback_t func_in,
87 usbhc_iface_transfer_out_callback_t func_out, void *arg, endpoint_t *ep
88 )
89{
90 assert(func_in == NULL || func_out == NULL);
91 assert(func_in != NULL || func_out != NULL);
92
93#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
94 if (ptr == NULL) { \
95 usb_log_error(message); \
96 if (instance) { \
97 batch_dispose(instance); \
98 } \
99 return NULL; \
100 } else (void)0
101
102 usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
103 CHECK_NULL_DISPOSE_RETURN(instance,
104 "Failed to allocate batch instance.\n");
105 usb_transfer_batch_init(instance, target,
106 transfer_type, speed, max_packet_size,
107 buffer, NULL, buffer_size, NULL, setup_size, func_in,
108 func_out, arg, fun, ep, NULL);
109
110
111 uhci_batch_t *data = malloc(sizeof(uhci_batch_t));
112 CHECK_NULL_DISPOSE_RETURN(instance,
113 "Failed to allocate batch instance.\n");
114 bzero(data, sizeof(uhci_batch_t));
115 instance->private_data = data;
116
117 data->transfers = (buffer_size + max_packet_size - 1) / max_packet_size;
118 if (transfer_type == USB_TRANSFER_CONTROL) {
119 data->transfers += 2;
120 }
121
122 data->tds = malloc32(sizeof(td_t) * data->transfers);
123 CHECK_NULL_DISPOSE_RETURN(
124 data->tds, "Failed to allocate transfer descriptors.\n");
125 bzero(data->tds, sizeof(td_t) * data->transfers);
126
127 data->qh = malloc32(sizeof(qh_t));
128 CHECK_NULL_DISPOSE_RETURN(data->qh,
129 "Failed to allocate batch queue head.\n");
130 qh_init(data->qh);
131 qh_set_element_td(data->qh, addr_to_phys(data->tds));
132
133 if (buffer_size > 0) {
134 instance->transport_buffer = malloc32(buffer_size);
135 CHECK_NULL_DISPOSE_RETURN(instance->transport_buffer,
136 "Failed to allocate device accessible buffer.\n");
137 }
138
139 if (setup_size > 0) {
140 instance->setup_buffer = malloc32(setup_size);
141 CHECK_NULL_DISPOSE_RETURN(instance->setup_buffer,
142 "Failed to allocate device accessible setup buffer.\n");
143 memcpy(instance->setup_buffer, setup_buffer, setup_size);
144 }
145
146 usb_log_debug("Batch(%p) %d:%d memory structures ready.\n",
147 instance, target.address, target.endpoint);
148 return instance;
149}
150/*----------------------------------------------------------------------------*/
151/** Check batch TDs for activity.
152 *
153 * @param[in] instance Batch structure to use.
154 * @return False, if there is an active TD, true otherwise.
155 *
156 * Walk all TDs. Stop with false if there is an active one (it is to be
157 * processed). Stop with true if an error is found. Return true if the last TS
158 * is reached.
159 */
160bool batch_is_complete(usb_transfer_batch_t *instance)
161{
162 assert(instance);
163 uhci_batch_t *data = instance->private_data;
164 assert(data);
165
166 usb_log_debug2("Batch(%p) checking %d transfer(s) for completion.\n",
167 instance, data->transfers);
168 instance->transfered_size = 0;
169 size_t i = 0;
170 for (;i < data->transfers; ++i) {
171 if (td_is_active(&data->tds[i])) {
172 return false;
173 }
174
175 instance->error = td_status(&data->tds[i]);
176 if (instance->error != EOK) {
177 usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
178 instance, i, data->tds[i].status);
179 td_print_status(&data->tds[i]);
180 if (instance->ep != NULL)
181 endpoint_toggle_set(instance->ep,
182 td_toggle(&data->tds[i]));
183 if (i > 0)
184 goto substract_ret;
185 return true;
186 }
187
188 instance->transfered_size += td_act_size(&data->tds[i]);
189 if (td_is_short(&data->tds[i]))
190 goto substract_ret;
191 }
192substract_ret:
193 instance->transfered_size -= instance->setup_size;
194 return true;
195}
196/*----------------------------------------------------------------------------*/
197/** Prepares control write transaction.
198 *
199 * @param[in] instance Batch structure to use.
200 *
201 * Uses genercir control function with pids OUT and IN.
202 */
203void batch_control_write(usb_transfer_batch_t *instance)
204{
205 assert(instance);
206 /* We are data out, we are supposed to provide data */
207 memcpy(instance->transport_buffer, instance->buffer,
208 instance->buffer_size);
209 batch_control(instance, USB_PID_OUT, USB_PID_IN);
210 instance->next_step = batch_call_out_and_dispose;
211 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
212}
213/*----------------------------------------------------------------------------*/
214/** Prepares control read transaction.
215 *
216 * @param[in] instance Batch structure to use.
217 *
218 * Uses generic control with pids IN and OUT.
219 */
220void batch_control_read(usb_transfer_batch_t *instance)
221{
222 assert(instance);
223 batch_control(instance, USB_PID_IN, USB_PID_OUT);
224 instance->next_step = batch_call_in_and_dispose;
225 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
226}
227/*----------------------------------------------------------------------------*/
228/** Prepare interrupt in transaction.
229 *
230 * @param[in] instance Batch structure to use.
231 *
232 * Data transaction with PID_IN.
233 */
234void batch_interrupt_in(usb_transfer_batch_t *instance)
235{
236 assert(instance);
237 instance->direction = USB_DIRECTION_IN;
238 batch_data(instance, USB_PID_IN);
239 instance->next_step = batch_call_in_and_dispose;
240 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
241}
242/*----------------------------------------------------------------------------*/
243/** Prepare interrupt out transaction.
244 *
245 * @param[in] instance Batch structure to use.
246 *
247 * Data transaction with PID_OUT.
248 */
249void batch_interrupt_out(usb_transfer_batch_t *instance)
250{
251 assert(instance);
252 instance->direction = USB_DIRECTION_OUT;
253 /* We are data out, we are supposed to provide data */
254 memcpy(instance->transport_buffer, instance->buffer,
255 instance->buffer_size);
256 batch_data(instance, USB_PID_OUT);
257 instance->next_step = batch_call_out_and_dispose;
258 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
259}
260/*----------------------------------------------------------------------------*/
261/** Prepare bulk in transaction.
262 *
263 * @param[in] instance Batch structure to use.
264 *
265 * Data transaction with PID_IN.
266 */
267void batch_bulk_in(usb_transfer_batch_t *instance)
268{
269 assert(instance);
270 batch_data(instance, USB_PID_IN);
271 instance->direction = USB_DIRECTION_IN;
272 instance->next_step = batch_call_in_and_dispose;
273 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
274}
275/*----------------------------------------------------------------------------*/
276/** Prepare bulk out transaction.
277 *
278 * @param[in] instance Batch structure to use.
279 *
280 * Data transaction with PID_OUT.
281 */
282void batch_bulk_out(usb_transfer_batch_t *instance)
283{
284 assert(instance);
285 instance->direction = USB_DIRECTION_OUT;
286 /* We are data out, we are supposed to provide data */
287 memcpy(instance->transport_buffer, instance->buffer,
288 instance->buffer_size);
289 batch_data(instance, USB_PID_OUT);
290 instance->next_step = batch_call_out_and_dispose;
291 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
292}
293/*----------------------------------------------------------------------------*/
294/** Prepare generic data transaction
295 *
296 * @param[in] instance Batch structure to use.
297 * @param[in] pid Pid to use for data transfers.
298 *
299 * Packets with alternating toggle bit and supplied pid value.
300 * The last transfer is marked with IOC flag.
301 */
302void batch_data(usb_transfer_batch_t *instance, usb_packet_id pid)
303{
304 assert(instance);
305 uhci_batch_t *data = instance->private_data;
306 assert(data);
307
308 const bool low_speed = instance->speed == USB_SPEED_LOW;
309 int toggle = endpoint_toggle_get(instance->ep);
310 assert(toggle == 0 || toggle == 1);
311
312 size_t transfer = 0;
313 size_t remain_size = instance->buffer_size;
314 while (remain_size > 0) {
315 char *trans_data =
316 instance->transport_buffer + instance->buffer_size
317 - remain_size;
318
319 const size_t packet_size =
320 (instance->max_packet_size > remain_size) ?
321 remain_size : instance->max_packet_size;
322
323 td_t *next_transfer = (transfer + 1 < data->transfers)
324 ? &data->tds[transfer + 1] : NULL;
325
326 assert(transfer < data->transfers);
327 assert(packet_size <= remain_size);
328
329 td_init(
330 &data->tds[transfer], DEFAULT_ERROR_COUNT, packet_size,
331 toggle, false, low_speed, instance->target, pid, trans_data,
332 next_transfer);
333
334
335 toggle = 1 - toggle;
336 remain_size -= packet_size;
337 ++transfer;
338 }
339 td_set_ioc(&data->tds[transfer - 1]);
340 endpoint_toggle_set(instance->ep, toggle);
341}
342/*----------------------------------------------------------------------------*/
343/** Prepare generic control transaction
344 *
345 * @param[in] instance Batch structure to use.
346 * @param[in] data_stage Pid to use for data transfers.
347 * @param[in] status_stage Pid to use for data transfers.
348 *
349 * Setup stage with toggle 0 and USB_PID_SETUP.
350 * Data stage with alternating toggle and pid supplied by parameter.
351 * Status stage with toggle 1 and pid supplied by parameter.
352 * The last transfer is marked with IOC.
353 */
354void batch_control(usb_transfer_batch_t *instance,
355 usb_packet_id data_stage, usb_packet_id status_stage)
356{
357 assert(instance);
358 uhci_batch_t *data = instance->private_data;
359 assert(data);
360 assert(data->transfers >= 2);
361
362 const bool low_speed = instance->speed == USB_SPEED_LOW;
363 int toggle = 0;
364 /* setup stage */
365 td_init(
366 data->tds, DEFAULT_ERROR_COUNT, instance->setup_size, toggle, false,
367 low_speed, instance->target, USB_PID_SETUP, instance->setup_buffer,
368 &data->tds[1]);
369
370 /* data stage */
371 size_t transfer = 1;
372 size_t remain_size = instance->buffer_size;
373 while (remain_size > 0) {
374 char *control_data =
375 instance->transport_buffer + instance->buffer_size
376 - remain_size;
377
378 toggle = 1 - toggle;
379
380 const size_t packet_size =
381 (instance->max_packet_size > remain_size) ?
382 remain_size : instance->max_packet_size;
383
384 td_init(
385 &data->tds[transfer], DEFAULT_ERROR_COUNT, packet_size,
386 toggle, false, low_speed, instance->target, data_stage,
387 control_data, &data->tds[transfer + 1]);
388
389 ++transfer;
390 assert(transfer < data->transfers);
391 assert(packet_size <= remain_size);
392 remain_size -= packet_size;
393 }
394
395 /* status stage */
396 assert(transfer == data->transfers - 1);
397
398 td_init(
399 &data->tds[transfer], DEFAULT_ERROR_COUNT, 0, 1, false, low_speed,
400 instance->target, status_stage, NULL, NULL);
401 td_set_ioc(&data->tds[transfer]);
402
403 usb_log_debug2("Control last TD status: %x.\n",
404 data->tds[transfer].status);
405}
406/*----------------------------------------------------------------------------*/
407qh_t * batch_qh(usb_transfer_batch_t *instance)
408{
409 assert(instance);
410 uhci_batch_t *data = instance->private_data;
411 assert(data);
412 return data->qh;
413}
414/*----------------------------------------------------------------------------*/
415/** Helper function calls callback and correctly disposes of batch structure.
416 *
417 * @param[in] instance Batch structure to use.
418 */
419void batch_call_in_and_dispose(usb_transfer_batch_t *instance)
420{
421 assert(instance);
422 usb_transfer_batch_call_in(instance);
423 batch_dispose(instance);
424}
425/*----------------------------------------------------------------------------*/
426/** Helper function calls callback and correctly disposes of batch structure.
427 *
428 * @param[in] instance Batch structure to use.
429 */
430void batch_call_out_and_dispose(usb_transfer_batch_t *instance)
431{
432 assert(instance);
433 usb_transfer_batch_call_out(instance);
434 batch_dispose(instance);
435}
436/*----------------------------------------------------------------------------*/
437/** Correctly dispose all used data structures.
438 *
439 * @param[in] instance Batch structure to use.
440 */
441void batch_dispose(usb_transfer_batch_t *instance)
442{
443 assert(instance);
444 uhci_batch_t *data = instance->private_data;
445 assert(data);
446 usb_log_debug("Batch(%p) disposing.\n", instance);
447 /* free32 is NULL safe */
448 free32(data->tds);
449 free32(data->qh);
450 free32(instance->setup_buffer);
451 free32(instance->transport_buffer);
452 free(data);
453 free(instance);
454}
455/**
456 * @}
457 */
Note: See TracBrowser for help on using the repository browser.