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

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

libusbhost: Drop hash_table and use multiple lists instead.

This has several advantages:
Knowing that all endpoints on one address are in one list makes toggle resets more efficient.
Endpoint search is done only once for inserts and deletes.
Lists are part of the structure and not allocated separately, removing one more failure point from init path.

  • Property mode set to 100644
File size: 8.1 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
36static inline bool ep_match(const endpoint_t *ep,
37 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
38{
39 assert(ep);
40 return
41 ((direction == ep->direction)
42 || (ep->direction == USB_DIRECTION_BOTH)
43 || (direction == USB_DIRECTION_BOTH))
44 && (endpoint == ep->endpoint)
45 && (address == ep->address);
46}
47/*----------------------------------------------------------------------------*/
48static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
49{
50 assert(instance);
51 return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
52}
53/*----------------------------------------------------------------------------*/
54static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
55 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
56{
57 assert(instance);
58 assert(fibril_mutex_is_locked(&instance->guard));
59 list_foreach(*get_list(instance, address), iterator) {
60 endpoint_t *ep = endpoint_get_instance(iterator);
61 if (ep_match(ep, address, endpoint, direction))
62 return ep;
63 }
64 return NULL;
65}
66/*----------------------------------------------------------------------------*/
67size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
68 size_t size, size_t max_packet_size)
69{
70 /* We care about bandwidth only for interrupt and isochronous. */
71 if ((type != USB_TRANSFER_INTERRUPT)
72 && (type != USB_TRANSFER_ISOCHRONOUS)) {
73 return 0;
74 }
75
76 const unsigned packet_count =
77 (size + max_packet_size - 1) / max_packet_size;
78 /* TODO: It may be that ISO and INT transfers use only one packet per
79 * transaction, but I did not find text in USB spec to confirm this */
80 /* NOTE: All data packets will be considered to be max_packet_size */
81 switch (speed)
82 {
83 case USB_SPEED_LOW:
84 assert(type == USB_TRANSFER_INTERRUPT);
85 /* Protocol overhead 13B
86 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
87 * CRC bytes, and a 3-byte interpacket delay)
88 * see USB spec page 45-46. */
89 /* Speed penalty 8: low speed is 8-times slower*/
90 return packet_count * (13 + max_packet_size) * 8;
91 case USB_SPEED_FULL:
92 /* Interrupt transfer overhead see above
93 * or page 45 of USB spec */
94 if (type == USB_TRANSFER_INTERRUPT)
95 return packet_count * (13 + max_packet_size);
96
97 assert(type == USB_TRANSFER_ISOCHRONOUS);
98 /* Protocol overhead 9B
99 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
100 * bytes, and a 1-byte interpacket delay)
101 * see USB spec page 42 */
102 return packet_count * (9 + max_packet_size);
103 default:
104 return 0;
105 }
106}
107/*----------------------------------------------------------------------------*/
108int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
109 size_t available_bandwidth,
110 size_t (*bw_count)(usb_speed_t, usb_transfer_type_t, size_t, size_t))
111{
112 assert(instance);
113 fibril_mutex_initialize(&instance->guard);
114 instance->free_bw = available_bandwidth;
115 instance->bw_count = bw_count;
116 for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
117 list_initialize(&instance->endpoint_lists[i]);
118 }
119 return EOK;
120}
121/*----------------------------------------------------------------------------*/
122int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
123 endpoint_t *ep, size_t data_size)
124{
125 assert(instance);
126 assert(instance->bw_count);
127 assert(ep);
128 ep->bandwidth = instance->bw_count(ep->speed, ep->transfer_type,
129 data_size, ep->max_packet_size);
130
131 fibril_mutex_lock(&instance->guard);
132
133 if (ep->bandwidth > instance->free_bw) {
134 fibril_mutex_unlock(&instance->guard);
135 return ENOSPC;
136 }
137
138 /* Check for existence */
139 const endpoint_t *endpoint =
140 find_locked(instance, ep->address, ep->endpoint, ep->direction);
141 if (endpoint != NULL) {
142 fibril_mutex_unlock(&instance->guard);
143 return EEXISTS;
144 }
145 list_t *list = get_list(instance, ep->address);
146 list_append(&ep->link, list);
147
148 instance->free_bw -= ep->bandwidth;
149 fibril_mutex_unlock(&instance->guard);
150 return EOK;
151}
152/*----------------------------------------------------------------------------*/
153int usb_endpoint_manager_unregister_ep(usb_endpoint_manager_t *instance,
154 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
155{
156 assert(instance);
157
158 fibril_mutex_lock(&instance->guard);
159 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
160 if (ep != NULL) {
161 list_remove(&ep->link);
162 instance->free_bw += ep->bandwidth;
163 }
164
165 fibril_mutex_unlock(&instance->guard);
166 endpoint_destroy(ep);
167 return (ep != NULL) ? EOK : EINVAL;
168}
169/*----------------------------------------------------------------------------*/
170endpoint_t * usb_endpoint_manager_get_ep(usb_endpoint_manager_t *instance,
171 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
172{
173 assert(instance);
174
175 fibril_mutex_lock(&instance->guard);
176 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
177 fibril_mutex_unlock(&instance->guard);
178
179 return ep;
180}
181/*----------------------------------------------------------------------------*/
182/** Check setup packet data for signs of toggle reset.
183 *
184 * @param[in] instance Device keeper structure to use.
185 * @param[in] target Device to receive setup packet.
186 * @param[in] data Setup packet data.
187 *
188 * Really ugly one.
189 */
190void usb_endpoint_manager_reset_if_need(
191 usb_endpoint_manager_t *instance, usb_target_t target, const uint8_t *data)
192{
193 assert(instance);
194 if (!usb_target_is_valid(target)) {
195 usb_log_error("Invalid data when checking for toggle reset.\n");
196 return;
197 }
198
199 assert(data);
200 switch (data[1])
201 {
202 case 0x01: /* Clear Feature -- resets only cleared ep */
203 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
204 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
205 fibril_mutex_lock(&instance->guard);
206 /* endpoint number is < 16, thus first byte is enough */
207 list_foreach(*get_list(instance, target.address), it) {
208 endpoint_t *ep = endpoint_get_instance(it);
209 if ((ep->address == target.address)
210 && (ep->endpoint = data[4])) {
211 endpoint_toggle_set(ep,0);
212 }
213 }
214 fibril_mutex_unlock(&instance->guard);
215 }
216 break;
217
218 case 0x9: /* Set Configuration */
219 case 0x11: /* Set Interface */
220 /* Recipient must be device, this resets all endpoints,
221 * In fact there should be no endpoints but EP 0 registered
222 * as different interfaces use different endpoints. */
223 if ((data[0] & 0xf) == 0) {
224 fibril_mutex_lock(&instance->guard);
225 list_foreach(*get_list(instance, target.address), it) {
226 endpoint_t *ep = endpoint_get_instance(it);
227 if (ep->address == target.address) {
228 endpoint_toggle_set(ep,0);
229 }
230 }
231 fibril_mutex_unlock(&instance->guard);
232 }
233 break;
234 }
235}
Note: See TracBrowser for help on using the repository browser.