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

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

Refactored XHCI bus to hold devices instead of endpoints. Added middle layer to keep track of endpoints within devices.

  • Property mode set to 100644
File size: 7.7 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/endpoint.h>
38#include <usb/debug.h>
39
40#include <assert.h>
41#include <errno.h>
42#include <macros.h>
43#include <stdbool.h>
44
45#include "bus.h"
46#include "endpoint.h"
47
48/** Element of the hash table. */
49typedef struct {
50 ht_link_t link;
51
52 /** Device */
53 xhci_device_t *device;
54} hashed_device_t;
55
56/** Ops receive generic bus_t pointer. */
57static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
58{
59 assert(bus_base);
60 return (xhci_bus_t *) bus_base;
61}
62
63static endpoint_t *create_endpoint(bus_t *base)
64{
65 xhci_bus_t *bus = bus_to_xhci_bus(base);
66
67 xhci_endpoint_t *ep = malloc(sizeof(xhci_endpoint_t));
68 if (!ep)
69 return NULL;
70
71 if (xhci_endpoint_init(ep, bus)) {
72 free(ep);
73 return NULL;
74 }
75
76 return &ep->base;
77}
78
79static void destroy_endpoint(endpoint_t *ep)
80{
81 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
82
83 xhci_endpoint_fini(xhci_ep);
84 free(xhci_ep);
85}
86
87static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
88{
89 ht_link_t *link = hash_table_find(&bus->devices, &address);
90 if (link == NULL)
91 return ENOENT;
92
93 *dev = hash_table_get_inst(link, hashed_device_t, link);
94 return EOK;
95}
96
97static int xhci_endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, xhci_endpoint_t **ep)
98{
99 hashed_device_t *dev;
100 int res = hashed_device_find_by_address(bus, target.address, &dev);
101 if (res != EOK)
102 return res;
103
104 xhci_endpoint_t *ret_ep = xhci_device_get_endpoint(dev->device, target.endpoint);
105 if (!ret_ep)
106 return ENOENT;
107
108 *ep = ret_ep;
109 return EOK;
110}
111
112static int hashed_device_create(xhci_bus_t *bus, hashed_device_t **hashed_dev)
113{
114 int res;
115 xhci_device_t *dev = (xhci_device_t *) malloc(sizeof(xhci_device_t));
116 if (!dev) {
117 res = ENOMEM;
118 goto err_xhci_dev_alloc;
119 }
120
121 res = xhci_device_init(dev, bus);
122 if (res != EOK) {
123 goto err_xhci_dev_init;
124 }
125
126 // TODO: Set device data.
127
128 hashed_device_t *ret_dev = (hashed_device_t *) malloc(sizeof(hashed_device_t));
129 if (!ret_dev) {
130 res = ENOMEM;
131 goto err_hashed_dev_alloc;
132 }
133
134 ret_dev->device = dev;
135
136 hash_table_insert(&bus->devices, &ret_dev->link);
137 *hashed_dev = ret_dev;
138 return EOK;
139
140err_hashed_dev_alloc:
141err_xhci_dev_init:
142 free(dev);
143err_xhci_dev_alloc:
144 return res;
145}
146
147static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
148{
149 hash_table_remove(&bus->devices, &hashed_dev->device->address);
150 xhci_device_fini(hashed_dev->device);
151 free(hashed_dev->device);
152 free(hashed_dev);
153
154 return EOK;
155}
156
157static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
158{
159 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
160 assert(bus);
161
162 hashed_device_t *hashed_dev;
163 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
164 if (res != EOK && res != ENOENT)
165 return res;
166
167 if (res == ENOENT) {
168 res = hashed_device_create(bus, &hashed_dev);
169
170 if (res != EOK)
171 return res;
172 }
173
174 return xhci_device_add_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
175}
176
177static int release_endpoint(bus_t *bus_base, endpoint_t *ep)
178{
179 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
180 assert(bus);
181
182 hashed_device_t *hashed_dev;
183 int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
184 if (res != EOK)
185 return res;
186
187 xhci_device_remove_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
188
189 if (hashed_dev->device->active_endpoint_count == 0) {
190 res = hashed_device_remove(bus, hashed_dev);
191
192 if (res != EOK)
193 return res;
194 }
195
196 return EOK;
197}
198
199static endpoint_t* find_endpoint(bus_t *bus_base, usb_target_t target, usb_direction_t direction)
200{
201 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
202 assert(bus);
203
204 xhci_endpoint_t *ep;
205 int res = xhci_endpoint_find_by_target(bus, target, &ep);
206 if (res != EOK)
207 return NULL;
208
209 return &ep->base;
210}
211
212static int request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
213{
214 // TODO: Implement me!
215 return ENOTSUP;
216}
217
218static int get_speed(bus_t *bus_base, usb_address_t address, usb_speed_t *speed)
219{
220 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
221 assert(bus);
222
223 // TODO: Use `xhci_get_port_speed` once we find the port corresponding to `address`.
224 *speed = USB_SPEED_SUPER;
225 return EOK;
226}
227
228static int release_address(bus_t *bus_base, usb_address_t address)
229{
230 // TODO: Implement me!
231 return ENOTSUP;
232}
233
234static int reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
235{
236 // TODO: Implement me!
237 return ENOTSUP;
238}
239
240static size_t count_bw(endpoint_t *ep, size_t size)
241{
242 // TODO: Implement me!
243 return 0;
244}
245
246/* Endpoint ops, optional (have generic fallback) */
247static bool endpoint_get_toggle(endpoint_t *ep)
248{
249 // TODO: Implement me!
250 return ENOTSUP;
251}
252
253static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
254{
255 // TODO: Implement me!
256}
257
258static const bus_ops_t xhci_bus_ops = {
259 .create_endpoint = create_endpoint,
260 .destroy_endpoint = destroy_endpoint,
261
262 .register_endpoint = register_endpoint,
263 .release_endpoint = release_endpoint,
264 .find_endpoint = find_endpoint,
265
266 .request_address = request_address,
267 .get_speed = get_speed,
268 .release_address = release_address,
269 .reset_toggle = reset_toggle,
270
271 .count_bw = count_bw,
272
273 .endpoint_get_toggle = endpoint_get_toggle,
274 .endpoint_set_toggle = endpoint_set_toggle,
275};
276
277static size_t device_ht_hash(const ht_link_t *item)
278{
279 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
280 return (size_t) hash_mix(dev->device->address);
281}
282
283static size_t device_ht_key_hash(void *key)
284{
285 return (size_t) hash_mix(*(usb_address_t *)key);
286}
287
288static bool device_ht_key_equal(void *key, const ht_link_t *item)
289{
290 hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
291 return dev->device->address == *(usb_address_t *) key;
292}
293
294/** Operations for the device hash table. */
295static hash_table_ops_t device_ht_ops = {
296 .hash = device_ht_hash,
297 .key_hash = device_ht_key_hash,
298 .key_equal = device_ht_key_equal,
299 .equal = NULL,
300 .remove_callback = NULL
301};
302
303int xhci_bus_init(xhci_bus_t *bus)
304{
305 assert(bus);
306
307 bus_init(&bus->base);
308
309 if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
310 // FIXME: Dealloc base!
311 return ENOMEM;
312 }
313
314 bus->base.ops = xhci_bus_ops;
315 return EOK;
316}
317
318void xhci_bus_fini(xhci_bus_t *bus)
319{
320 // FIXME: Make sure no devices are in the hash table.
321
322 hash_table_destroy(&bus->devices);
323}
324/**
325 * @}
326 */
Note: See TracBrowser for help on using the repository browser.