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

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

libsusbhost: Doxygen.

  • Property mode set to 100644
File size: 14.4 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 <bool.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,
159 size_t (*bw_count)(usb_speed_t, usb_transfer_type_t, size_t, size_t))
160{
161 assert(instance);
162 fibril_mutex_initialize(&instance->guard);
163 instance->free_bw = available_bandwidth;
164 instance->bw_count = bw_count;
165 for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
166 list_initialize(&instance->endpoint_lists[i]);
167 }
168 return EOK;
169}
170/*----------------------------------------------------------------------------*/
171/** Check setup packet data for signs of toggle reset.
172 *
173 * @param[in] instance usb_endpoint_manager structure, non-null.
174 * @param[in] target Device to receive setup packet.
175 * @param[in] data Setup packet data.
176 *
177 * Really ugly one. Resets toggle bit on all endpoints that need it.
178 * @TODO Use tools from libusbdev requests.h
179 */
180void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance,
181 usb_target_t target, const uint8_t data[8])
182{
183 assert(instance);
184 if (!usb_target_is_valid(target)) {
185 usb_log_error("Invalid data when checking for toggle reset.\n");
186 return;
187 }
188
189 assert(data);
190 switch (data[1])
191 {
192 case 0x01: /* Clear Feature -- resets only cleared ep */
193 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
194 // TODO Use macros in libusbdev requests.h
195 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
196 fibril_mutex_lock(&instance->guard);
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 }
205 fibril_mutex_unlock(&instance->guard);
206 }
207 break;
208
209 case 0x9: /* Set Configuration */
210 case 0x11: /* Set Interface */
211 /* Recipient must be device, this resets all endpoints,
212 * In fact there should be no endpoints but EP 0 registered
213 * as different interfaces use different endpoints,
214 * unless you're changing configuration or alternative
215 * interface of an already setup device. */
216 if ((data[0] & 0xf) == 0) {
217 fibril_mutex_lock(&instance->guard);
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 }
224 fibril_mutex_unlock(&instance->guard);
225 }
226 break;
227 }
228}
229/*----------------------------------------------------------------------------*/
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 */
237int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
238 endpoint_t *ep, size_t data_size)
239{
240 assert(instance);
241 if (ep == NULL || ep->address < 0)
242 return EINVAL;
243
244 fibril_mutex_lock(&instance->guard);
245 /* Check for available bandwidth */
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 }
258 list_append(&ep->link, get_list(instance, ep->address));
259
260 instance->free_bw -= ep->bandwidth;
261 fibril_mutex_unlock(&instance->guard);
262 return EOK;
263}
264/*----------------------------------------------------------------------------*/
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 */
271int usb_endpoint_manager_unregister_ep(
272 usb_endpoint_manager_t *instance, endpoint_t *ep)
273{
274 assert(instance);
275 if (ep == NULL || ep->address < 0)
276 return EINVAL;
277
278 fibril_mutex_lock(&instance->guard);
279 if (!list_member(&ep->link, get_list(instance, ep->address))) {
280 fibril_mutex_unlock(&instance->guard);
281 return ENOENT;
282 }
283 list_remove(&ep->link);
284 instance->free_bw += ep->bandwidth;
285 fibril_mutex_unlock(&instance->guard);
286 return EOK;
287}
288/*----------------------------------------------------------------------------*/
289/** Find endpoint_t representing the given communication route.
290 * @param instance usb_endpoint_manager, non-null.
291 * @param address
292 */
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}
303/*----------------------------------------------------------------------------*/
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 */
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);
323 if (instance->bw_count == NULL)
324 return ENOTSUP;
325 if (address < 0)
326 return EINVAL;
327
328 const size_t bw =
329 instance->bw_count(speed, type, data_size, max_packet_size);
330
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(
346 address, endpoint, direction, type, speed, max_packet_size, bw);
347 if (!ep) {
348 fibril_mutex_unlock(&instance->guard);
349 return ENOMEM;
350 }
351
352 if (callback) {
353 const int ret = callback(ep, arg);
354 if (ret != EOK) {
355 fibril_mutex_unlock(&instance->guard);
356 endpoint_destroy(ep);
357 return ret;
358 }
359 }
360 list_append(&ep->link, get_list(instance, ep->address));
361
362 instance->free_bw -= ep->bandwidth;
363 fibril_mutex_unlock(&instance->guard);
364 return EOK;
365}
366/*----------------------------------------------------------------------------*/
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 */
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}
397/*----------------------------------------------------------------------------*/
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 */
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}
425/**
426 * @}
427 */
Note: See TracBrowser for help on using the repository browser.