source: mainline/uspace/drv/uhci_hcd/batch.c@ c4fb5ecd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c4fb5ecd was c4fb5ecd, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Less logging in USB host controller drivers

Many of the debug messages were degraded to debug2 level or
completely removed. Also added formatting string for unified
dump of USB transfer batches.

Warnings in EHCI stub use debug level now.

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