source: mainline/uspace/lib/usbhost/src/endpoint.c@ 47b2d7e3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 47b2d7e3 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: 7.8 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 libusbhost
31 * @{
32 */
33/** @file
34 * @brief UHCI host controller driver structure
35 */
36
37#include <assert.h>
38#include <atomic.h>
39#include <mem.h>
40#include <stdlib.h>
41#include <str_error.h>
42#include <usb/debug.h>
43#include <usb/descriptor.h>
44#include <usb/host/hcd.h>
45#include <usb/host/utility.h>
46
47#include "usb_transfer_batch.h"
48#include "bus.h"
49
50#include "endpoint.h"
51
52/**
53 * Initialize provided endpoint structure.
54 */
55void endpoint_init(endpoint_t *ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
56{
57 memset(ep, 0, sizeof(endpoint_t));
58
59 assert(dev);
60 ep->device = dev;
61
62 atomic_set(&ep->refcnt, 0);
63 fibril_condvar_initialize(&ep->avail);
64
65 ep->endpoint = USB_ED_GET_EP(desc->endpoint);
66 ep->direction = USB_ED_GET_DIR(desc->endpoint);
67 ep->transfer_type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
68 ep->max_packet_size = USB_ED_GET_MPS(desc->endpoint);
69 ep->packets_per_uframe = USB_ED_GET_ADD_OPPS(desc->endpoint) + 1;
70
71 /** Direction both is our construct never present in descriptors */
72 if (ep->transfer_type == USB_TRANSFER_CONTROL)
73 ep->direction = USB_DIRECTION_BOTH;
74
75 ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
76 ep->transfer_buffer_policy = DMA_POLICY_STRICT;
77 ep->required_transfer_buffer_policy = DMA_POLICY_STRICT;
78}
79
80/**
81 * Get the bus endpoint belongs to.
82 */
83static inline const bus_ops_t *get_bus_ops(endpoint_t *ep)
84{
85 return ep->device->bus->ops;
86}
87
88/**
89 * Increase the reference count on endpoint.
90 */
91void endpoint_add_ref(endpoint_t *ep)
92{
93 atomic_inc(&ep->refcnt);
94}
95
96/**
97 * Call the desctruction callback. Default behavior is to free the memory directly.
98 */
99static inline void endpoint_destroy(endpoint_t *ep)
100{
101 const bus_ops_t *ops = get_bus_ops(ep);
102 if (ops->endpoint_destroy) {
103 ops->endpoint_destroy(ep);
104 } else {
105 assert(ep->active_batch == NULL);
106
107 /* Assume mostly the eps will be allocated by malloc. */
108 free(ep);
109 }
110}
111
112/**
113 * Decrease the reference count.
114 */
115void endpoint_del_ref(endpoint_t *ep)
116{
117 if (atomic_predec(&ep->refcnt) == 0) {
118 endpoint_destroy(ep);
119 }
120}
121
122/**
123 * Mark the endpoint as online. Supply a guard to be used for this endpoint
124 * synchronization.
125 */
126void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
127{
128 ep->guard = guard;
129 ep->online = true;
130}
131
132/**
133 * Mark the endpoint as offline. All other fibrils waiting to activate this
134 * endpoint will be interrupted.
135 */
136void endpoint_set_offline_locked(endpoint_t *ep)
137{
138 assert(ep);
139 assert(fibril_mutex_is_locked(ep->guard));
140
141 ep->online = false;
142 fibril_condvar_broadcast(&ep->avail);
143}
144
145/**
146 * Wait until a transfer finishes. Can be used even when the endpoint is
147 * offline (and is interrupted by the endpoint going offline).
148 */
149void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
150{
151 assert(ep);
152 assert(fibril_mutex_is_locked(ep->guard));
153
154 if (ep->active_batch == NULL)
155 return;
156
157 fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
158}
159
160/**
161 * Mark the endpoint as active and block access for further fibrils. If the
162 * endpoint is already active, it will block on ep->avail condvar.
163 *
164 * Call only under endpoint guard. After you activate the endpoint and release
165 * the guard, you must assume that particular transfer is already
166 * finished/aborted.
167 *
168 * Activation and deactivation is not done by the library to maximize
169 * performance. The HC might want to prepare some memory buffers prior to
170 * interfering with other world.
171 *
172 * @param batch Transfer batch this endpoint is blocked by.
173 */
174int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
175{
176 assert(ep);
177 assert(batch);
178 assert(batch->ep == ep);
179 assert(ep->guard);
180 assert(fibril_mutex_is_locked(ep->guard));
181
182 while (ep->online && ep->active_batch != NULL)
183 fibril_condvar_wait(&ep->avail, ep->guard);
184
185 if (!ep->online)
186 return EINTR;
187
188 assert(ep->active_batch == NULL);
189 ep->active_batch = batch;
190 return EOK;
191}
192
193/**
194 * Mark the endpoint as inactive and allow access for further fibrils.
195 */
196void endpoint_deactivate_locked(endpoint_t *ep)
197{
198 assert(ep);
199 assert(fibril_mutex_is_locked(ep->guard));
200
201 ep->active_batch = NULL;
202 fibril_condvar_signal(&ep->avail);
203}
204
205/**
206 * Initiate a transfer on an endpoint. Creates a transfer batch, checks the
207 * bandwidth requirements and schedules the batch.
208 *
209 * @param endpoint Endpoint for which to send the batch
210 */
211errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req)
212{
213 assert(ep);
214 assert(req);
215
216 if (ep->transfer_type == USB_TRANSFER_CONTROL) {
217 usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name,
218 req->target.address, req->target.endpoint,
219 req->size, ep->max_packet_size,
220 req->setup);
221 } else {
222 usb_log_debug("%s %d:%d %zu/%zuB", req->name,
223 req->target.address, req->target.endpoint,
224 req->size, ep->max_packet_size);
225 }
226
227 device_t * const device = ep->device;
228 if (!device) {
229 usb_log_warning("Endpoint detached");
230 return EAGAIN;
231 }
232
233 const bus_ops_t *ops = device->bus->ops;
234 if (!ops->batch_schedule) {
235 usb_log_error("HCD does not implement scheduler.");
236 return ENOTSUP;
237 }
238
239 size_t size = req->size;
240 /*
241 * Limit transfers with reserved bandwidth to the amount reserved.
242 * OUT transfers are rejected, IN can be just trimmed in advance.
243 */
244 if (size > ep->max_transfer_size &&
245 (ep->transfer_type == USB_TRANSFER_INTERRUPT
246 || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) {
247 if (req->dir == USB_DIRECTION_OUT)
248 return ENOSPC;
249 else
250 size = ep->max_transfer_size;
251 }
252
253 /* Offline devices don't schedule transfers other than on EP0. */
254 if (!device->online && ep->endpoint > 0)
255 return EAGAIN;
256
257 usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
258 if (!batch) {
259 usb_log_error("Failed to create transfer batch.");
260 return ENOMEM;
261 }
262
263 batch->target = req->target;
264 batch->setup.packed = req->setup;
265 batch->dir = req->dir;
266 batch->size = size;
267 batch->offset = req->offset;
268 batch->dma_buffer = req->buffer;
269
270 dma_buffer_acquire(&batch->dma_buffer);
271
272 if (batch->offset != 0) {
273 usb_log_debug("A transfer with nonzero offset requested.");
274 usb_transfer_batch_bounce(batch);
275 }
276
277 if (usb_transfer_batch_bounce_required(batch))
278 usb_transfer_batch_bounce(batch);
279
280 batch->on_complete = req->on_complete;
281 batch->on_complete_data = req->arg;
282
283 const int ret = ops->batch_schedule(batch);
284 if (ret != EOK) {
285 usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
286 usb_transfer_batch_destroy(batch);
287 }
288
289 return ret;
290}
291
292/**
293 * @}
294 */
Note: See TracBrowser for help on using the repository browser.