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

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

libusbhost: usb_endpoint_manager add function to remove all endpoints of one address.

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