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

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

Track toggle on errors

  • Property mode set to 100644
File size: 12.3 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 usb
29 * @{
30 */
31/** @file
32 * @brief UHCI driver
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 "uhci.h"
43#include "utils/malloc32.h"
44
45#define DEFAULT_ERROR_COUNT 3
46
47static int batch_schedule(batch_t *instance);
48
49static void batch_control(batch_t *instance,
50 usb_packet_id data_stage, usb_packet_id status_stage);
51static void batch_data(batch_t *instance, usb_packet_id pid);
52static void batch_call_in(batch_t *instance);
53static void batch_call_out(batch_t *instance);
54static void batch_call_in_and_dispose(batch_t *instance);
55static void batch_call_out_and_dispose(batch_t *instance);
56static void batch_dispose(batch_t *instance);
57
58
59batch_t * batch_get(ddf_fun_t *fun, usb_target_t target,
60 usb_transfer_type_t transfer_type, size_t max_packet_size,
61 usb_speed_t speed, char *buffer, size_t size,
62 char* setup_buffer, size_t setup_size,
63 usbhc_iface_transfer_in_callback_t func_in,
64 usbhc_iface_transfer_out_callback_t func_out, void *arg,
65 device_keeper_t *manager
66 )
67{
68 assert(func_in == NULL || func_out == NULL);
69 assert(func_in != NULL || func_out != NULL);
70
71 batch_t *instance = malloc(sizeof(batch_t));
72 if (instance == NULL) {
73 usb_log_error("Failed to allocate batch instance.\n");
74 return NULL;
75 }
76
77 instance->qh = queue_head_get();
78 if (instance->qh == NULL) {
79 usb_log_error("Failed to allocate queue head.\n");
80 free(instance);
81 return NULL;
82 }
83
84 instance->packets = (size + max_packet_size - 1) / max_packet_size;
85 if (transfer_type == USB_TRANSFER_CONTROL) {
86 instance->packets += 2;
87 }
88
89 instance->tds = malloc32(sizeof(td_t) * instance->packets);
90 if (instance->tds == NULL) {
91 usb_log_error("Failed to allocate transfer descriptors.\n");
92 queue_head_dispose(instance->qh);
93 free(instance);
94 return NULL;
95 }
96 bzero(instance->tds, sizeof(td_t) * instance->packets);
97
98 const size_t transport_size = max_packet_size * instance->packets;
99
100 instance->transport_buffer =
101 (size > 0) ? malloc32(transport_size) : NULL;
102
103 if ((size > 0) && (instance->transport_buffer == NULL)) {
104 usb_log_error("Failed to allocate device accessible buffer.\n");
105 queue_head_dispose(instance->qh);
106 free32(instance->tds);
107 free(instance);
108 return NULL;
109 }
110
111 instance->setup_buffer = setup_buffer ? malloc32(setup_size) : NULL;
112 if ((setup_size > 0) && (instance->setup_buffer == NULL)) {
113 usb_log_error("Failed to allocate device accessible setup buffer.\n");
114 queue_head_dispose(instance->qh);
115 free32(instance->tds);
116 free32(instance->transport_buffer);
117 free(instance);
118 return NULL;
119 }
120 if (instance->setup_buffer) {
121 memcpy(instance->setup_buffer, setup_buffer, setup_size);
122 }
123
124 instance->max_packet_size = max_packet_size;
125
126 link_initialize(&instance->link);
127
128 instance->target = target;
129 instance->transfer_type = transfer_type;
130
131 if (func_out)
132 instance->callback_out = func_out;
133 if (func_in)
134 instance->callback_in = func_in;
135
136 instance->buffer = buffer;
137 instance->buffer_size = size;
138 instance->setup_size = setup_size;
139 instance->fun = fun;
140 instance->arg = arg;
141 instance->speed = speed;
142 instance->manager = manager;
143
144 queue_head_element_td(instance->qh, addr_to_phys(instance->tds));
145 usb_log_debug("Batch(%p) %d:%d memory structures ready.\n",
146 instance, target.address, target.endpoint);
147 return instance;
148}
149/*----------------------------------------------------------------------------*/
150bool batch_is_complete(batch_t *instance)
151{
152 assert(instance);
153 usb_log_debug2("Batch(%p) checking %d packet(s) for completion.\n",
154 instance, instance->packets);
155 instance->transfered_size = 0;
156 size_t i = 0;
157 for (;i < instance->packets; ++i) {
158 if (td_is_active(&instance->tds[i])) {
159 return false;
160 }
161
162 instance->error = td_status(&instance->tds[i]);
163 if (instance->error != EOK) {
164 usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
165 instance, i, instance->tds[i].status);
166
167 device_keeper_set_toggle(instance->manager, instance->target,
168 td_toggle(&instance->tds[i]));
169 if (i > 0)
170 goto substract_ret;
171 return true;
172 }
173
174 instance->transfered_size += td_act_size(&instance->tds[i]);
175 if (td_is_short(&instance->tds[i]))
176 goto substract_ret;
177 }
178substract_ret:
179 instance->transfered_size -= instance->setup_size;
180 return true;
181}
182/*----------------------------------------------------------------------------*/
183void batch_control_write(batch_t *instance)
184{
185 assert(instance);
186 /* we are data out, we are supposed to provide data */
187 memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
188 batch_control(instance, USB_PID_OUT, USB_PID_IN);
189 instance->next_step = batch_call_out_and_dispose;
190 usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
191 batch_schedule(instance);
192}
193/*----------------------------------------------------------------------------*/
194void batch_control_read(batch_t *instance)
195{
196 assert(instance);
197 batch_control(instance, USB_PID_IN, USB_PID_OUT);
198 instance->next_step = batch_call_in_and_dispose;
199 usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
200 batch_schedule(instance);
201}
202/*----------------------------------------------------------------------------*/
203void batch_interrupt_in(batch_t *instance)
204{
205 assert(instance);
206 batch_data(instance, USB_PID_IN);
207 instance->next_step = batch_call_in_and_dispose;
208 usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
209 batch_schedule(instance);
210}
211/*----------------------------------------------------------------------------*/
212void batch_interrupt_out(batch_t *instance)
213{
214 assert(instance);
215 memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
216 batch_data(instance, USB_PID_OUT);
217 instance->next_step = batch_call_out_and_dispose;
218 usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
219 batch_schedule(instance);
220}
221/*----------------------------------------------------------------------------*/
222void batch_bulk_in(batch_t *instance)
223{
224 assert(instance);
225 batch_data(instance, USB_PID_IN);
226 instance->next_step = batch_call_in_and_dispose;
227 usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
228 batch_schedule(instance);
229}
230/*----------------------------------------------------------------------------*/
231void batch_bulk_out(batch_t *instance)
232{
233 assert(instance);
234 memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
235 batch_data(instance, USB_PID_OUT);
236 instance->next_step = batch_call_out_and_dispose;
237 usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
238 batch_schedule(instance);
239}
240/*----------------------------------------------------------------------------*/
241void batch_data(batch_t *instance, usb_packet_id pid)
242{
243 assert(instance);
244 const bool low_speed = instance->speed == USB_SPEED_LOW;
245 int toggle = device_keeper_get_toggle(instance->manager, instance->target);
246 assert(toggle == 0 || toggle == 1);
247
248 size_t packet = 0;
249 size_t remain_size = instance->buffer_size;
250 while (remain_size > 0) {
251 char *data =
252 instance->transport_buffer + instance->buffer_size
253 - remain_size;
254
255
256 const size_t packet_size =
257 (instance->max_packet_size > remain_size) ?
258 remain_size : instance->max_packet_size;
259
260 td_init(&instance->tds[packet],
261 DEFAULT_ERROR_COUNT, packet_size, toggle, false, low_speed,
262 instance->target, pid, data,
263 &instance->tds[packet + 1]);
264
265 toggle = 1 - toggle;
266 ++packet;
267 assert(packet <= instance->packets);
268 assert(packet_size <= remain_size);
269 remain_size -= packet_size;
270 }
271 device_keeper_set_toggle(instance->manager, instance->target, toggle);
272
273 instance->tds[packet - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
274 instance->tds[packet - 1].next = 0 | LINK_POINTER_TERMINATE_FLAG;
275}
276/*----------------------------------------------------------------------------*/
277void batch_control(batch_t *instance,
278 usb_packet_id data_stage, usb_packet_id status_stage)
279{
280 assert(instance);
281
282 const bool low_speed = instance->speed == USB_SPEED_LOW;
283 int toggle = 0;
284 /* setup stage */
285 td_init(instance->tds, DEFAULT_ERROR_COUNT,
286 instance->setup_size, toggle, false, low_speed, instance->target,
287 USB_PID_SETUP, instance->setup_buffer, &instance->tds[1]);
288
289 /* data stage */
290 size_t packet = 1;
291 size_t remain_size = instance->buffer_size;
292 while (remain_size > 0) {
293 char *data =
294 instance->transport_buffer + instance->buffer_size
295 - remain_size;
296
297 toggle = 1 - toggle;
298
299 const size_t packet_size =
300 (instance->max_packet_size > remain_size) ?
301 remain_size : instance->max_packet_size;
302
303 td_init(&instance->tds[packet],
304 DEFAULT_ERROR_COUNT, packet_size, toggle, false, low_speed,
305 instance->target, data_stage, data,
306 &instance->tds[packet + 1]);
307
308 ++packet;
309 assert(packet < instance->packets);
310 assert(packet_size <= remain_size);
311 remain_size -= packet_size;
312 }
313
314 /* status stage */
315 assert(packet == instance->packets - 1);
316 td_init(&instance->tds[packet], DEFAULT_ERROR_COUNT,
317 0, 1, false, low_speed, instance->target, status_stage, NULL, NULL);
318
319
320 instance->tds[packet].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
321 usb_log_debug2("Control last TD status: %x.\n",
322 instance->tds[packet].status);
323}
324/*----------------------------------------------------------------------------*/
325void batch_call_in(batch_t *instance)
326{
327 assert(instance);
328 assert(instance->callback_in);
329
330 memcpy(instance->buffer, instance->transport_buffer, instance->buffer_size);
331
332 int err = instance->error;
333 usb_log_debug("Batch(%p) callback IN(type:%d): %s(%d), %zu.\n",
334 instance, instance->transfer_type, str_error(err), err,
335 instance->transfered_size);
336
337 instance->callback_in(instance->fun,
338 err, instance->transfered_size,
339 instance->arg);
340}
341/*----------------------------------------------------------------------------*/
342void batch_call_out(batch_t *instance)
343{
344 assert(instance);
345 assert(instance->callback_out);
346
347 int err = instance->error;
348 usb_log_debug("Batch(%p) callback OUT(type:%d): %s(%d).\n",
349 instance, instance->transfer_type, str_error(err), err);
350 instance->callback_out(instance->fun,
351 err, instance->arg);
352}
353/*----------------------------------------------------------------------------*/
354void batch_call_in_and_dispose(batch_t *instance)
355{
356 assert(instance);
357 batch_call_in(instance);
358 batch_dispose(instance);
359}
360/*----------------------------------------------------------------------------*/
361void batch_call_out_and_dispose(batch_t *instance)
362{
363 assert(instance);
364 batch_call_out(instance);
365 batch_dispose(instance);
366}
367/*----------------------------------------------------------------------------*/
368void batch_dispose(batch_t *instance)
369{
370 assert(instance);
371 usb_log_debug("Batch(%p) disposing.\n", instance);
372 free32(instance->tds);
373 free32(instance->qh);
374 free32(instance->setup_buffer);
375 free32(instance->transport_buffer);
376 free(instance);
377}
378/*----------------------------------------------------------------------------*/
379int batch_schedule(batch_t *instance)
380{
381 assert(instance);
382 uhci_t *hc = fun_to_uhci(instance->fun);
383 assert(hc);
384 return uhci_schedule(hc, instance);
385}
386/**
387 * @}
388 */
Note: See TracBrowser for help on using the repository browser.