source: mainline/uspace/lib/usbhost/src/usb2_bus.c@ f9d787c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f9d787c was fc0271a5, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

WIP usbhost refactoring: uhci converted

  • Property mode set to 100644
File size: 8.6 KB
RevLine 
[41924f30]1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 * HC Endpoint management.
34 */
35
36#include <usb/host/usb2_bus.h>
37#include <usb/host/endpoint.h>
38#include <usb/debug.h>
39
40#include <assert.h>
41#include <errno.h>
42#include <macros.h>
43#include <stdbool.h>
44
45/** Ops receive generic bus_t pointer. */
46static inline usb2_bus_t *bus_to_usb2_bus(bus_t *bus_base)
47{
48 assert(bus_base);
49 return (usb2_bus_t *) bus_base;
50}
51
52/** Get list that holds endpoints for given address.
53 * @param bus usb2_bus structure, non-null.
54 * @param addr USB address, must be >= 0.
55 * @return Pointer to the appropriate list.
56 */
57static list_t * get_list(usb2_bus_t *bus, usb_address_t addr)
58{
59 assert(bus);
60 assert(addr >= 0);
61 return &bus->devices[addr % ARRAY_SIZE(bus->devices)].endpoint_list;
62}
63
64/** Get a free USB address
65 *
66 * @param[in] bus Device manager structure to use.
67 * @return Free address, or error code.
68 */
69static int usb_bus_get_free_address(usb2_bus_t *bus, usb_address_t *addr)
70{
71 usb_address_t new_address = bus->last_address;
72 do {
73 new_address = (new_address + 1) % USB_ADDRESS_COUNT;
74 if (new_address == USB_ADDRESS_DEFAULT)
75 new_address = 1;
76 if (new_address == bus->last_address)
77 return ENOSPC;
78 } while (bus->devices[new_address].occupied);
79
80 assert(new_address != USB_ADDRESS_DEFAULT);
81 bus->last_address = new_address;
82
83 *addr = new_address;
84 return EOK;
85}
86
87/** Find endpoint.
88 * @param bus usb_bus structure, non-null.
89 * @param target Endpoint address.
90 * @param direction Communication direction.
91 * @return Pointer to endpoint_t structure representing given communication
92 * target, NULL if there is no such endpoint registered.
93 * @note Assumes that the internal mutex is locked.
94 */
95static endpoint_t *usb2_bus_find_ep(bus_t *bus_base, usb_target_t target, usb_direction_t direction)
96{
97 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
98
99 if (!usb_address_is_valid(target.address))
100 return NULL;
101
102 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
103 if (((direction == ep->direction)
104 || (ep->direction == USB_DIRECTION_BOTH)
105 || (direction == USB_DIRECTION_BOTH))
106 && (target.packed == ep->target.packed))
107 return ep;
108 }
109 return NULL;
110}
111
112/** Register an endpoint to the bus. Reserves bandwidth.
113 * @param bus usb_bus structure, non-null.
114 * @param endpoint USB endpoint number.
115 */
116static int usb2_bus_register_ep(bus_t *bus_base, endpoint_t *ep)
117{
118 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
119 assert(ep);
120
121 usb_address_t address = ep->target.address;
122
123 if (!usb_address_is_valid(address))
124 return EINVAL;
125
126 /* Check for speed and address */
127 if (!bus->devices[address].occupied)
128 return ENOENT;
129
130 /* Check for existence */
131 if (usb2_bus_find_ep(bus_base, ep->target, ep->direction))
132 return EEXIST;
133
134 /* Check for available bandwidth */
135 if (ep->bandwidth > bus->free_bw)
136 return ENOSPC;
137
138 list_append(&ep->link, get_list(bus, ep->target.address));
139 bus->free_bw -= ep->bandwidth;
140
141 return EOK;
142}
143
144
145/** Release bandwidth reserved by the given endpoint.
146 */
147static int usb2_bus_release_ep(bus_t *bus_base, endpoint_t *ep)
148{
149 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
150 assert(ep);
151
152 bus->free_bw += ep->bandwidth;
153
154 return EOK;
155}
156
157static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
158{
159 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
160
161 if (!usb_target_is_valid(target))
162 return EINVAL;
163
164 int ret = ENOENT;
165
166 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
167 if ((ep->target.address == target.address)
168 && (all || ep->target.endpoint == target.endpoint)) {
169 endpoint_toggle_set(ep, 0);
170 ret = EOK;
171 }
172 }
173 return ret;
174}
175
176/** Unregister and destroy all endpoints using given address.
177 * @param bus usb_bus structure, non-null.
178 * @param address USB address.
179 * @param endpoint USB endpoint number.
180 * @param direction Communication direction.
181 * @param callback Function to call after unregister, before destruction.
182 * @arg Argument to pass to the callback function.
183 * @return Error code.
184 */
185static int usb2_bus_release_address(bus_t *bus_base, usb_address_t address)
186{
187 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
188
189 if (!usb_address_is_valid(address))
190 return EINVAL;
191
192 const int ret = bus->devices[address].occupied ? EOK : ENOENT;
193 bus->devices[address].occupied = false;
194
195 list_t *list = get_list(bus, address);
196 for (link_t *link = list_first(list); link != NULL; ) {
197 endpoint_t *ep = list_get_instance(link, endpoint_t, link);
198 link = list_next(link, list);
199 assert(ep->target.address == address);
200 list_remove(&ep->link);
201
202 /* Drop bus reference */
203 endpoint_del_ref(ep);
204 }
205
206 return ret;
207}
208
209/** Request USB address.
210 * @param bus usb_device_manager
211 * @param addr Pointer to requested address value, place to store new address
212 * @parma strict Fail if the requested address is not available.
213 * @return Error code.
214 * @note Default address is only available in strict mode.
215 */
216static int usb2_bus_request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
217{
218 int err;
219
220 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
221 assert(addr);
222
223 if (!usb_address_is_valid(*addr))
224 return EINVAL;
225
226 /* Only grant default address to strict requests */
227 if ((*addr == USB_ADDRESS_DEFAULT) && !strict) {
228 if ((err = usb_bus_get_free_address(bus, addr)))
229 return err;
230 }
231 else if (bus->devices[*addr].occupied) {
232 if (strict) {
233 return ENOENT;
234 }
235 if ((err = usb_bus_get_free_address(bus, addr)))
236 return err;
237 }
238
239 assert(usb_address_is_valid(*addr));
240 assert(bus->devices[*addr].occupied == false);
241 assert(*addr != USB_ADDRESS_DEFAULT || strict);
242
243 bus->devices[*addr].occupied = true;
244 bus->devices[*addr].speed = speed;
245
246 return EOK;
247}
248
249/** Get speed assigned to USB address.
250 *
251 * @param[in] bus Device manager structure to use.
252 * @param[in] address Address the caller wants to find.
253 * @param[out] speed Assigned speed.
254 * @return Error code.
255 */
256static int usb2_bus_get_speed(bus_t *bus_base, usb_address_t address, usb_speed_t *speed)
257{
258 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
259
260 if (!usb_address_is_valid(address)) {
261 return EINVAL;
262 }
263
264 const int ret = bus->devices[address].occupied ? EOK : ENOENT;
265 if (speed && bus->devices[address].occupied) {
266 *speed = bus->devices[address].speed;
267 }
268
269 return ret;
270}
271
272static const bus_ops_t usb2_bus_ops = {
273 .find_endpoint = usb2_bus_find_ep,
274 .release_endpoint = usb2_bus_release_ep,
275 .register_endpoint = usb2_bus_register_ep,
276 .request_address = usb2_bus_request_address,
277 .release_address = usb2_bus_release_address,
278 .reset_toggle = usb2_bus_reset_toggle,
279 .get_speed = usb2_bus_get_speed,
280};
281
282/** Initialize to default state.
283 *
284 * @param bus usb_bus structure, non-null.
285 * @param available_bandwidth Size of the bandwidth pool.
286 * @param bw_count function to use to calculate endpoint bw requirements.
287 * @return Error code.
288 */
289int usb2_bus_init(usb2_bus_t *bus, hcd_t *hcd, size_t available_bandwidth, count_bw_func_t count_bw)
290{
291 assert(bus);
292
[fc0271a5]293 bus_init(&bus->base, hcd);
[41924f30]294
[fc0271a5]295 bus->base.ops = usb2_bus_ops;
296 bus->base.ops.count_bw = count_bw;
[41924f30]297
298 bus->free_bw = available_bandwidth;
299 bus->last_address = 0;
300 for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
301 list_initialize(&bus->devices[i].endpoint_list);
302 bus->devices[i].speed = USB_SPEED_MAX;
303 bus->devices[i].occupied = false;
304 }
305 return EOK;
306}
307/**
308 * @}
309 */
Note: See TracBrowser for help on using the repository browser.