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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f401312 was 2c98e78, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Bugfix in USB address keeping

  • Property mode set to 100644
File size: 9.4 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 usb_address_keeping_used_t *first
190 = used_address_get_instance(addresses->used_addresses.next);
191 previous_address = first->address;
192
193 for_all_used_addresses(new_address_position, addresses) {
194 usb_address_keeping_used_t *info
195 = used_address_get_instance(new_address_position);
196 if (info->address > previous_address + 1) {
197 free_address = previous_address + 1;
198 break;
199 }
200 previous_address = info->address;
201 }
202
203 if (free_address == 0) {
204 usb_address_keeping_used_t *last
205 = used_address_get_instance(addresses->used_addresses.next);
206 free_address = last->address + 1;
207 }
208 }
209
210 if (free_address >= addresses->max_address) {
211 free_address = ELIMIT;
212 goto leave;
213 }
214
215 usb_address_keeping_used_t *used
216 = usb_address_keeping_used_create(free_address);
217 if (used == NULL) {
218 free_address = ENOMEM;
219 goto leave;
220 }
221
222 list_prepend(&used->link, new_address_position);
223
224leave:
225 fibril_mutex_unlock(&addresses->used_addresses_guard);
226
227 return free_address;
228}
229
230/** Release USB address.
231 *
232 * @param addresses Address keeping info.
233 * @param address Address to be released.
234 * @return Error code.
235 * @retval ENOENT Address is not in use.
236 */
237int usb_address_keeping_release(usb_address_keeping_t *addresses,
238 usb_address_t address)
239{
240 int rc = ENOENT;
241
242 fibril_mutex_lock(&addresses->used_addresses_guard);
243
244 usb_address_keeping_used_t *info
245 = usb_address_keeping_used_find_no_lock(addresses, address);
246
247 if (info != NULL) {
248 rc = EOK;
249 list_remove(&info->link);
250 usb_address_keeping_used_destroy(info);
251 }
252
253 fibril_mutex_unlock(&addresses->used_addresses_guard);
254
255 return rc;
256}
257
258/** Bind devman handle with USB address.
259 *
260 * When the @p address is invalid (e.g. no such entry), the request
261 * is silently ignored.
262 *
263 * @param addresses Address keeping info.
264 * @param address USB address.
265 * @param handle Devman handle.
266 */
267void usb_address_keeping_devman_bind(usb_address_keeping_t *addresses,
268 usb_address_t address, devman_handle_t handle)
269{
270 fibril_mutex_lock(&addresses->used_addresses_guard);
271
272 usb_address_keeping_used_t *info
273 = usb_address_keeping_used_find_no_lock(addresses, address);
274 if (info == NULL) {
275 goto leave;
276 }
277
278 assert(info->address == address);
279 info->devman_handle = handle;
280
281 /*
282 * Inform that new handle was added.
283 */
284 fibril_condvar_broadcast(&addresses->used_addresses_condvar);
285
286leave:
287 fibril_mutex_unlock(&addresses->used_addresses_guard);
288}
289
290/** Find address by its devman handle.
291 *
292 * @param addresses Address keeping info.
293 * @param handle Devman handle.
294 * @return USB address or negative error code.
295 * @retval ENOENT No such address.
296 */
297static usb_address_t usb_address_keeping_find_no_lock(
298 usb_address_keeping_t *addresses, devman_handle_t handle)
299{
300 usb_address_t address = ENOENT;
301
302 link_t *link;
303 for_all_used_addresses(link, addresses) {
304 usb_address_keeping_used_t *info
305 = used_address_get_instance(link);
306
307 if (info->devman_handle == handle) {
308 address = info->address;
309 break;
310 }
311 }
312
313 return address;
314}
315
316/** Find USB address by its devman handle.
317 *
318 * This function blocks until corresponding address is found.
319 *
320 * @param addresses Address keeping info.
321 * @param handle Devman handle.
322 * @return USB address or negative error code.
323 */
324usb_address_t usb_address_keeping_find(usb_address_keeping_t *addresses,
325 devman_handle_t handle)
326{
327 usb_address_t address = ENOENT;
328
329 fibril_mutex_lock(&addresses->used_addresses_guard);
330 while (true) {
331 address = usb_address_keeping_find_no_lock(addresses, handle);
332 if (address != ENOENT) {
333 break;
334 }
335 fibril_condvar_wait(&addresses->used_addresses_condvar,
336 &addresses->used_addresses_guard);
337 }
338
339 fibril_mutex_unlock(&addresses->used_addresses_guard);
340
341 return address;
342}
343
344/**
345 * @}
346 */
Note: See TracBrowser for help on using the repository browser.