source: mainline/uspace/lib/usbhost/src/usb_bus.c@ b4b534ac

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b4b534ac was b4b534ac, checked in by Jakub Jermar <jakub@…>, 9 years ago

Merge from lp:~jan.vesely/helenos/usb

  • Property mode set to 100644
File size: 15.8 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 <usb/host/usb_bus.h>
36#include <usb/debug.h>
37
38#include <assert.h>
39#include <errno.h>
40#include <macros.h>
41#include <stdbool.h>
42
43
44/** Endpoint compare helper function.
45 *
46 * USB_DIRECTION_BOTH matches both IN and OUT.
47 * @param ep Endpoint to compare, non-null.
48 * @param address Tested address.
49 * @param endpoint Tested endpoint number.
50 * @param direction Tested direction.
51 * @return True if ep can be used to communicate with given device,
52 * false otherwise.
53 */
54static inline bool ep_match(const endpoint_t *ep,
55 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
56{
57 assert(ep);
58 return
59 ((direction == ep->direction)
60 || (ep->direction == USB_DIRECTION_BOTH)
61 || (direction == USB_DIRECTION_BOTH))
62 && (endpoint == ep->endpoint)
63 && (address == ep->address);
64}
65
66/** Get list that holds endpoints for given address.
67 * @param instance usb_bus structure, non-null.
68 * @param addr USB address, must be >= 0.
69 * @return Pointer to the appropriate list.
70 */
71static list_t * get_list(usb_bus_t *instance, usb_address_t addr)
72{
73 assert(instance);
74 assert(addr >= 0);
75 return &instance->devices[addr % ARRAY_SIZE(instance->devices)].endpoint_list;
76}
77
78/** Internal search function, works on locked structure.
79 * @param instance usb_bus structure, non-null.
80 * @param address USB address, must be valid.
81 * @param endpoint USB endpoint number.
82 * @param direction Communication direction.
83 * @return Pointer to endpoint_t structure representing given communication
84 * target, NULL if there is no such endpoint registered.
85 * @note Assumes that the internal mutex is locked.
86 */
87static endpoint_t * find_locked(usb_bus_t *instance,
88 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
89{
90 assert(instance);
91 assert(fibril_mutex_is_locked(&instance->guard));
92 if (address < 0)
93 return NULL;
94 list_foreach(*get_list(instance, address), link, endpoint_t, ep) {
95 if (ep_match(ep, address, endpoint, direction))
96 return ep;
97 }
98 return NULL;
99}
100
101/** Get a free USB address
102 *
103 * @param[in] instance Device manager structure to use.
104 * @return Free address, or error code.
105 */
106static usb_address_t usb_bus_get_free_address(usb_bus_t *instance)
107{
108
109 usb_address_t new_address = instance->last_address;
110 do {
111 new_address = (new_address + 1) % USB_ADDRESS_COUNT;
112 if (new_address == USB_ADDRESS_DEFAULT)
113 new_address = 1;
114 if (new_address == instance->last_address)
115 return ENOSPC;
116 } while (instance->devices[new_address].occupied);
117
118 assert(new_address != USB_ADDRESS_DEFAULT);
119 instance->last_address = new_address;
120
121 return new_address;
122}
123
124/** Calculate bandwidth that needs to be reserved for communication with EP.
125 * Calculation follows USB 1.1 specification.
126 * @param speed Device's speed.
127 * @param type Type of the transfer.
128 * @param size Number of byte to transfer.
129 * @param max_packet_size Maximum bytes in one packet.
130 */
131size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
132 size_t size, size_t max_packet_size)
133{
134 /* We care about bandwidth only for interrupt and isochronous. */
135 if ((type != USB_TRANSFER_INTERRUPT)
136 && (type != USB_TRANSFER_ISOCHRONOUS)) {
137 return 0;
138 }
139
140 const unsigned packet_count =
141 (size + max_packet_size - 1) / max_packet_size;
142 /* TODO: It may be that ISO and INT transfers use only one packet per
143 * transaction, but I did not find text in USB spec to confirm this */
144 /* NOTE: All data packets will be considered to be max_packet_size */
145 switch (speed)
146 {
147 case USB_SPEED_LOW:
148 assert(type == USB_TRANSFER_INTERRUPT);
149 /* Protocol overhead 13B
150 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
151 * CRC bytes, and a 3-byte interpacket delay)
152 * see USB spec page 45-46. */
153 /* Speed penalty 8: low speed is 8-times slower*/
154 return packet_count * (13 + max_packet_size) * 8;
155 case USB_SPEED_FULL:
156 /* Interrupt transfer overhead see above
157 * or page 45 of USB spec */
158 if (type == USB_TRANSFER_INTERRUPT)
159 return packet_count * (13 + max_packet_size);
160
161 assert(type == USB_TRANSFER_ISOCHRONOUS);
162 /* Protocol overhead 9B
163 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
164 * bytes, and a 1-byte interpacket delay)
165 * see USB spec page 42 */
166 return packet_count * (9 + max_packet_size);
167 default:
168 return 0;
169 }
170}
171
172/** Calculate bandwidth that needs to be reserved for communication with EP.
173 * Calculation follows USB 2.0 specification.
174 * @param speed Device's speed.
175 * @param type Type of the transfer.
176 * @param size Number of byte to transfer.
177 * @param max_packet_size Maximum bytes in one packet.
178 */
179size_t bandwidth_count_usb20(usb_speed_t speed, usb_transfer_type_t type,
180 size_t size, size_t max_packet_size)
181{
182 /* We care about bandwidth only for interrupt and isochronous. */
183 if ((type != USB_TRANSFER_INTERRUPT)
184 && (type != USB_TRANSFER_ISOCHRONOUS)) {
185 return 0;
186 }
187 //TODO Implement
188 return 0;
189}
190
191/** Initialize to default state.
192 * You need to provide valid bw_count function if you plan to use
193 * add_endpoint/remove_endpoint pair.
194 *
195 * @param instance usb_bus structure, non-null.
196 * @param available_bandwidth Size of the bandwidth pool.
197 * @param bw_count function to use to calculate endpoint bw requirements.
198 * @return Error code.
199 */
200int usb_bus_init(usb_bus_t *instance,
201 size_t available_bandwidth, bw_count_func_t bw_count, usb_speed_t max_speed)
202{
203 assert(instance);
204 fibril_mutex_initialize(&instance->guard);
205 instance->free_bw = available_bandwidth;
206 instance->bw_count = bw_count;
207 instance->last_address = 0;
208 instance->max_speed = max_speed;
209 for (unsigned i = 0; i < ARRAY_SIZE(instance->devices); ++i) {
210 list_initialize(&instance->devices[i].endpoint_list);
211 instance->devices[i].speed = USB_SPEED_MAX;
212 instance->devices[i].occupied = false;
213 }
214 return EOK;
215}
216
217/** Register endpoint structure.
218 * Checks for duplicates.
219 * @param instance usb_bus, non-null.
220 * @param ep endpoint_t to register.
221 * @param data_size Size of data to transfer.
222 * @return Error code.
223 */
224int usb_bus_register_ep(usb_bus_t *instance, endpoint_t *ep, size_t data_size)
225{
226 assert(instance);
227 if (ep == NULL || ep->address < 0)
228 return EINVAL;
229
230 fibril_mutex_lock(&instance->guard);
231 /* Check for available bandwidth */
232 if (ep->bandwidth > instance->free_bw) {
233 fibril_mutex_unlock(&instance->guard);
234 return ENOSPC;
235 }
236
237 /* Check for existence */
238 const endpoint_t *endpoint =
239 find_locked(instance, ep->address, ep->endpoint, ep->direction);
240 if (endpoint != NULL) {
241 fibril_mutex_unlock(&instance->guard);
242 return EEXIST;
243 }
244 list_append(&ep->link, get_list(instance, ep->address));
245
246 instance->free_bw -= ep->bandwidth;
247 usb_log_debug("Registered EP(%d:%d:%s:%s)\n", ep->address, ep->endpoint,
248 usb_str_transfer_type_short(ep->transfer_type),
249 usb_str_direction(ep->direction));
250 fibril_mutex_unlock(&instance->guard);
251 return EOK;
252}
253
254/** Unregister endpoint structure.
255 * Checks for duplicates.
256 * @param instance usb_bus, non-null.
257 * @param ep endpoint_t to unregister.
258 * @return Error code.
259 */
260int usb_bus_unregister_ep(usb_bus_t *instance, endpoint_t *ep)
261{
262 assert(instance);
263 if (ep == NULL || ep->address < 0)
264 return EINVAL;
265
266 fibril_mutex_lock(&instance->guard);
267 if (!list_member(&ep->link, get_list(instance, ep->address))) {
268 fibril_mutex_unlock(&instance->guard);
269 return ENOENT;
270 }
271 list_remove(&ep->link);
272 instance->free_bw += ep->bandwidth;
273 usb_log_debug("Unregistered EP(%d:%d:%s:%s)\n", ep->address,
274 ep->endpoint, usb_str_transfer_type_short(ep->transfer_type),
275 usb_str_direction(ep->direction));
276 fibril_mutex_unlock(&instance->guard);
277 return EOK;
278}
279
280/** Find endpoint_t representing the given communication route.
281 * @param instance usb_bus, non-null.
282 * @param address
283 */
284endpoint_t * usb_bus_find_ep(usb_bus_t *instance,
285 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
286{
287 assert(instance);
288
289 fibril_mutex_lock(&instance->guard);
290 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
291 fibril_mutex_unlock(&instance->guard);
292 return ep;
293}
294
295/** Create and register new endpoint_t structure.
296 * @param instance usb_bus structure, non-null.
297 * @param address USB address.
298 * @param endpoint USB endpoint number.
299 * @param direction Communication direction.
300 * @param type USB transfer type.
301 * @param speed USB Communication speed.
302 * @param max_packet_size Maximum size of data packets.
303 * @param data_size Expected communication size.
304 * @param callback function to call just after registering.
305 * @param arg Argument to pass to the callback function.
306 * @return Error code.
307 */
308int usb_bus_add_ep(usb_bus_t *instance,
309 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
310 usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
311 size_t data_size, ep_add_callback_t callback, void *arg,
312 usb_address_t tt_address, unsigned tt_port)
313{
314 assert(instance);
315 if (instance->bw_count == NULL)
316 return ENOTSUP;
317 if (!usb_address_is_valid(address))
318 return EINVAL;
319
320
321 fibril_mutex_lock(&instance->guard);
322 /* Check for speed and address */
323 if (!instance->devices[address].occupied) {
324 fibril_mutex_unlock(&instance->guard);
325 return ENOENT;
326 }
327
328 /* Check for existence */
329 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
330 if (ep != NULL) {
331 fibril_mutex_unlock(&instance->guard);
332 return EEXIST;
333 }
334
335 const usb_speed_t speed = instance->devices[address].speed;
336 const size_t bw =
337 instance->bw_count(speed, type, data_size, max_packet_size);
338
339 /* Check for available bandwidth */
340 if (bw > instance->free_bw) {
341 fibril_mutex_unlock(&instance->guard);
342 return ENOSPC;
343 }
344
345 ep = endpoint_create(address, endpoint, direction, type, speed,
346 max_packet_size, packets, bw, tt_address, tt_port);
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_bus 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_bus_remove_ep(usb_bus_t *instance,
377 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
378 ep_remove_callback_t callback, 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
398int usb_bus_reset_toggle(usb_bus_t *instance, usb_target_t target, bool all)
399{
400 assert(instance);
401 if (!usb_target_is_valid(target))
402 return EINVAL;
403
404 int ret = ENOENT;
405
406 fibril_mutex_lock(&instance->guard);
407 list_foreach(*get_list(instance, target.address), link, endpoint_t, ep) {
408 if ((ep->address == target.address)
409 && (all || ep->endpoint == target.endpoint)) {
410 endpoint_toggle_set(ep, 0);
411 ret = EOK;
412 }
413 }
414 fibril_mutex_unlock(&instance->guard);
415 return ret;
416}
417
418/** Unregister and destroy all endpoints using given address.
419 * @param instance usb_bus structure, non-null.
420 * @param address USB address.
421 * @param endpoint USB endpoint number.
422 * @param direction Communication direction.
423 * @param callback Function to call after unregister, before destruction.
424 * @arg Argument to pass to the callback function.
425 * @return Error code.
426 */
427int usb_bus_remove_address(usb_bus_t *instance,
428 usb_address_t address, ep_remove_callback_t callback, void *arg)
429{
430 assert(instance);
431 if (!usb_address_is_valid(address))
432 return EINVAL;
433
434 fibril_mutex_lock(&instance->guard);
435
436 const int ret = instance->devices[address].occupied ? EOK : ENOENT;
437 instance->devices[address].occupied = false;
438
439 list_t *list = get_list(instance, address);
440 for (link_t *link = list_first(list); link != NULL; ) {
441 endpoint_t *ep = list_get_instance(link, endpoint_t, link);
442 link = list_next(link, list);
443 if (ep->address == address) {
444 list_remove(&ep->link);
445 if (callback)
446 callback(ep, arg);
447 endpoint_destroy(ep);
448 }
449 }
450 fibril_mutex_unlock(&instance->guard);
451 return ret;
452}
453
454/** Request USB address.
455 * @param instance usb_device_manager
456 * @param address Pointer to requested address value, place to store new address
457 * @parma strict Fail if the requested address is not available.
458 * @return Error code.
459 * @note Default address is only available in strict mode.
460 */
461int usb_bus_request_address(usb_bus_t *instance,
462 usb_address_t *address, bool strict, usb_speed_t speed)
463{
464 assert(instance);
465 assert(address);
466 if (speed > instance->max_speed)
467 return ENOTSUP;
468
469 if (!usb_address_is_valid(*address))
470 return EINVAL;
471
472 usb_address_t addr = *address;
473
474 fibril_mutex_lock(&instance->guard);
475 /* Only grant default address to strict requests */
476 if ((addr == USB_ADDRESS_DEFAULT) && !strict) {
477 addr = usb_bus_get_free_address(instance);
478 }
479
480 if (instance->devices[addr].occupied) {
481 if (strict) {
482 fibril_mutex_unlock(&instance->guard);
483 return ENOENT;
484 }
485 addr = usb_bus_get_free_address(instance);
486 }
487 if (usb_address_is_valid(addr)) {
488 assert(instance->devices[addr].occupied == false);
489 assert(addr != USB_ADDRESS_DEFAULT || strict);
490
491 instance->devices[addr].occupied = true;
492 instance->devices[addr].speed = speed;
493 *address = addr;
494 addr = 0;
495 }
496
497 fibril_mutex_unlock(&instance->guard);
498 return addr;
499}
500
501/** Get speed assigned to USB address.
502 *
503 * @param[in] instance Device manager structure to use.
504 * @param[in] address Address the caller wants to find.
505 * @param[out] speed Assigned speed.
506 * @return Error code.
507 */
508int usb_bus_get_speed(usb_bus_t *instance, usb_address_t address,
509 usb_speed_t *speed)
510{
511 assert(instance);
512 if (!usb_address_is_valid(address)) {
513 return EINVAL;
514 }
515
516 fibril_mutex_lock(&instance->guard);
517
518 const int ret = instance->devices[address].occupied ? EOK : ENOENT;
519 if (speed && instance->devices[address].occupied) {
520 *speed = instance->devices[address].speed;
521 }
522
523 fibril_mutex_unlock(&instance->guard);
524 return ret;
525}
526/**
527 * @}
528 */
Note: See TracBrowser for help on using the repository browser.