source: mainline/uspace/lib/usb/src/addrkeep.c@ 682b697

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 682b697 was 9223dc5c, checked in by Matus Dekanek <smekideki@…>, 15 years ago

fixes for hub and hcd, left debug messsages

  • Property mode set to 100644
File size: 9.2 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
3 * All rights reserved.
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/** @addtogroup libusb usb
30 * @{
31 */
32/** @file
33 * @brief Address keeping.
34 */
35#include <usb/hcd.h>
36#include <errno.h>
37#include <assert.h>
38
39/** For loop over all used addresses in address keeping.
40 *
41 * @param link Iterator.
42 * @param addresses Addresses keeping structure to iterate.
43 */
44#define for_all_used_addresses(link, addresses) \
45 for (link = (addresses)->used_addresses.next; \
46 link != &(addresses)->used_addresses; \
47 link = link->next)
48
49/** Get instance of usb_address_keeping_used_t. */
50#define used_address_get_instance(lnk) \
51 list_get_instance(lnk, usb_address_keeping_used_t, link)
52
53/** Invalid value of devman handle. */
54#define INVALID_DEVMAN_HANDLE \
55 ((devman_handle_t)-1)
56
57/** Creates structure for used USB address.
58 *
59 * @param address USB address.
60 * @return Initialized structure.
61 * @retval NULL Out of memory.
62 */
63static usb_address_keeping_used_t *usb_address_keeping_used_create(
64 usb_address_t address)
65{
66 usb_address_keeping_used_t *info
67 = malloc(sizeof(usb_address_keeping_used_t));
68 if (info == NULL) {
69 return NULL;
70 }
71
72 info->address = address;
73 info->devman_handle = INVALID_DEVMAN_HANDLE;
74 list_initialize(&info->link);
75 return info;
76}
77
78/** Destroys structure for used USB address.
79 *
80 * @param info Structure to be destroyed.
81 */
82static void usb_address_keeping_used_destroy(usb_address_keeping_used_t *info)
83{
84 free(info);
85}
86
87/** Find used USB address structure by USB address.
88 *
89 * It is expected that guard mutex is already locked.
90 *
91 * @param addresses Address keeping info.
92 * @param address Address to be found.
93 * @return Structure describing looked for address.
94 * @retval NULL Address not found.
95 */
96static usb_address_keeping_used_t *usb_address_keeping_used_find_no_lock(
97 usb_address_keeping_t *addresses, usb_address_t address)
98{
99 link_t *link;
100 for_all_used_addresses(link, addresses) {
101 usb_address_keeping_used_t *info
102 = used_address_get_instance(link);
103
104 if (info->address == address) {
105 return info;
106 }
107 }
108
109 return NULL;
110}
111
112/** Initialize address keeping structure.
113 *
114 * @param addresses Address keeping info.
115 * @param max_address Maximum USB address (exclusive bound).
116 */
117void usb_address_keeping_init(usb_address_keeping_t *addresses,
118 usb_address_t max_address)
119{
120 /*
121 * Items related with used addresses.
122 */
123 addresses->max_address = max_address;
124 list_initialize(&addresses->used_addresses);
125 fibril_mutex_initialize(&addresses->used_addresses_guard);
126 fibril_condvar_initialize(&addresses->used_addresses_condvar);
127
128 /*
129 * Items related with default address.
130 */
131 addresses->default_available = true;
132 fibril_condvar_initialize(&addresses->default_condvar);
133 fibril_mutex_initialize(&addresses->default_condvar_guard);
134}
135
136/** Reserved default USB address.
137 *
138 * This function blocks until reserved address is available.
139 *
140 * @see usb_address_keeping_release_default
141 *
142 * @param addresses Address keeping info.
143 */
144void usb_address_keeping_reserve_default(usb_address_keeping_t *addresses)
145{
146 fibril_mutex_lock(&addresses->default_condvar_guard);
147 while (!addresses->default_available) {
148 fibril_condvar_wait(&addresses->default_condvar,
149 &addresses->default_condvar_guard);
150 }
151 fibril_mutex_unlock(&addresses->default_condvar_guard);
152}
153
154/** Releases default USB address.
155 *
156 * @see usb_address_keeping_reserve_default
157 *
158 * @param addresses Address keeping info.
159 */
160void usb_address_keeping_release_default(usb_address_keeping_t *addresses)
161{
162 fibril_mutex_lock(&addresses->default_condvar_guard);
163 addresses->default_available = true;
164 fibril_condvar_signal(&addresses->default_condvar);
165 fibril_mutex_unlock(&addresses->default_condvar_guard);
166}
167
168/** Request free address assignment.
169 *
170 * This function does not block when there are not free addresses to be
171 * assigned.
172 *
173 * @param addresses Address keeping info.
174 * @return USB address that could be used or negative error code.
175 * @retval ELIMIT No more addresses to assign.
176 * @retval ENOMEM Out of memory.
177 */
178usb_address_t usb_address_keeping_request(usb_address_keeping_t *addresses)
179{
180 usb_address_t previous_address = 0;
181 usb_address_t free_address = 0;
182
183 fibril_mutex_lock(&addresses->used_addresses_guard);
184 link_t *new_address_position;
185 if (list_empty(&addresses->used_addresses)) {
186 free_address = 1;
187 new_address_position = addresses->used_addresses.next;
188 } else {
189 for_all_used_addresses(new_address_position, addresses) {
190 usb_address_keeping_used_t *info
191 = used_address_get_instance(new_address_position);
192 if (info->address > previous_address + 1) {
193 free_address = previous_address + 1;
194 break;
195 }
196 }
197
198 if (free_address == 0) {
199 usb_address_keeping_used_t *last
200 = used_address_get_instance(addresses->used_addresses.next);
201 free_address = last->address + 1;
202 }
203 }
204
205 if (free_address >= addresses->max_address) {
206 free_address = ELIMIT;
207 goto leave;
208 }
209
210 usb_address_keeping_used_t *used
211 = usb_address_keeping_used_create(free_address);
212 if (used == NULL) {
213 free_address = ENOMEM;
214 goto leave;
215 }
216
217 list_prepend(&used->link, new_address_position);
218
219leave:
220 fibril_mutex_unlock(&addresses->used_addresses_guard);
221
222 return free_address;
223}
224
225/** Release USB address.
226 *
227 * @param addresses Address keeping info.
228 * @param address Address to be released.
229 * @return Error code.
230 * @retval ENOENT Address is not in use.
231 */
232int usb_address_keeping_release(usb_address_keeping_t *addresses,
233 usb_address_t address)
234{
235 int rc = ENOENT;
236
237 fibril_mutex_lock(&addresses->used_addresses_guard);
238
239 usb_address_keeping_used_t *info
240 = usb_address_keeping_used_find_no_lock(addresses, address);
241
242 if (info != NULL) {
243 rc = EOK;
244 list_remove(&info->link);
245 usb_address_keeping_used_destroy(info);
246 }
247
248 fibril_mutex_unlock(&addresses->used_addresses_guard);
249
250 return rc;
251}
252
253/** Bind devman handle with USB address.
254 *
255 * When the @p address is invalid (e.g. no such entry), the request
256 * is silently ignored.
257 *
258 * @param addresses Address keeping info.
259 * @param address USB address.
260 * @param handle Devman handle.
261 */
262void usb_address_keeping_devman_bind(usb_address_keeping_t *addresses,
263 usb_address_t address, devman_handle_t handle)
264{
265 fibril_mutex_lock(&addresses->used_addresses_guard);
266
267 usb_address_keeping_used_t *info
268 = usb_address_keeping_used_find_no_lock(addresses, address);
269 if (info == NULL) {
270 goto leave;
271 }
272
273 assert(info->address == address);
274 info->devman_handle = handle;
275
276 /*
277 * Inform that new handle was added.
278 */
279 fibril_condvar_broadcast(&addresses->used_addresses_condvar);
280
281leave:
282 fibril_mutex_unlock(&addresses->used_addresses_guard);
283}
284
285/** Find address by its devman handle.
286 *
287 * @param addresses Address keeping info.
288 * @param handle Devman handle.
289 * @return USB address or negative error code.
290 * @retval ENOENT No such address.
291 */
292static usb_address_t usb_address_keeping_find_no_lock(
293 usb_address_keeping_t *addresses, devman_handle_t handle)
294{
295 usb_address_t address = ENOENT;
296
297 link_t *link;
298 for_all_used_addresses(link, addresses) {
299 usb_address_keeping_used_t *info
300 = used_address_get_instance(link);
301
302 if (info->devman_handle == handle) {
303 address = info->address;
304 break;
305 }
306 }
307
308 return address;
309}
310
311/** Find USB address by its devman handle.
312 *
313 * This function blocks until corresponding address is found.
314 *
315 * @param addresses Address keeping info.
316 * @param handle Devman handle.
317 * @return USB address or negative error code.
318 */
319usb_address_t usb_address_keeping_find(usb_address_keeping_t *addresses,
320 devman_handle_t handle)
321{
322 usb_address_t address = ENOENT;
323
324 fibril_mutex_lock(&addresses->used_addresses_guard);
325 while (true) {
326 address = usb_address_keeping_find_no_lock(addresses, handle);
327 if (address != ENOENT) {
328 break;
329 }
330 fibril_condvar_wait(&addresses->used_addresses_condvar,
331 &addresses->used_addresses_guard);
332 }
333
334 fibril_mutex_unlock(&addresses->used_addresses_guard);
335
336 return address;
337}
338
339/**
340 * @}
341 */
Note: See TracBrowser for help on using the repository browser.