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

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

Setting up endpoint contexts (almost) properly. Boilerplate for interrupt transfers. Simplified TRB ring initialization.

  • Property mode set to 100644
File size: 9.2 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
96static int enumerate_device(bus_t *bus, hcd_t *hcd, device_t *dev)
97{
98 xhci_hc_t *hc = hcd_get_driver_data(hcd);
99 assert(hc);
100
101 return xhci_bus_enumerate_device((xhci_bus_t *) bus, hc, dev);
102}
103
104static int remove_device(bus_t *bus, hcd_t *hcd, device_t *dev)
105{
106 // TODO: Implement me!
107
108 return ENOTSUP;
109}
110
111/** Ops receive generic bus_t pointer. */
112static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
113{
114 assert(bus_base);
115 return (xhci_bus_t *) bus_base;
116}
117
118static endpoint_t *create_endpoint(bus_t *base)
119{
120 xhci_bus_t *bus = bus_to_xhci_bus(base);
121
122 xhci_endpoint_t *ep = malloc(sizeof(xhci_endpoint_t));
123 if (!ep)
124 return NULL;
125
126 if (xhci_endpoint_init(ep, bus)) {
127 free(ep);
128 return NULL;
129 }
130
131 return &ep->base;
132}
133
134static void destroy_endpoint(endpoint_t *ep)
135{
136 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
137
138 xhci_endpoint_fini(xhci_ep);
139 free(xhci_ep);
140}
141
142static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
143{
144 ht_link_t *link = hash_table_find(&bus->devices, &address);
145 if (link == NULL)
146 return ENOENT;
147
148 *dev = hash_table_get_inst(link, hashed_device_t, link);
149 return EOK;
150}
151
152static int xhci_endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, xhci_endpoint_t **ep)
153{
154 hashed_device_t *dev;
155 int res = hashed_device_find_by_address(bus, target.address, &dev);
156 if (res != EOK)
157 return res;
158
159 xhci_endpoint_t *ret_ep = xhci_device_get_endpoint(dev->device, target.endpoint);
160 if (!ret_ep)
161 return ENOENT;
162
163 *ep = ret_ep;
164 return EOK;
165}
166
167static int hashed_device_create(xhci_bus_t *bus, hashed_device_t **hashed_dev, usb_address_t address)
168{
169 int res;
170 xhci_device_t *dev = (xhci_device_t *) malloc(sizeof(xhci_device_t));
171 if (!dev) {
172 res = ENOMEM;
173 goto err_xhci_dev_alloc;
174 }
175
176 res = xhci_device_init(dev, bus, address);
177 if (res != EOK) {
178 goto err_xhci_dev_init;
179 }
180
181 hashed_device_t *ret_dev = (hashed_device_t *) malloc(sizeof(hashed_device_t));
182 if (!ret_dev) {
183 res = ENOMEM;
184 goto err_hashed_dev_alloc;
185 }
186
187 ret_dev->device = dev;
188
189 usb_log_debug("Device(%d) registered to XHCI bus.", dev->address);
190
191 hash_table_insert(&bus->devices, &ret_dev->link);
192 *hashed_dev = ret_dev;
193 return EOK;
194
195err_hashed_dev_alloc:
196err_xhci_dev_init:
197 free(dev);
198err_xhci_dev_alloc:
199 return res;
200}
201
202static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
203{
204 usb_log_debug("Device(%d) released from XHCI bus.", hashed_dev->device->address);
205
206 hash_table_remove(&bus->devices, &hashed_dev->device->address);
207 xhci_device_fini(hashed_dev->device);
208 free(hashed_dev->device);
209 free(hashed_dev);
210
211 return EOK;
212}
213
214static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
215{
216 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
217 assert(bus);
218
219 hashed_device_t *hashed_dev;
220 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
221 if (res != EOK && res != ENOENT)
222 return res;
223
224 if (res == ENOENT) {
225 res = hashed_device_create(bus, &hashed_dev, ep->target.address);
226
227 if (res != EOK)
228 return res;
229 }
230
231 usb_log_debug("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
232
233 return xhci_device_add_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
234}
235
236static int release_endpoint(bus_t *bus_base, endpoint_t *ep)
237{
238 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
239 assert(bus);
240
241 usb_log_debug("Endpoint(%d:%d) released from XHCI bus.", ep->target.address, ep->target.endpoint);
242
243 hashed_device_t *hashed_dev;
244 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
245 if (res != EOK)
246 return res;
247
248 xhci_device_remove_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
249
250 if (hashed_dev->device->active_endpoint_count == 0) {
251 res = hashed_device_remove(bus, hashed_dev);
252
253 if (res != EOK)
254 return res;
255 }
256
257 return EOK;
258}
259
260static endpoint_t* find_endpoint(bus_t *bus_base, usb_target_t target, usb_direction_t direction)
261{
262 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
263 assert(bus);
264
265 xhci_endpoint_t *ep;
266 int res = xhci_endpoint_find_by_target(bus, target, &ep);
267 if (res != EOK)
268 return NULL;
269
270 return &ep->base;
271}
272
273static int request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
274{
275 // TODO: Implement me!
276 return ENOTSUP;
277}
278
279static int release_address(bus_t *bus_base, usb_address_t address)
280{
281 // TODO: Implement me!
282 return ENOTSUP;
283}
284
285static int reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
286{
287 // TODO: Implement me!
288 return ENOTSUP;
289}
290
291static size_t count_bw(endpoint_t *ep, size_t size)
292{
293 // TODO: Implement me!
294 return 0;
295}
296
297/* Endpoint ops, optional (have generic fallback) */
298static bool endpoint_get_toggle(endpoint_t *ep)
299{
300 // TODO: Implement me!
301 return ENOTSUP;
302}
303
304static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
305{
306 // TODO: Implement me!
307}
308
309static const bus_ops_t xhci_bus_ops = {
310 .enumerate_device = enumerate_device,
311 .remove_device = remove_device,
312
313 .create_endpoint = create_endpoint,
314 .destroy_endpoint = destroy_endpoint,
315
316 .register_endpoint = register_endpoint,
317 .release_endpoint = release_endpoint,
318 .find_endpoint = find_endpoint,
319
320 .request_address = request_address,
321 .release_address = release_address,
322 .reset_toggle = reset_toggle,
323
324 .count_bw = count_bw,
325
326 .endpoint_get_toggle = endpoint_get_toggle,
327 .endpoint_set_toggle = endpoint_set_toggle,
328};
329
330static size_t device_ht_hash(const ht_link_t *item)
331{
332 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
333 return (size_t) hash_mix(dev->device->address);
334}
335
336static size_t device_ht_key_hash(void *key)
337{
338 return (size_t) hash_mix(*(usb_address_t *)key);
339}
340
341static bool device_ht_key_equal(void *key, const ht_link_t *item)
342{
343 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
344 return dev->device->address == *(usb_address_t *) key;
345}
346
347/** Operations for the device hash table. */
348static hash_table_ops_t device_ht_ops = {
349 .hash = device_ht_hash,
350 .key_hash = device_ht_key_hash,
351 .key_equal = device_ht_key_equal,
352 .equal = NULL,
353 .remove_callback = NULL
354};
355
356int xhci_bus_init(xhci_bus_t *bus)
357{
358 assert(bus);
359
360 bus_init(&bus->base, sizeof(device_t));
361
362 if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
363 // FIXME: Dealloc base!
364 return ENOMEM;
365 }
366
367 bus->base.ops = xhci_bus_ops;
368 return EOK;
369}
370
371void xhci_bus_fini(xhci_bus_t *bus)
372{
373 // FIXME: Make sure no devices are in the hash table.
374
375 hash_table_destroy(&bus->devices);
376}
377
378/**
379 * @}
380 */
Note: See TracBrowser for help on using the repository browser.