source: mainline/uspace/drv/bus/usb/xhci/bus.c@ 81487c4a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 81487c4a was ee794529, checked in by Petr Manek <petr.manek@…>, 8 years ago

Refactoring. Renamed functions in bus endpoint interface. Register/release is now register/unregister to prevent name collisions with use/release.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
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/** @addtogroup libusbhost
29 * @{
30 */
31/** @file
32 * HC Endpoint management.
33 */
34
35#include <adt/hash_table.h>
36#include <adt/hash.h>
37#include <usb/host/utils/malloc32.h>
38#include <usb/host/ddf_helpers.h>
39#include <usb/host/endpoint.h>
40#include <usb/host/hcd.h>
41#include <usb/debug.h>
42
43#include <assert.h>
44#include <errno.h>
45#include <str_error.h>
46#include <macros.h>
47#include <stdbool.h>
48
49#include "bus.h"
50#include "endpoint.h"
51#include "transfers.h"
52
53/** Element of the hash table. */
54typedef struct {
55 ht_link_t link;
56
57 /** Device */
58 xhci_device_t *device;
59} hashed_device_t;
60
61static int hashed_device_insert(xhci_bus_t *bus, hashed_device_t **hashed_dev, xhci_device_t *dev);
62static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev);
63static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev);
64
65/** TODO: Still some copy-pasta left...
66 */
67int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
68{
69 int err;
70 xhci_device_t *xhci_dev = xhci_device_get(dev);
71
72 /* TODO: get speed from the default address reservation */
73 dev->speed = USB_SPEED_FULL;
74
75 /* Manage TT */
76 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
77 /* For LS devices under HS hub */
78 /* TODO: How about SS hubs? */
79 dev->tt.address = dev->hub->address;
80 dev->tt.port = dev->port;
81 }
82 else {
83 /* Inherit hub's TT */
84 dev->tt = dev->hub->tt;
85 }
86
87 /* Assign an address to the device */
88 if ((err = xhci_rh_address_device(&hc->rh, dev, bus))) {
89 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
90 return err;
91 }
92
93 assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
94 bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
95
96 hashed_device_t *hashed_dev = NULL;
97 if ((err = hashed_device_insert(bus, &hashed_dev, xhci_dev)))
98 goto err_address;
99
100 /* Read the device descriptor, derive the match ids */
101 if ((err = hcd_ddf_device_explore(hc->hcd, dev))) {
102 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
103 goto err_hash;
104 }
105
106 return EOK;
107
108err_hash:
109 bus->devices_by_slot[xhci_dev->slot_id] = NULL;
110 hashed_device_remove(bus, hashed_dev);
111err_address:
112 bus_release_address(&bus->base, dev->address);
113 return err;
114}
115
116int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
117{
118 xhci_device_t *xhci_dev = xhci_device_get(dev);
119
120 /* Unregister remaining endpoints. */
121 for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
122 if (!xhci_dev->endpoints[i])
123 continue;
124
125 // FIXME: ignoring return code
126 bus_unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base);
127 }
128
129 hashed_device_t *hashed_dev;
130 int res = hashed_device_find_by_address(bus, dev->address, &hashed_dev);
131 if (res)
132 return res;
133
134 res = hashed_device_remove(bus, hashed_dev);
135 if (res != EOK)
136 return res;
137
138 // XXX: Ugly here. Move to device_destroy at endpoint.c?
139 free32(xhci_dev->dev_ctx);
140 hc->dcbaa[xhci_dev->slot_id] = 0;
141 return ENOTSUP;
142}
143
144/** Ops receive generic bus_t pointer. */
145static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
146{
147 assert(bus_base);
148 return (xhci_bus_t *) bus_base;
149}
150
151static int enumerate_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
152{
153 xhci_hc_t *hc = hcd_get_driver_data(hcd);
154 assert(hc);
155
156 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
157 assert(bus);
158
159 return xhci_bus_enumerate_device(bus, hc, dev);
160}
161
162static int remove_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
163{
164 xhci_hc_t *hc = hcd_get_driver_data(hcd);
165 assert(hc);
166
167 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
168 assert(bus);
169
170 return xhci_bus_remove_device(bus, hc, dev);
171}
172
173static endpoint_t *create_endpoint(bus_t *base)
174{
175 xhci_bus_t *bus = bus_to_xhci_bus(base);
176
177 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
178 if (!ep)
179 return NULL;
180
181 if (xhci_endpoint_init(ep, bus)) {
182 free(ep);
183 return NULL;
184 }
185
186 return &ep->base;
187}
188
189static void destroy_endpoint(endpoint_t *ep)
190{
191 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
192
193 xhci_endpoint_fini(xhci_ep);
194 free(xhci_ep);
195}
196
197static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
198{
199 ht_link_t *link = hash_table_find(&bus->devices, &address);
200 if (link == NULL)
201 return ENOENT;
202
203 *dev = hash_table_get_inst(link, hashed_device_t, link);
204 return EOK;
205}
206
207static int xhci_endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, xhci_endpoint_t **ep)
208{
209 hashed_device_t *dev;
210 int res = hashed_device_find_by_address(bus, target.address, &dev);
211 if (res != EOK)
212 return res;
213
214 xhci_endpoint_t *ret_ep = xhci_device_get_endpoint(dev->device, target.endpoint);
215 if (!ret_ep)
216 return ENOENT;
217
218 *ep = ret_ep;
219 return EOK;
220}
221
222static int hashed_device_insert(xhci_bus_t *bus, hashed_device_t **hashed_dev, xhci_device_t *dev)
223{
224 hashed_device_t *ret_dev = (hashed_device_t *) calloc(1, sizeof(hashed_device_t));
225 if (!ret_dev)
226 return ENOMEM;
227
228 ret_dev->device = dev;
229
230 usb_log_info("Device(%d) registered to XHCI bus.", dev->base.address);
231
232 hash_table_insert(&bus->devices, &ret_dev->link);
233 *hashed_dev = ret_dev;
234 return EOK;
235}
236
237static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
238{
239 usb_log_info("Device(%d) released from XHCI bus.", hashed_dev->device->base.address);
240
241 hash_table_remove(&bus->devices, &hashed_dev->device->base.address);
242 free(hashed_dev);
243
244 return EOK;
245}
246
247static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
248{
249 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
250 assert(bus);
251
252 usb_log_info("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
253
254 xhci_device_t *xhci_dev = xhci_device_get(ep->device);
255 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
256 return xhci_device_add_endpoint(xhci_dev, xhci_ep);
257}
258
259static int unregister_endpoint(bus_t *bus_base, endpoint_t *ep)
260{
261 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
262 assert(bus);
263
264 usb_log_info("Endpoint(%d:%d) unregistered from XHCI bus.", ep->target.address, ep->target.endpoint);
265
266 xhci_device_t *xhci_dev = xhci_device_get(ep->device);
267 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
268 const int res = xhci_device_remove_endpoint(xhci_dev, xhci_ep);
269 if (res != EOK)
270 return res;
271
272 return EOK;
273}
274
275static endpoint_t* find_endpoint(bus_t *bus_base, usb_target_t target, usb_direction_t direction)
276{
277 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
278 assert(bus);
279
280 xhci_endpoint_t *ep;
281 int res = xhci_endpoint_find_by_target(bus, target, &ep);
282 if (res != EOK)
283 return NULL;
284
285 return &ep->base;
286}
287
288static int reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
289{
290 // TODO: Implement me!
291 return ENOTSUP;
292}
293
294static size_t count_bw(endpoint_t *ep, size_t size)
295{
296 // TODO: Implement me!
297 return 0;
298}
299
300/* Endpoint ops, optional (have generic fallback) */
301static bool endpoint_get_toggle(endpoint_t *ep)
302{
303 // TODO: Implement me!
304 return ENOTSUP;
305}
306
307static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
308{
309 // TODO: Implement me!
310}
311
312static usb_transfer_batch_t *create_batch(bus_t *bus, endpoint_t *ep)
313{
314 xhci_transfer_t *transfer = xhci_transfer_create(ep);
315 return &transfer->batch;
316}
317
318static void destroy_batch(usb_transfer_batch_t *batch)
319{
320 xhci_transfer_destroy(xhci_transfer_from_batch(batch));
321}
322
323static const bus_ops_t xhci_bus_ops = {
324 .enumerate_device = enumerate_device,
325 .remove_device = remove_device,
326
327 .create_endpoint = create_endpoint,
328 .destroy_endpoint = destroy_endpoint,
329
330 .register_endpoint = register_endpoint,
331 .unregister_endpoint = unregister_endpoint,
332 .find_endpoint = find_endpoint,
333
334 .request_address = NULL,
335 .release_address = NULL,
336 .reset_toggle = reset_toggle,
337
338 .count_bw = count_bw,
339
340 .endpoint_get_toggle = endpoint_get_toggle,
341 .endpoint_set_toggle = endpoint_set_toggle,
342
343 .create_batch = create_batch,
344 .destroy_batch = destroy_batch,
345};
346
347static size_t device_ht_hash(const ht_link_t *item)
348{
349 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
350 return (size_t) hash_mix(dev->device->base.address);
351}
352
353static size_t device_ht_key_hash(void *key)
354{
355 return (size_t) hash_mix(*(usb_address_t *)key);
356}
357
358static bool device_ht_key_equal(void *key, const ht_link_t *item)
359{
360 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
361 return dev->device->base.address == *(usb_address_t *) key;
362}
363
364/** Operations for the device hash table. */
365static hash_table_ops_t device_ht_ops = {
366 .hash = device_ht_hash,
367 .key_hash = device_ht_key_hash,
368 .key_equal = device_ht_key_equal,
369 .equal = NULL,
370 .remove_callback = NULL
371};
372
373int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
374{
375 assert(bus);
376
377 bus_init(&bus->base, sizeof(xhci_device_t));
378
379 bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
380 if (!bus->devices_by_slot)
381 return ENOMEM;
382
383 if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
384 free(bus->devices_by_slot);
385 return ENOMEM;
386 }
387
388 bus->base.ops = xhci_bus_ops;
389 return EOK;
390}
391
392void xhci_bus_fini(xhci_bus_t *bus)
393{
394 // FIXME: Make sure no devices are in the hash table.
395
396 hash_table_destroy(&bus->devices);
397}
398
399/**
400 * @}
401 */
Note: See TracBrowser for help on using the repository browser.