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

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

libusbhost: Make interfaces more symmetric.

Make usb_endpoint_manager interface easier to use and understand.
Move ep removal hook pointer from endpoint_t to hcd_t.

  • Property mode set to 100644
File size: 9.3 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/*----------------------------------------------------------------------------*/
122/** Check setup packet data for signs of toggle reset.
123 *
124 * @param[in] instance Device keeper structure to use.
125 * @param[in] target Device to receive setup packet.
126 * @param[in] data Setup packet data.
127 *
128 * Really ugly one.
129 */
130void usb_endpoint_manager_reset_eps_if_need(
131 usb_endpoint_manager_t *instance, usb_target_t target, const uint8_t *data)
132{
133 assert(instance);
134 if (!usb_target_is_valid(target)) {
135 usb_log_error("Invalid data when checking for toggle reset.\n");
136 return;
137 }
138
139 assert(data);
140 switch (data[1])
141 {
142 case 0x01: /* Clear Feature -- resets only cleared ep */
143 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
144 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
145 fibril_mutex_lock(&instance->guard);
146 /* endpoint number is < 16, thus first byte is enough */
147 list_foreach(*get_list(instance, target.address), it) {
148 endpoint_t *ep = endpoint_get_instance(it);
149 if ((ep->address == target.address)
150 && (ep->endpoint = data[4])) {
151 endpoint_toggle_set(ep,0);
152 }
153 }
154 fibril_mutex_unlock(&instance->guard);
155 }
156 break;
157
158 case 0x9: /* Set Configuration */
159 case 0x11: /* Set Interface */
160 /* Recipient must be device, this resets all endpoints,
161 * In fact there should be no endpoints but EP 0 registered
162 * as different interfaces use different endpoints. */
163 if ((data[0] & 0xf) == 0) {
164 fibril_mutex_lock(&instance->guard);
165 list_foreach(*get_list(instance, target.address), it) {
166 endpoint_t *ep = endpoint_get_instance(it);
167 if (ep->address == target.address) {
168 endpoint_toggle_set(ep,0);
169 }
170 }
171 fibril_mutex_unlock(&instance->guard);
172 }
173 break;
174 }
175}
176/*----------------------------------------------------------------------------*/
177int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
178 endpoint_t *ep, size_t data_size)
179{
180 assert(instance);
181 assert(instance->bw_count);
182 assert(ep);
183 fibril_mutex_lock(&instance->guard);
184
185 if (ep->bandwidth > instance->free_bw) {
186 fibril_mutex_unlock(&instance->guard);
187 return ENOSPC;
188 }
189
190 /* Check for existence */
191 const endpoint_t *endpoint =
192 find_locked(instance, ep->address, ep->endpoint, ep->direction);
193 if (endpoint != NULL) {
194 fibril_mutex_unlock(&instance->guard);
195 return EEXISTS;
196 }
197 list_t *list = get_list(instance, ep->address);
198 list_append(&ep->link, list);
199
200 instance->free_bw -= ep->bandwidth;
201 fibril_mutex_unlock(&instance->guard);
202 return EOK;
203}
204/*----------------------------------------------------------------------------*/
205int usb_endpoint_manager_unregister_ep(
206 usb_endpoint_manager_t *instance, endpoint_t *ep)
207{
208 assert(instance);
209 if (ep == NULL)
210 return ENOENT;
211 fibril_mutex_lock(&instance->guard);
212 list_remove(&ep->link);
213 fibril_mutex_unlock(&instance->guard);
214 return EOK;
215}
216/*----------------------------------------------------------------------------*/
217endpoint_t * usb_endpoint_manager_find_ep(usb_endpoint_manager_t *instance,
218 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
219{
220 assert(instance);
221
222 fibril_mutex_lock(&instance->guard);
223 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
224 fibril_mutex_unlock(&instance->guard);
225
226 return ep;
227}
228/*----------------------------------------------------------------------------*/
229int usb_endpoint_manager_add_ep(usb_endpoint_manager_t *instance,
230 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
231 usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size,
232 size_t data_size, int (*callback)(endpoint_t *, void *), void *arg)
233{
234 assert(instance);
235 const size_t bw =
236 instance->bw_count(speed, type, data_size, max_packet_size);
237
238 endpoint_t *ep = endpoint_create(
239 address, endpoint, direction, type, speed, max_packet_size, bw);
240 if (!ep)
241 return ENOMEM;
242
243 if (callback) {
244 const int ret = callback(ep, arg);
245 if (ret != EOK) {
246 endpoint_destroy(ep);
247 return ret;
248 }
249 }
250
251 const int ret =
252 usb_endpoint_manager_register_ep(instance, ep, data_size);
253 if (ret != EOK) {
254 endpoint_destroy(ep);
255 }
256 return ret;
257}
258/*----------------------------------------------------------------------------*/
259int usb_endpoint_manager_remove_ep(usb_endpoint_manager_t *instance,
260 usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
261 void (*callback)(endpoint_t *, void *), void *arg)
262{
263 assert(instance);
264 fibril_mutex_lock(&instance->guard);
265 endpoint_t *ep = find_locked(instance, address, endpoint, direction);
266 if (ep != NULL) {
267 list_remove(&ep->link);
268 instance->free_bw += ep->bandwidth;
269 }
270 fibril_mutex_unlock(&instance->guard);
271 if (ep == NULL)
272 return ENOENT;
273
274 if (callback) {
275 callback(ep, arg);
276 }
277 endpoint_destroy(ep);
278 return EOK;
279}
Note: See TracBrowser for help on using the repository browser.