source: mainline/uspace/drv/bus/usb/uhci/uhci_batch.c@ 3061bc1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3061bc1 was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup drvusbuhcihc
31 * @{
32 */
33/** @file
34 * @brief UHCI driver USB transfer structure
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <macros.h>
40#include <mem.h>
41#include <stdlib.h>
42
43#include <usb/usb.h>
44#include <usb/debug.h>
45#include <usb/host/endpoint.h>
46#include <usb/host/utils/malloc32.h>
47
48#include "uhci_batch.h"
49#include "hc.h"
50#include "hw_struct/transfer_descriptor.h"
51
52#define DEFAULT_ERROR_COUNT 3
53
54/** Transfer batch setup table. */
55static void (*const batch_setup[])(uhci_transfer_batch_t*);
56
57/** Destroys uhci_transfer_batch_t structure.
58 *
59 * @param[in] uhci_batch Instance to destroy.
60 */
61void uhci_transfer_batch_destroy(uhci_transfer_batch_t *uhci_batch)
62{
63 assert(uhci_batch);
64 dma_buffer_free(&uhci_batch->uhci_dma_buffer);
65 free(uhci_batch);
66}
67
68/** Allocate memory and initialize internal data structure.
69 *
70 * @param[in] usb_batch Pointer to generic USB batch structure.
71 * @return Valid pointer if all structures were successfully created,
72 * NULL otherwise.
73 */
74uhci_transfer_batch_t * uhci_transfer_batch_create(endpoint_t *ep)
75{
76 uhci_transfer_batch_t *uhci_batch =
77 calloc(1, sizeof(uhci_transfer_batch_t));
78 if (!uhci_batch) {
79 usb_log_error("Failed to allocate UHCI batch.");
80 return NULL;
81 }
82
83 usb_transfer_batch_init(&uhci_batch->base, ep);
84
85 link_initialize(&uhci_batch->link);
86 return uhci_batch;
87}
88
89/* Prepares batch for commiting.
90 *
91 * Determines the number of needed transfer descriptors (TDs).
92 * Prepares a transport buffer (that is accessible by the hardware).
93 * Initializes parameters needed for the transfer and callback.
94 */
95int uhci_transfer_batch_prepare(uhci_transfer_batch_t *uhci_batch)
96{
97 static_assert((sizeof(td_t) % 16) == 0);
98
99 usb_transfer_batch_t *usb_batch = &uhci_batch->base;
100
101 uhci_batch->td_count = (usb_batch->size + usb_batch->ep->max_packet_size - 1)
102 / usb_batch->ep->max_packet_size;
103
104 if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
105 uhci_batch->td_count += 2;
106 }
107
108 const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL)
109 ? USB_SETUP_PACKET_SIZE
110 : 0;
111
112 const size_t total_size = (sizeof(td_t) * uhci_batch->td_count)
113 + sizeof(qh_t) + setup_size;
114
115 if (dma_buffer_alloc(&uhci_batch->uhci_dma_buffer, total_size)) {
116 usb_log_error("Failed to allocate UHCI buffer.");
117 return ENOMEM;
118 }
119 memset(uhci_batch->uhci_dma_buffer.virt, 0, total_size);
120
121 uhci_batch->tds = uhci_batch->uhci_dma_buffer.virt;
122 uhci_batch->qh = (qh_t *) &uhci_batch->tds[uhci_batch->td_count];
123
124 qh_init(uhci_batch->qh);
125 qh_set_element_td(uhci_batch->qh, &uhci_batch->tds[0]);
126
127 void *setup_buffer = uhci_transfer_batch_setup_buffer(uhci_batch);
128 assert(setup_buffer == (void *) (uhci_batch->qh + 1));
129 /* Copy SETUP packet data to the device buffer */
130 memcpy(setup_buffer, usb_batch->setup.buffer, setup_size);
131
132 usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
133 " memory structures ready.", usb_batch,
134 USB_TRANSFER_BATCH_ARGS(*usb_batch));
135
136 assert(batch_setup[usb_batch->ep->transfer_type]);
137 batch_setup[usb_batch->ep->transfer_type](uhci_batch);
138
139 return EOK;
140}
141
142/** Check batch TDs for activity.
143 *
144 * @param[in] uhci_batch Batch structure to use.
145 * @return False, if there is an active TD, true otherwise.
146 *
147 * Walk all TDs. Stop with false if there is an active one (it is to be
148 * processed). Stop with true if an error is found. Return true if the last TD
149 * is reached.
150 */
151bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *uhci_batch)
152{
153 assert(uhci_batch);
154 usb_transfer_batch_t *batch = &uhci_batch->base;
155
156 usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
157 " checking %zu transfer(s) for completion.",
158 uhci_batch, USB_TRANSFER_BATCH_ARGS(*batch),
159 uhci_batch->td_count);
160 batch->transferred_size = 0;
161
162 uhci_endpoint_t *uhci_ep = (uhci_endpoint_t *) batch->ep;
163
164 for (size_t i = 0;i < uhci_batch->td_count; ++i) {
165 if (td_is_active(&uhci_batch->tds[i])) {
166 return false;
167 }
168
169 batch->error = td_status(&uhci_batch->tds[i]);
170 if (batch->error != EOK) {
171 assert(batch->ep != NULL);
172
173 usb_log_debug("Batch %p found error TD(%zu->%p):%"
174 PRIx32 ".", uhci_batch, i,
175 &uhci_batch->tds[i], uhci_batch->tds[i].status);
176 td_print_status(&uhci_batch->tds[i]);
177
178 uhci_ep->toggle = td_toggle(&uhci_batch->tds[i]);
179 goto substract_ret;
180 }
181
182 batch->transferred_size
183 += td_act_size(&uhci_batch->tds[i]);
184 if (td_is_short(&uhci_batch->tds[i]))
185 goto substract_ret;
186 }
187substract_ret:
188 if (batch->transferred_size > 0 && batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
189 assert(batch->transferred_size >= USB_SETUP_PACKET_SIZE);
190 batch->transferred_size -= USB_SETUP_PACKET_SIZE;
191 }
192
193 assert(batch->transferred_size <= batch->size);
194
195 return true;
196}
197
198/** Direction to pid conversion table */
199static const usb_packet_id direction_pids[] = {
200 [USB_DIRECTION_IN] = USB_PID_IN,
201 [USB_DIRECTION_OUT] = USB_PID_OUT,
202};
203
204/** Prepare generic data transfer
205 *
206 * @param[in] uhci_batch Batch structure to use.
207 * @param[in] dir Communication direction.
208 *
209 * Transactions with alternating toggle bit and supplied pid value.
210 * The last transfer is marked with IOC flag.
211 */
212static void batch_data(uhci_transfer_batch_t *uhci_batch)
213{
214 assert(uhci_batch);
215
216 usb_direction_t dir = uhci_batch->base.dir;
217 assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
218
219
220 const usb_packet_id pid = direction_pids[dir];
221 const bool low_speed =
222 uhci_batch->base.ep->device->speed == USB_SPEED_LOW;
223 const size_t mps = uhci_batch->base.ep->max_packet_size;
224
225 uhci_endpoint_t *uhci_ep = (uhci_endpoint_t *) uhci_batch->base.ep;
226
227 int toggle = uhci_ep->toggle;
228 assert(toggle == 0 || toggle == 1);
229
230 size_t td = 0;
231 size_t remain_size = uhci_batch->base.size;
232 char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
233
234 while (remain_size > 0) {
235 const size_t packet_size = min(remain_size, mps);
236
237 const td_t *next_td = (td + 1 < uhci_batch->td_count)
238 ? &uhci_batch->tds[td + 1] : NULL;
239
240 assert(td < uhci_batch->td_count);
241 td_init(
242 &uhci_batch->tds[td], DEFAULT_ERROR_COUNT, packet_size,
243 toggle, false, low_speed, uhci_batch->base.target, pid, buffer, next_td);
244
245 ++td;
246 toggle = 1 - toggle;
247 buffer += packet_size;
248 remain_size -= packet_size;
249 }
250 td_set_ioc(&uhci_batch->tds[td - 1]);
251 uhci_ep->toggle = toggle;
252 usb_log_debug2(
253 "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.", \
254 uhci_batch,
255 usb_str_transfer_type(uhci_batch->base.ep->transfer_type),
256 usb_str_direction(uhci_batch->base.ep->direction),
257 USB_TRANSFER_BATCH_ARGS(uhci_batch->base));
258}
259
260/** Prepare generic control transfer
261 *
262 * @param[in] uhci_batch Batch structure to use.
263 * @param[in] dir Communication direction.
264 *
265 * Setup stage with toggle 0 and USB_PID_SETUP.
266 * Data stage with alternating toggle and pid determined by the communication
267 * direction.
268 * Status stage with toggle 1 and pid determined by the communication direction.
269 * The last transfer is marked with IOC.
270 */
271static void batch_control(uhci_transfer_batch_t *uhci_batch)
272{
273 assert(uhci_batch);
274
275 usb_direction_t dir = uhci_batch->base.dir;
276 assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
277 assert(uhci_batch->td_count >= 2);
278 static const usb_packet_id status_stage_pids[] = {
279 [USB_DIRECTION_IN] = USB_PID_OUT,
280 [USB_DIRECTION_OUT] = USB_PID_IN,
281 };
282
283 const usb_packet_id data_stage_pid = direction_pids[dir];
284 const usb_packet_id status_stage_pid = status_stage_pids[dir];
285 const bool low_speed =
286 uhci_batch->base.ep->device->speed == USB_SPEED_LOW;
287 const size_t mps = uhci_batch->base.ep->max_packet_size;
288 const usb_target_t target = uhci_batch->base.target;
289
290 /* setup stage */
291 td_init(
292 &uhci_batch->tds[0], DEFAULT_ERROR_COUNT,
293 USB_SETUP_PACKET_SIZE, 0, false,
294 low_speed, target, USB_PID_SETUP,
295 uhci_transfer_batch_setup_buffer(uhci_batch), &uhci_batch->tds[1]);
296
297 /* data stage */
298 size_t td = 1;
299 unsigned toggle = 1;
300 size_t remain_size = uhci_batch->base.size;
301 char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
302
303 while (remain_size > 0) {
304 const size_t packet_size = min(remain_size, mps);
305
306 td_init(
307 &uhci_batch->tds[td], DEFAULT_ERROR_COUNT, packet_size,
308 toggle, false, low_speed, target, data_stage_pid,
309 buffer, &uhci_batch->tds[td + 1]);
310
311 ++td;
312 toggle = 1 - toggle;
313 buffer += packet_size;
314 remain_size -= packet_size;
315 assert(td < uhci_batch->td_count);
316 }
317
318 /* status stage */
319 assert(td == uhci_batch->td_count - 1);
320
321 td_init(
322 &uhci_batch->tds[td], DEFAULT_ERROR_COUNT, 0, 1, false, low_speed,
323 target, status_stage_pid, NULL, NULL);
324 td_set_ioc(&uhci_batch->tds[td]);
325
326 usb_log_debug2("Control last TD status: %x.",
327 uhci_batch->tds[td].status);
328}
329
330static void (*const batch_setup[])(uhci_transfer_batch_t*) =
331{
332 [USB_TRANSFER_CONTROL] = batch_control,
333 [USB_TRANSFER_BULK] = batch_data,
334 [USB_TRANSFER_INTERRUPT] = batch_data,
335 [USB_TRANSFER_ISOCHRONOUS] = NULL,
336};
337/**
338 * @}
339 */
Note: See TracBrowser for help on using the repository browser.