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
RevLine 
[a5976973]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
[a8435eb5]35#include <adt/hash_table.h>
36#include <adt/hash.h>
[2b61945]37#include <usb/host/utils/malloc32.h>
[20eaa82]38#include <usb/host/ddf_helpers.h>
[a5976973]39#include <usb/host/endpoint.h>
[20eaa82]40#include <usb/host/hcd.h>
[a5976973]41#include <usb/debug.h>
42
43#include <assert.h>
44#include <errno.h>
[20eaa82]45#include <str_error.h>
[a5976973]46#include <macros.h>
47#include <stdbool.h>
48
49#include "bus.h"
50#include "endpoint.h"
[5fd9c30]51#include "transfers.h"
[a5976973]52
[a8435eb5]53/** Element of the hash table. */
54typedef struct {
55 ht_link_t link;
56
[c10daa8]57 /** Device */
58 xhci_device_t *device;
59} hashed_device_t;
[a8435eb5]60
[2b61945]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
[20eaa82]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;
[2b61945]70 xhci_device_t *xhci_dev = xhci_device_get(dev);
[20eaa82]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 */
[2770b66]88 if ((err = xhci_rh_address_device(&hc->rh, dev, bus))) {
[20eaa82]89 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
90 return err;
91 }
92
[2b61945]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
[20eaa82]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));
[2b61945]103 goto err_hash;
[20eaa82]104 }
105
106 return EOK;
[2b61945]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;
[20eaa82]114}
115
[8ea7459]116int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
[20eaa82]117{
[2b61945]118 xhci_device_t *xhci_dev = xhci_device_get(dev);
119
[ee794529]120 /* Unregister remaining endpoints. */
[a4e26882]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
[ee794529]126 bus_unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base);
[a4e26882]127 }
[2b61945]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;
[20eaa82]141 return ENOTSUP;
142}
143
[a5976973]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
[8ea7459]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
[a5976973]173static endpoint_t *create_endpoint(bus_t *base)
174{
175 xhci_bus_t *bus = bus_to_xhci_bus(base);
176
[2b61945]177 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
[a5976973]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
[c10daa8]197static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
[a8435eb5]198{
[c10daa8]199 ht_link_t *link = hash_table_find(&bus->devices, &address);
[a8435eb5]200 if (link == NULL)
201 return ENOENT;
202
[c10daa8]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
[2b61945]222static int hashed_device_insert(xhci_bus_t *bus, hashed_device_t **hashed_dev, xhci_device_t *dev)
[c10daa8]223{
[2b61945]224 hashed_device_t *ret_dev = (hashed_device_t *) calloc(1, sizeof(hashed_device_t));
225 if (!ret_dev)
226 return ENOMEM;
[c10daa8]227
228 ret_dev->device = dev;
229
[2b61945]230 usb_log_info("Device(%d) registered to XHCI bus.", dev->base.address);
[9b2f69e]231
[c10daa8]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{
[2b61945]239 usb_log_info("Device(%d) released from XHCI bus.", hashed_dev->device->base.address);
[9b2f69e]240
[2b61945]241 hash_table_remove(&bus->devices, &hashed_dev->device->base.address);
[c10daa8]242 free(hashed_dev);
243
[a8435eb5]244 return EOK;
245}
246
[add878aa]247static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
248{
[a8435eb5]249 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
250 assert(bus);
251
[d1d7a92]252 usb_log_info("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
[9b2f69e]253
[2b61945]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);
[add878aa]257}
258
[ee794529]259static int unregister_endpoint(bus_t *bus_base, endpoint_t *ep)
[add878aa]260{
[a8435eb5]261 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
262 assert(bus);
263
[ee794529]264 usb_log_info("Endpoint(%d:%d) unregistered from XHCI bus.", ep->target.address, ep->target.endpoint);
[9b2f69e]265
[2b61945]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);
[8ea7459]269 if (res != EOK)
270 return res;
[c10daa8]271
[a8435eb5]272 return EOK;
[add878aa]273}
274
275static endpoint_t* find_endpoint(bus_t *bus_base, usb_target_t target, usb_direction_t direction)
276{
[a8435eb5]277 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
278 assert(bus);
[a4e26882]279
[c10daa8]280 xhci_endpoint_t *ep;
281 int res = xhci_endpoint_find_by_target(bus, target, &ep);
[a8435eb5]282 if (res != EOK)
283 return NULL;
284
[c10daa8]285 return &ep->base;
[add878aa]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) */
[e6b9182]301static bool endpoint_get_toggle(endpoint_t *ep)
[add878aa]302{
303 // TODO: Implement me!
304 return ENOTSUP;
305}
306
[e6b9182]307static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
[add878aa]308{
309 // TODO: Implement me!
310}
311
[5fd9c30]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
[a5976973]323static const bus_ops_t xhci_bus_ops = {
[20eaa82]324 .enumerate_device = enumerate_device,
325 .remove_device = remove_device,
326
[a5976973]327 .create_endpoint = create_endpoint,
328 .destroy_endpoint = destroy_endpoint,
329
[add878aa]330 .register_endpoint = register_endpoint,
[ee794529]331 .unregister_endpoint = unregister_endpoint,
[add878aa]332 .find_endpoint = find_endpoint,
[a5976973]333
[fe1c48d]334 .request_address = NULL,
335 .release_address = NULL,
[add878aa]336 .reset_toggle = reset_toggle,
[a5976973]337
[add878aa]338 .count_bw = count_bw,
[a5976973]339
[add878aa]340 .endpoint_get_toggle = endpoint_get_toggle,
341 .endpoint_set_toggle = endpoint_set_toggle,
[5fd9c30]342
343 .create_batch = create_batch,
344 .destroy_batch = destroy_batch,
[a5976973]345};
346
[c10daa8]347static size_t device_ht_hash(const ht_link_t *item)
[a8435eb5]348{
[c10daa8]349 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
[2b61945]350 return (size_t) hash_mix(dev->device->base.address);
[a8435eb5]351}
352
[c10daa8]353static size_t device_ht_key_hash(void *key)
[a8435eb5]354{
[c10daa8]355 return (size_t) hash_mix(*(usb_address_t *)key);
[a8435eb5]356}
357
[c10daa8]358static bool device_ht_key_equal(void *key, const ht_link_t *item)
[a8435eb5]359{
[c10daa8]360 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
[2b61945]361 return dev->device->base.address == *(usb_address_t *) key;
[a8435eb5]362}
363
[c10daa8]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,
[a8435eb5]369 .equal = NULL,
370 .remove_callback = NULL
371};
372
[2b61945]373int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
[a5976973]374{
375 assert(bus);
376
[2b61945]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;
[a5976973]382
[c10daa8]383 if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
[2b61945]384 free(bus->devices_by_slot);
[a8435eb5]385 return ENOMEM;
386 }
387
[a5976973]388 bus->base.ops = xhci_bus_ops;
389 return EOK;
390}
[a8435eb5]391
392void xhci_bus_fini(xhci_bus_t *bus)
393{
[c10daa8]394 // FIXME: Make sure no devices are in the hash table.
395
396 hash_table_destroy(&bus->devices);
[a8435eb5]397}
[20eaa82]398
[a5976973]399/**
400 * @}
401 */
Note: See TracBrowser for help on using the repository browser.