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

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

Refactored some parts of RH to HC. Also moved some bus ops around.

  • Property mode set to 100644
File size: 9.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/ddf_helpers.h>
38#include <usb/host/endpoint.h>
39#include <usb/host/hcd.h>
40#include <usb/debug.h>
41
42#include <assert.h>
43#include <errno.h>
44#include <str_error.h>
45#include <macros.h>
46#include <stdbool.h>
47
48#include "bus.h"
49#include "endpoint.h"
50
51/** Element of the hash table. */
52typedef struct {
53 ht_link_t link;
54
55 /** Device */
56 xhci_device_t *device;
57} hashed_device_t;
58
59/** TODO: Still some copy-pasta left...
60 */
61int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
62{
63 int err;
64
65 /* TODO: get speed from the default address reservation */
66 dev->speed = USB_SPEED_FULL;
67
68 /* Manage TT */
69 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
70 /* For LS devices under HS hub */
71 /* TODO: How about SS hubs? */
72 dev->tt.address = dev->hub->address;
73 dev->tt.port = dev->port;
74 }
75 else {
76 /* Inherit hub's TT */
77 dev->tt = dev->hub->tt;
78 }
79
80 /* Assign an address to the device */
81 if ((err = xhci_rh_address_device(&hc->rh, dev, bus))) {
82 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
83 return err;
84 }
85
86 /* Read the device descriptor, derive the match ids */
87 if ((err = hcd_ddf_device_explore(hc->hcd, dev))) {
88 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
89 bus_release_address(&bus->base, dev->address);
90 return err;
91 }
92
93 return EOK;
94}
95
96int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
97{
98 // TODO: Implement me!
99 return ENOTSUP;
100}
101
102/** Ops receive generic bus_t pointer. */
103static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
104{
105 assert(bus_base);
106 return (xhci_bus_t *) bus_base;
107}
108
109static int enumerate_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
110{
111 xhci_hc_t *hc = hcd_get_driver_data(hcd);
112 assert(hc);
113
114 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
115 assert(bus);
116
117 return xhci_bus_enumerate_device(bus, hc, dev);
118}
119
120static int remove_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
121{
122 xhci_hc_t *hc = hcd_get_driver_data(hcd);
123 assert(hc);
124
125 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
126 assert(bus);
127
128 return xhci_bus_remove_device(bus, hc, dev);
129}
130
131static endpoint_t *create_endpoint(bus_t *base)
132{
133 xhci_bus_t *bus = bus_to_xhci_bus(base);
134
135 xhci_endpoint_t *ep = malloc(sizeof(xhci_endpoint_t));
136 if (!ep)
137 return NULL;
138
139 if (xhci_endpoint_init(ep, bus)) {
140 free(ep);
141 return NULL;
142 }
143
144 return &ep->base;
145}
146
147static void destroy_endpoint(endpoint_t *ep)
148{
149 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
150
151 xhci_endpoint_fini(xhci_ep);
152 free(xhci_ep);
153}
154
155static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
156{
157 ht_link_t *link = hash_table_find(&bus->devices, &address);
158 if (link == NULL)
159 return ENOENT;
160
161 *dev = hash_table_get_inst(link, hashed_device_t, link);
162 return EOK;
163}
164
165static int xhci_endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, xhci_endpoint_t **ep)
166{
167 hashed_device_t *dev;
168 int res = hashed_device_find_by_address(bus, target.address, &dev);
169 if (res != EOK)
170 return res;
171
172 xhci_endpoint_t *ret_ep = xhci_device_get_endpoint(dev->device, target.endpoint);
173 if (!ret_ep)
174 return ENOENT;
175
176 *ep = ret_ep;
177 return EOK;
178}
179
180static int hashed_device_create(xhci_bus_t *bus, hashed_device_t **hashed_dev, usb_address_t address)
181{
182 int res;
183 xhci_device_t *dev = (xhci_device_t *) malloc(sizeof(xhci_device_t));
184 if (!dev) {
185 res = ENOMEM;
186 goto err_xhci_dev_alloc;
187 }
188
189 res = xhci_device_init(dev, bus, address);
190 if (res != EOK) {
191 goto err_xhci_dev_init;
192 }
193
194 hashed_device_t *ret_dev = (hashed_device_t *) malloc(sizeof(hashed_device_t));
195 if (!ret_dev) {
196 res = ENOMEM;
197 goto err_hashed_dev_alloc;
198 }
199
200 ret_dev->device = dev;
201
202 usb_log_info("Device(%d) registered to XHCI bus.", dev->address);
203
204 hash_table_insert(&bus->devices, &ret_dev->link);
205 *hashed_dev = ret_dev;
206 return EOK;
207
208err_hashed_dev_alloc:
209err_xhci_dev_init:
210 free(dev);
211err_xhci_dev_alloc:
212 return res;
213}
214
215static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
216{
217 usb_log_info("Device(%d) released from XHCI bus.", hashed_dev->device->address);
218
219 hash_table_remove(&bus->devices, &hashed_dev->device->address);
220 xhci_device_fini(hashed_dev->device);
221 free(hashed_dev->device);
222 free(hashed_dev);
223
224 return EOK;
225}
226
227static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
228{
229 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
230 assert(bus);
231
232 hashed_device_t *hashed_dev;
233 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
234 if (res != EOK && res != ENOENT)
235 return res;
236
237 if (res == ENOENT) {
238 res = hashed_device_create(bus, &hashed_dev, ep->target.address);
239
240 if (res != EOK)
241 return res;
242 }
243
244 usb_log_info("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
245
246 return xhci_device_add_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
247}
248
249static int release_endpoint(bus_t *bus_base, endpoint_t *ep)
250{
251 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
252 assert(bus);
253
254 usb_log_info("Endpoint(%d:%d) released from XHCI bus.", ep->target.address, ep->target.endpoint);
255
256 hashed_device_t *hashed_dev;
257 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
258 if (res != EOK)
259 return res;
260
261 res = xhci_device_remove_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
262 if (res != EOK)
263 return res;
264
265 if (hashed_dev->device->active_endpoint_count == 0) {
266 res = hashed_device_remove(bus, hashed_dev);
267
268 if (res != EOK)
269 return res;
270 }
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 const bus_ops_t xhci_bus_ops = {
313 .enumerate_device = enumerate_device,
314 .remove_device = remove_device,
315
316 .create_endpoint = create_endpoint,
317 .destroy_endpoint = destroy_endpoint,
318
319 .register_endpoint = register_endpoint,
320 .release_endpoint = release_endpoint,
321 .find_endpoint = find_endpoint,
322
323 .request_address = NULL,
324 .release_address = NULL,
325 .reset_toggle = reset_toggle,
326
327 .count_bw = count_bw,
328
329 .endpoint_get_toggle = endpoint_get_toggle,
330 .endpoint_set_toggle = endpoint_set_toggle,
331};
332
333static size_t device_ht_hash(const ht_link_t *item)
334{
335 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
336 return (size_t) hash_mix(dev->device->address);
337}
338
339static size_t device_ht_key_hash(void *key)
340{
341 return (size_t) hash_mix(*(usb_address_t *)key);
342}
343
344static bool device_ht_key_equal(void *key, const ht_link_t *item)
345{
346 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
347 return dev->device->address == *(usb_address_t *) key;
348}
349
350/** Operations for the device hash table. */
351static hash_table_ops_t device_ht_ops = {
352 .hash = device_ht_hash,
353 .key_hash = device_ht_key_hash,
354 .key_equal = device_ht_key_equal,
355 .equal = NULL,
356 .remove_callback = NULL
357};
358
359int xhci_bus_init(xhci_bus_t *bus)
360{
361 assert(bus);
362
363 bus_init(&bus->base, sizeof(device_t));
364
365 if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
366 // FIXME: Dealloc base!
367 return ENOMEM;
368 }
369
370 bus->base.ops = xhci_bus_ops;
371 return EOK;
372}
373
374void xhci_bus_fini(xhci_bus_t *bus)
375{
376 // FIXME: Make sure no devices are in the hash table.
377
378 hash_table_destroy(&bus->devices);
379}
380
381/**
382 * @}
383 */
Note: See TracBrowser for help on using the repository browser.