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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 46f2808 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
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
29#include <bool.h>
30#include <assert.h>
31#include <errno.h>
32
33#include <usb/debug.h>
34#include <usb/host/usb_endpoint_manager.h>
35
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 */
46static inline bool ep_match(const endpoint_t *ep,
47 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
48{
49 assert(ep);
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);
56}
57/*----------------------------------------------------------------------------*/
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 */
63static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
64{
65 assert(instance);
66 assert(addr >= 0);
67 return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
68}
69/*----------------------------------------------------------------------------*/
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 */
78static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
79 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
80{
81 assert(instance);
82 assert(fibril_mutex_is_locked(&instance->guard));
83 if (address < 0)
84 return NULL;
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;
91}
92/*----------------------------------------------------------------------------*/
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 */
100size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
101 size_t size, size_t max_packet_size)
102{
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
109 const unsigned packet_count =
110 (size + max_packet_size - 1) / max_packet_size;
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 */
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/*----------------------------------------------------------------------------*/
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 */
150int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
151 size_t available_bandwidth,
152 size_t (*bw_count)(usb_speed_t, usb_transfer_type_t, size_t, size_t))
153{
154 assert(instance);
155 fibril_mutex_initialize(&instance->guard);
156 instance->free_bw = available_bandwidth;
157 instance->bw_count = bw_count;
158 for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
159 list_initialize(&instance->endpoint_lists[i]);
160 }
161 return EOK;
162}
163/*----------------------------------------------------------------------------*/
164/** Check setup packet data for signs of toggle reset.
165 *
166 * @param[in] instance usb_endpoint_manager structure, non-null.
167 * @param[in] target Device to receive setup packet.
168 * @param[in] data Setup packet data.
169 *
170 * Really ugly one. Resets toggle bit on all endpoints that need it.
171 */
172void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance,
173 usb_target_t target, const uint8_t data[8])
174{
175 assert(instance);
176 if (!usb_target_is_valid(target)) {
177 usb_log_error("Invalid data when checking for toggle reset.\n");
178 return;
179 }
180
181 assert(data);
182 switch (data[1])
183 {
184 case 0x01: /* Clear Feature -- resets only cleared ep */
185 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
186 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
187 fibril_mutex_lock(&instance->guard);
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 }
196 fibril_mutex_unlock(&instance->guard);
197 }
198 break;
199
200 case 0x9: /* Set Configuration */
201 case 0x11: /* Set Interface */
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. */
205 if ((data[0] & 0xf) == 0) {
206 fibril_mutex_lock(&instance->guard);
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 }
213 fibril_mutex_unlock(&instance->guard);
214 }
215 break;
216 }
217}
218/*----------------------------------------------------------------------------*/
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 */
226int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
227 endpoint_t *ep, size_t data_size)
228{
229 assert(instance);
230 if (ep == NULL || ep->address < 0)
231 return EINVAL;
232
233 fibril_mutex_lock(&instance->guard);
234 /* Check for available bandwidth */
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 }
247 list_append(&ep->link, get_list(instance, ep->address));
248
249 instance->free_bw -= ep->bandwidth;
250 fibril_mutex_unlock(&instance->guard);
251 return EOK;
252}
253/*----------------------------------------------------------------------------*/
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 */
260int usb_endpoint_manager_unregister_ep(
261 usb_endpoint_manager_t *instance, endpoint_t *ep)
262{
263 assert(instance);
264 if (ep == NULL || ep->address < 0)
265 return EINVAL;
266
267 fibril_mutex_lock(&instance->guard);
268 if (!list_member(&ep->link, get_list(instance, ep->address))) {
269 fibril_mutex_unlock(&instance->guard);
270 return ENOENT;
271 }
272 list_remove(&ep->link);
273 instance->free_bw += ep->bandwidth;
274 fibril_mutex_unlock(&instance->guard);
275 return EOK;
276}
277/*----------------------------------------------------------------------------*/
278/** Find endpoint_t representing the given communication route.
279 * @param instance usb_endpoint_manager, non-null.
280 * @param address
281 */
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/*----------------------------------------------------------------------------*/
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 */
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);
312 if (instance->bw_count == NULL)
313 return ENOTSUP;
314 if (address < 0)
315 return EINVAL;
316
317 const size_t bw =
318 instance->bw_count(speed, type, data_size, max_packet_size);
319
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(
335 address, endpoint, direction, type, speed, max_packet_size, bw);
336 if (!ep) {
337 fibril_mutex_unlock(&instance->guard);
338 return ENOMEM;
339 }
340
341 if (callback) {
342 const int ret = callback(ep, arg);
343 if (ret != EOK) {
344 fibril_mutex_unlock(&instance->guard);
345 endpoint_destroy(ep);
346 return ret;
347 }
348 }
349 list_append(&ep->link, get_list(instance, ep->address));
350
351 instance->free_bw -= ep->bandwidth;
352 fibril_mutex_unlock(&instance->guard);
353 return EOK;
354}
355/*----------------------------------------------------------------------------*/
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 */
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}
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.