source: mainline/uspace/lib/usbhost/src/usb_endpoint_manager.c@ d8cdf39e

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

libusbhost: Use callback wrapper for toggle handling.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights eps.
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 libusbhost
29 * @{
30 */
31/** @file
32 * HC Endpoint management.
33 */
34
35#include <stdbool.h>
36#include <assert.h>
37#include <errno.h>
38
39#include <usb/debug.h>
40#include <usb/host/usb_endpoint_manager.h>
41
42/** Endpoint compare helper function.
43 *
44 * USB_DIRECTION_BOTH matches both IN and OUT.
45 * @param ep Endpoint to compare, non-null.
46 * @param address Tested address.
47 * @param endpoint Tested endpoint number.
48 * @param direction Tested direction.
49 * @return True if ep can be used to communicate with given device,
50 * false otherwise.
51 */
52static inline bool ep_match(const endpoint_t *ep,
53 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
54{
55 assert(ep);
56 return
57 ((direction == ep->direction)
58 || (ep->direction == USB_DIRECTION_BOTH)
59 || (direction == USB_DIRECTION_BOTH))
60 && (endpoint == ep->endpoint)
61 && (address == ep->address);
62}
63
64/** Get list that holds endpoints for given address.
65 * @param instance usb_endpoint_manager structure, non-null.
66 * @param addr USB address, must be >= 0.
67 * @return Pointer to the appropriate list.
68 */
69static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
70{
71 assert(instance);
72 assert(addr >= 0);
73 return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
74}
75
76/** Internal search function, works on locked structure.
77 * @param instance usb_endpoint_manager structure, non-null.
78 * @param address USB address, must be valid.
79 * @param endpoint USB endpoint number.
80 * @param direction Communication direction.
81 * @return Pointer to endpoint_t structure representing given communication
82 * target, NULL if there is no such endpoint registered.
83 * @note Assumes that the internal mutex is locked.
84 */
85static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
86 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
87{
88 assert(instance);
89 assert(fibril_mutex_is_locked(&instance->guard));
90 if (address < 0)
91 return NULL;
92 list_foreach(*get_list(instance, address), iterator) {
93 endpoint_t *ep = endpoint_get_instance(iterator);
94 if (ep_match(ep, address, endpoint, direction))
95 return ep;
96 }
97 return NULL;
98}
99
100/** Calculate bandwidth that needs to be reserved for communication with EP.
101 * Calculation follows USB 1.1 specification.
102 * @param speed Device's speed.
103 * @param type Type of the transfer.
104 * @param size Number of byte to transfer.
105 * @param max_packet_size Maximum bytes in one packet.
106 */
107size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
108 size_t size, size_t max_packet_size)
109{
110 /* We care about bandwidth only for interrupt and isochronous. */
111 if ((type != USB_TRANSFER_INTERRUPT)
112 && (type != USB_TRANSFER_ISOCHRONOUS)) {
113 return 0;
114 }
115
116 const unsigned packet_count =
117 (size + max_packet_size - 1) / max_packet_size;
118 /* TODO: It may be that ISO and INT transfers use only one packet per
119 * transaction, but I did not find text in USB spec to confirm this */
120 /* NOTE: All data packets will be considered to be max_packet_size */
121 switch (speed)
122 {
123 case USB_SPEED_LOW:
124 assert(type == USB_TRANSFER_INTERRUPT);
125 /* Protocol overhead 13B
126 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
127 * CRC bytes, and a 3-byte interpacket delay)
128 * see USB spec page 45-46. */
129 /* Speed penalty 8: low speed is 8-times slower*/
130 return packet_count * (13 + max_packet_size) * 8;
131 case USB_SPEED_FULL:
132 /* Interrupt transfer overhead see above
133 * or page 45 of USB spec */
134 if (type == USB_TRANSFER_INTERRUPT)
135 return packet_count * (13 + max_packet_size);
136
137 assert(type == USB_TRANSFER_ISOCHRONOUS);
138 /* Protocol overhead 9B
139 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
140 * bytes, and a 1-byte interpacket delay)
141 * see USB spec page 42 */
142 return packet_count * (9 + max_packet_size);
143 default:
144 return 0;
145 }
146}
147
148/** Initialize to default state.
149 * You need to provide valid bw_count function if you plan to use
150 * add_endpoint/remove_endpoint pair.
151 *
152 * @param instance usb_endpoint_manager structure, non-null.
153 * @param available_bandwidth Size of the bandwidth pool.
154 * @param bw_count function to use to calculate endpoint bw requirements.
155 * @return Error code.
156 */
157int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
158 size_t available_bandwidth, bw_count_func_t bw_count)
159{
160 assert(instance);
161 fibril_mutex_initialize(&instance->guard);
162 instance->free_bw = available_bandwidth;
163 instance->bw_count = bw_count;
164 for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
165 list_initialize(&instance->endpoint_lists[i]);
166 }
167 return EOK;
168}
169
170/** Register endpoint structure.
171 * Checks for duplicates.
172 * @param instance usb_endpoint_manager, non-null.
173 * @param ep endpoint_t to register.
174 * @param data_size Size of data to transfer.
175 * @return Error code.
176 */
177int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
178 endpoint_t *ep, size_t data_size)
179{
180 assert(instance);
181 if (ep == NULL || ep->address < 0)
182 return EINVAL;
183
184 fibril_mutex_lock(&instance->guard);
185 /* Check for available bandwidth */
186 if (ep->bandwidth > instance->free_bw) {
187 fibril_mutex_unlock(&instance->guard);
188 return ENOSPC;
189 }
190
191 /* Check for existence */
192 const endpoint_t *endpoint =
193 find_locked(instance, ep->address, ep->endpoint, ep->direction);
194 if (endpoint != NULL) {
195 fibril_mutex_unlock(&instance->guard);
196 return EEXISTS;
197 }
198 list_append(&ep->link, get_list(instance, ep->address));
199
200 instance->free_bw -= ep->bandwidth;
201 fibril_mutex_unlock(&instance->guard);
202 return EOK;
203}
204
205/** Unregister endpoint structure.
206 * Checks for duplicates.
207 * @param instance usb_endpoint_manager, non-null.
208 * @param ep endpoint_t to unregister.
209 * @return Error code.
210 */
211int usb_endpoint_manager_unregister_ep(
212 usb_endpoint_manager_t *instance, endpoint_t *ep)
213{
214 assert(instance);
215 if (ep == NULL || ep->address < 0)
216 return EINVAL;
217
218 fibril_mutex_lock(&instance->guard);
219 if (!list_member(&ep->link, get_list(instance, ep->address))) {
220 fibril_mutex_unlock(&instance->guard);
221 return ENOENT;
222 }
223 list_remove(&ep->link);
224 instance->free_bw += ep->bandwidth;
225 fibril_mutex_unlock(&instance->guard);
226 return EOK;
227}
228
229/** Find endpoint_t representing the given communication route.
230 * @param instance usb_endpoint_manager, non-null.
231 * @param address
232 */
233endpoint_t * usb_endpoint_manager_find_ep(usb_endpoint_manager_t *instance,
234 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
235{
236 assert(instance);
237
238 fibril_mutex_lock(&instance->guard);
239 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
240 fibril_mutex_unlock(&instance->guard);
241 return ep;
242}
243
244/** Create and register new endpoint_t structure.
245 * @param instance usb_endpoint_manager structure, non-null.
246 * @param address USB address.
247 * @param endpoint USB endpoint number.
248 * @param direction Communication direction.
249 * @param type USB transfer type.
250 * @param speed USB Communication speed.
251 * @param max_packet_size Maximum size of data packets.
252 * @param data_size Expected communication size.
253 * @param callback function to call just after registering.
254 * @param arg Argument to pass to the callback function.
255 * @return Error code.
256 */
257int usb_endpoint_manager_add_ep(usb_endpoint_manager_t *instance,
258 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
259 usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size,
260 size_t data_size, ep_add_callback_t callback, void *arg)
261{
262 assert(instance);
263 if (instance->bw_count == NULL)
264 return ENOTSUP;
265 if (address < 0)
266 return EINVAL;
267
268 const size_t bw =
269 instance->bw_count(speed, type, data_size, max_packet_size);
270
271 fibril_mutex_lock(&instance->guard);
272 /* Check for available bandwidth */
273 if (bw > instance->free_bw) {
274 fibril_mutex_unlock(&instance->guard);
275 return ENOSPC;
276 }
277
278 /* Check for existence */
279 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
280 if (ep != NULL) {
281 fibril_mutex_unlock(&instance->guard);
282 return EEXISTS;
283 }
284
285 ep = endpoint_create(
286 address, endpoint, direction, type, speed, max_packet_size, bw);
287 if (!ep) {
288 fibril_mutex_unlock(&instance->guard);
289 return ENOMEM;
290 }
291
292 if (callback) {
293 const int ret = callback(ep, arg);
294 if (ret != EOK) {
295 fibril_mutex_unlock(&instance->guard);
296 endpoint_destroy(ep);
297 return ret;
298 }
299 }
300 list_append(&ep->link, get_list(instance, ep->address));
301
302 instance->free_bw -= ep->bandwidth;
303 fibril_mutex_unlock(&instance->guard);
304 return EOK;
305}
306
307/** Unregister and destroy endpoint_t structure representing given route.
308 * @param instance usb_endpoint_manager structure, non-null.
309 * @param address USB address.
310 * @param endpoint USB endpoint number.
311 * @param direction Communication direction.
312 * @param callback Function to call after unregister, before destruction.
313 * @arg Argument to pass to the callback function.
314 * @return Error code.
315 */
316int usb_endpoint_manager_remove_ep(usb_endpoint_manager_t *instance,
317 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
318 void (*callback)(endpoint_t *, void *), void *arg)
319{
320 assert(instance);
321 fibril_mutex_lock(&instance->guard);
322 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
323 if (ep != NULL) {
324 list_remove(&ep->link);
325 instance->free_bw += ep->bandwidth;
326 }
327 fibril_mutex_unlock(&instance->guard);
328 if (ep == NULL)
329 return ENOENT;
330
331 if (callback) {
332 callback(ep, arg);
333 }
334 endpoint_destroy(ep);
335 return EOK;
336}
337
338int usb_endpoint_manager_reset_toggle(usb_endpoint_manager_t *instance,
339 usb_target_t target, bool all)
340{
341 assert(instance);
342 if (!usb_target_is_valid(target)) {
343 return EINVAL;
344 }
345
346 int rc = ENOENT;
347
348 fibril_mutex_lock(&instance->guard);
349 list_foreach(*get_list(instance, target.address), it) {
350 endpoint_t *ep = endpoint_get_instance(it);
351 if ((ep->address == target.address)
352 && (all || ep->endpoint == target.endpoint)) {
353 endpoint_toggle_set(ep, 0);
354 rc = EOK;
355 }
356 }
357 fibril_mutex_unlock(&instance->guard);
358 return rc;
359}
360
361/** Unregister and destroy all endpoints using given address.
362 * @param instance usb_endpoint_manager structure, non-null.
363 * @param address USB address.
364 * @param endpoint USB endpoint number.
365 * @param direction Communication direction.
366 * @param callback Function to call after unregister, before destruction.
367 * @arg Argument to pass to the callback function.
368 * @return Error code.
369 */
370void usb_endpoint_manager_remove_address(usb_endpoint_manager_t *instance,
371 usb_address_t address, void (*callback)(endpoint_t *, void *), void *arg)
372{
373 assert(address >= 0);
374 assert(instance);
375 fibril_mutex_lock(&instance->guard);
376 list_foreach(*get_list(instance, address), iterator) {
377 endpoint_t *ep = endpoint_get_instance(iterator);
378 if (ep->address == address) {
379 iterator = iterator->next;
380 list_remove(&ep->link);
381 if (callback)
382 callback(ep, arg);
383 endpoint_destroy(ep);
384 }
385 }
386 fibril_mutex_unlock(&instance->guard);
387}
388/**
389 * @}
390 */
Note: See TracBrowser for help on using the repository browser.