source: mainline/uspace/drv/bus/usb/uhci/batch.c@ 5fe0a697

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

UHCI: Use new usb hc driver architecture.

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