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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e8d6ce2 was 3e6a98c5, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Standards-compliant boolean type.

  • Property mode set to 100644
File size: 13.6 KB
RevLine 
[f0891ce]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 */
[cbd568b]28/** @addtogroup libusbhost
29 * @{
30 */
31/** @file
32 * HC Endpoint management.
33 */
[f0891ce]34
[3e6a98c5]35#include <stdbool.h>
[f0891ce]36#include <assert.h>
37#include <errno.h>
[90b9ab5]38
[ba038f4]39#include <usb/debug.h>
[f0891ce]40#include <usb/host/usb_endpoint_manager.h>
41
[5400606]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 */
[7265558]52static inline bool ep_match(const endpoint_t *ep,
53 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
[f0891ce]54{
[83c3123]55 assert(ep);
[7265558]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);
[f0891ce]62}
[a76b01b4]63
[cbd568b]64/** Get list that holds endpoints for given address.
[5400606]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 */
[7265558]69static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
[f0891ce]70{
[7265558]71 assert(instance);
[5400606]72 assert(addr >= 0);
[7265558]73 return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
[f0891ce]74}
[a76b01b4]75
[5400606]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.
[cbd568b]83 * @note Assumes that the internal mutex is locked.
[5400606]84 */
[7265558]85static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
86 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
[ba038f4]87{
[7265558]88 assert(instance);
89 assert(fibril_mutex_is_locked(&instance->guard));
[5400606]90 if (address < 0)
91 return NULL;
[7265558]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;
[ba038f4]98}
[a76b01b4]99
[5400606]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 */
[f0891ce]107size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
108 size_t size, size_t max_packet_size)
109{
[d41f301]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
[f0891ce]116 const unsigned packet_count =
117 (size + max_packet_size - 1) / max_packet_size;
[7265558]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 */
[f0891ce]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}
[a76b01b4]147
[5400606]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 */
[f0891ce]157int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
[933b0d7]158 size_t available_bandwidth,
159 size_t (*bw_count)(usb_speed_t, usb_transfer_type_t, size_t, size_t))
[f0891ce]160{
161 assert(instance);
162 fibril_mutex_initialize(&instance->guard);
163 instance->free_bw = available_bandwidth;
[933b0d7]164 instance->bw_count = bw_count;
[7265558]165 for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
166 list_initialize(&instance->endpoint_lists[i]);
167 }
168 return EOK;
[f0891ce]169}
[a76b01b4]170
[ba038f4]171/** Check setup packet data for signs of toggle reset.
172 *
[5400606]173 * @param[in] instance usb_endpoint_manager structure, non-null.
[ba038f4]174 * @param[in] target Device to receive setup packet.
175 * @param[in] data Setup packet data.
176 *
[5400606]177 * Really ugly one. Resets toggle bit on all endpoints that need it.
[cbd568b]178 * @TODO Use tools from libusbdev requests.h
[ba038f4]179 */
[5400606]180void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance,
181 usb_target_t target, const uint8_t data[8])
[ba038f4]182{
183 assert(instance);
[563d9d0a]184 if (!usb_target_is_valid(target)) {
[ba038f4]185 usb_log_error("Invalid data when checking for toggle reset.\n");
186 return;
187 }
188
[563d9d0a]189 assert(data);
[ba038f4]190 switch (data[1])
191 {
[563d9d0a]192 case 0x01: /* Clear Feature -- resets only cleared ep */
193 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
[cbd568b]194 // TODO Use macros in libusbdev requests.h
[ba038f4]195 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
196 fibril_mutex_lock(&instance->guard);
[7265558]197 /* endpoint number is < 16, thus first byte is enough */
198 list_foreach(*get_list(instance, target.address), it) {
199 endpoint_t *ep = endpoint_get_instance(it);
200 if ((ep->address == target.address)
201 && (ep->endpoint = data[4])) {
202 endpoint_toggle_set(ep,0);
203 }
204 }
[ba038f4]205 fibril_mutex_unlock(&instance->guard);
206 }
207 break;
208
[563d9d0a]209 case 0x9: /* Set Configuration */
210 case 0x11: /* Set Interface */
[7265558]211 /* Recipient must be device, this resets all endpoints,
212 * In fact there should be no endpoints but EP 0 registered
[cbd568b]213 * as different interfaces use different endpoints,
214 * unless you're changing configuration or alternative
215 * interface of an already setup device. */
[ba038f4]216 if ((data[0] & 0xf) == 0) {
217 fibril_mutex_lock(&instance->guard);
[7265558]218 list_foreach(*get_list(instance, target.address), it) {
219 endpoint_t *ep = endpoint_get_instance(it);
220 if (ep->address == target.address) {
221 endpoint_toggle_set(ep,0);
222 }
223 }
[ba038f4]224 fibril_mutex_unlock(&instance->guard);
225 }
226 break;
227 }
228}
[a76b01b4]229
[5400606]230/** Register endpoint structure.
231 * Checks for duplicates.
232 * @param instance usb_endpoint_manager, non-null.
233 * @param ep endpoint_t to register.
234 * @param data_size Size of data to transfer.
235 * @return Error code.
236 */
[48ae3ef]237int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
238 endpoint_t *ep, size_t data_size)
239{
240 assert(instance);
[5400606]241 if (ep == NULL || ep->address < 0)
242 return EINVAL;
[48ae3ef]243
[5400606]244 fibril_mutex_lock(&instance->guard);
245 /* Check for available bandwidth */
[48ae3ef]246 if (ep->bandwidth > instance->free_bw) {
247 fibril_mutex_unlock(&instance->guard);
248 return ENOSPC;
249 }
250
251 /* Check for existence */
252 const endpoint_t *endpoint =
253 find_locked(instance, ep->address, ep->endpoint, ep->direction);
254 if (endpoint != NULL) {
255 fibril_mutex_unlock(&instance->guard);
256 return EEXISTS;
257 }
[5400606]258 list_append(&ep->link, get_list(instance, ep->address));
[48ae3ef]259
260 instance->free_bw -= ep->bandwidth;
261 fibril_mutex_unlock(&instance->guard);
262 return EOK;
263}
[a76b01b4]264
[5400606]265/** Unregister endpoint structure.
266 * Checks for duplicates.
267 * @param instance usb_endpoint_manager, non-null.
268 * @param ep endpoint_t to unregister.
269 * @return Error code.
270 */
[48ae3ef]271int usb_endpoint_manager_unregister_ep(
272 usb_endpoint_manager_t *instance, endpoint_t *ep)
273{
274 assert(instance);
[5400606]275 if (ep == NULL || ep->address < 0)
276 return EINVAL;
277
[48ae3ef]278 fibril_mutex_lock(&instance->guard);
[5400606]279 if (!list_member(&ep->link, get_list(instance, ep->address))) {
280 fibril_mutex_unlock(&instance->guard);
281 return ENOENT;
282 }
[48ae3ef]283 list_remove(&ep->link);
[5400606]284 instance->free_bw += ep->bandwidth;
[48ae3ef]285 fibril_mutex_unlock(&instance->guard);
286 return EOK;
287}
[a76b01b4]288
[5400606]289/** Find endpoint_t representing the given communication route.
290 * @param instance usb_endpoint_manager, non-null.
291 * @param address
292 */
[48ae3ef]293endpoint_t * usb_endpoint_manager_find_ep(usb_endpoint_manager_t *instance,
294 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
295{
296 assert(instance);
297
298 fibril_mutex_lock(&instance->guard);
299 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
300 fibril_mutex_unlock(&instance->guard);
301 return ep;
302}
[a76b01b4]303
[5400606]304/** Create and register new endpoint_t structure.
305 * @param instance usb_endpoint_manager structure, non-null.
306 * @param address USB address.
307 * @param endpoint USB endpoint number.
308 * @param direction Communication direction.
309 * @param type USB transfer type.
310 * @param speed USB Communication speed.
311 * @param max_packet_size Maximum size of data packets.
312 * @param data_size Expected communication size.
313 * @param callback function to call just after registering.
314 * @param arg Argument to pass to the callback function.
315 * @return Error code.
316 */
[48ae3ef]317int usb_endpoint_manager_add_ep(usb_endpoint_manager_t *instance,
318 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
319 usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size,
320 size_t data_size, int (*callback)(endpoint_t *, void *), void *arg)
321{
322 assert(instance);
[5400606]323 if (instance->bw_count == NULL)
324 return ENOTSUP;
325 if (address < 0)
326 return EINVAL;
327
[48ae3ef]328 const size_t bw =
329 instance->bw_count(speed, type, data_size, max_packet_size);
330
[5400606]331 fibril_mutex_lock(&instance->guard);
332 /* Check for available bandwidth */
333 if (bw > instance->free_bw) {
334 fibril_mutex_unlock(&instance->guard);
335 return ENOSPC;
336 }
337
338 /* Check for existence */
339 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
340 if (ep != NULL) {
341 fibril_mutex_unlock(&instance->guard);
342 return EEXISTS;
343 }
344
345 ep = endpoint_create(
[48ae3ef]346 address, endpoint, direction, type, speed, max_packet_size, bw);
[5400606]347 if (!ep) {
348 fibril_mutex_unlock(&instance->guard);
[48ae3ef]349 return ENOMEM;
[5400606]350 }
[48ae3ef]351
352 if (callback) {
353 const int ret = callback(ep, arg);
354 if (ret != EOK) {
[5400606]355 fibril_mutex_unlock(&instance->guard);
[48ae3ef]356 endpoint_destroy(ep);
357 return ret;
358 }
359 }
[5400606]360 list_append(&ep->link, get_list(instance, ep->address));
[48ae3ef]361
[5400606]362 instance->free_bw -= ep->bandwidth;
363 fibril_mutex_unlock(&instance->guard);
364 return EOK;
[48ae3ef]365}
[a76b01b4]366
[5400606]367/** Unregister and destroy endpoint_t structure representing given route.
368 * @param instance usb_endpoint_manager structure, non-null.
369 * @param address USB address.
370 * @param endpoint USB endpoint number.
371 * @param direction Communication direction.
372 * @param callback Function to call after unregister, before destruction.
373 * @arg Argument to pass to the callback function.
374 * @return Error code.
375 */
[48ae3ef]376int usb_endpoint_manager_remove_ep(usb_endpoint_manager_t *instance,
377 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
378 void (*callback)(endpoint_t *, void *), void *arg)
379{
380 assert(instance);
381 fibril_mutex_lock(&instance->guard);
382 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
383 if (ep != NULL) {
384 list_remove(&ep->link);
385 instance->free_bw += ep->bandwidth;
386 }
387 fibril_mutex_unlock(&instance->guard);
388 if (ep == NULL)
389 return ENOENT;
390
391 if (callback) {
392 callback(ep, arg);
393 }
394 endpoint_destroy(ep);
395 return EOK;
396}
[a76b01b4]397
[cbd568b]398/** Unregister and destroy all endpoints using given address.
399 * @param instance usb_endpoint_manager structure, non-null.
400 * @param address USB address.
401 * @param endpoint USB endpoint number.
402 * @param direction Communication direction.
403 * @param callback Function to call after unregister, before destruction.
404 * @arg Argument to pass to the callback function.
405 * @return Error code.
406 */
[46f2808]407void usb_endpoint_manager_remove_address(usb_endpoint_manager_t *instance,
408 usb_address_t address, void (*callback)(endpoint_t *, void *), void *arg)
409{
410 assert(address >= 0);
411 assert(instance);
412 fibril_mutex_lock(&instance->guard);
413 list_foreach(*get_list(instance, address), iterator) {
414 endpoint_t *ep = endpoint_get_instance(iterator);
415 if (ep->address == address) {
416 iterator = iterator->next;
417 list_remove(&ep->link);
418 if (callback)
419 callback(ep, arg);
420 endpoint_destroy(ep);
421 }
422 }
423 fibril_mutex_unlock(&instance->guard);
424}
[cbd568b]425/**
426 * @}
427 */
Note: See TracBrowser for help on using the repository browser.