source: mainline/uspace/lib/usbhost/src/usb2_bus.c@ d369b3b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d369b3b was d369b3b, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb2_bus: no longer be a bus

As the number of implemented functions got to 3, it's not so beneficial
to inherit usb2 bus to get the functionality. Overall, four trampolines
needed to be added, which is an acceptable number.

Now, the usb2_bus has become a usb2_bus_helper, to be used as
a companion to the common bus.

This is mostly a preparation to remove the runtime binding of the bus
methods.

  • Property mode set to 100644
File size: 7.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 *
34 * A bus_t implementation for USB 2 and lower. Implements USB 2 enumeration and
35 * configurable bandwidth counting.
36 */
37
38#include <assert.h>
39#include <errno.h>
40#include <macros.h>
41#include <stdbool.h>
42#include <stdlib.h>
43#include <str_error.h>
44#include <usb/debug.h>
45#include <usb/descriptor.h>
46#include <usb/request.h>
47#include <usb/host/utility.h>
48#include <usb/usb.h>
49
50#include "endpoint.h"
51#include "ddf_helpers.h"
52
53#include "usb2_bus.h"
54
55/**
56 * Request a new address. A free address is found and marked as occupied.
57 *
58 * There's no need to synchronize this method, because it is called only with
59 * default address reserved.
60 *
61 * @param bus usb_device_manager
62 * @param addr Pointer to requested address value, place to store new address
63 */
64static int request_address(usb2_bus_helper_t *helper, usb_address_t *addr)
65{
66 // Find a free address
67 usb_address_t new_address = helper->last_address;
68 do {
69 new_address = (new_address + 1) % USB_ADDRESS_COUNT;
70 if (new_address == USB_ADDRESS_DEFAULT)
71 new_address = 1;
72 if (new_address == helper->last_address)
73 return ENOSPC;
74 } while (helper->address_occupied[new_address]);
75 helper->last_address = new_address;
76
77 *addr = new_address;
78 helper->address_occupied[*addr] = true;
79
80 return EOK;
81}
82
83/**
84 * Mark address as free.
85 */
86static void release_address(usb2_bus_helper_t *helper, usb_address_t address)
87{
88 helper->address_occupied[address] = false;
89}
90
91static const usb_target_t usb2_default_target = {{
92 .address = USB_ADDRESS_DEFAULT,
93 .endpoint = 0,
94}};
95
96/**
97 * Transition the device to the addressed state.
98 *
99 * Reserve address, configure the control EP, issue a SET_ADDRESS command.
100 * Configure the device with the new address,
101 */
102static int address_device(usb2_bus_helper_t *helper, device_t *dev)
103{
104 int err;
105
106 /* The default address is currently reserved for this device */
107 dev->address = USB_ADDRESS_DEFAULT;
108
109 /** Reserve address early, we want pretty log messages */
110 usb_address_t address = USB_ADDRESS_DEFAULT;
111 if ((err = request_address(helper, &address))) {
112 usb_log_error("Failed to reserve new address: %s.",
113 str_error(err));
114 return err;
115 }
116 usb_log_debug("Device(%d): Reserved new address.", address);
117
118 /* Add default pipe on default address */
119 usb_log_debug("Device(%d): Adding default target (0:0)", address);
120
121 usb_endpoint_descriptors_t ep0_desc = {
122 .endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
123 };
124 endpoint_t *default_ep;
125 err = bus_endpoint_add(dev, &ep0_desc, &default_ep);
126 if (err != EOK) {
127 usb_log_error("Device(%d): Failed to add default target: %s.",
128 address, str_error(err));
129 goto err_address;
130 }
131
132 if ((err = hc_get_ep0_max_packet_size(&ep0_desc.endpoint.max_packet_size, dev->bus, dev)))
133 goto err_address;
134
135 /* Set new address */
136 const usb_device_request_setup_packet_t set_address = SET_ADDRESS(address);
137
138 usb_log_debug("Device(%d): Setting USB address.", address);
139 err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
140 NULL, 0, *(uint64_t *)&set_address, "set address");
141 if (err != 0) {
142 usb_log_error("Device(%d): Failed to set new address: %s.",
143 address, str_error(err));
144 goto err_default_control_ep;
145 }
146
147 /* We need to remove ep before we change the address */
148 if ((err = bus_endpoint_remove(default_ep))) {
149 usb_log_error("Device(%d): Failed to unregister default target: %s", address, str_error(err));
150 goto err_address;
151 }
152
153 dev->address = address;
154
155 /* Register EP on the new address */
156 usb_log_debug("Device(%d): Registering control EP.", address);
157 err = bus_endpoint_add(dev, &ep0_desc, NULL);
158 if (err != EOK) {
159 usb_log_error("Device(%d): Failed to register EP0: %s",
160 address, str_error(err));
161 goto err_address;
162 }
163
164 return EOK;
165
166err_default_control_ep:
167 bus_endpoint_remove(default_ep);
168err_address:
169 release_address(helper, address);
170 return err;
171}
172
173/**
174 * Enumerate a USB device. Move it to the addressed state, then explore it
175 * to create a DDF function node with proper characteristics.
176 */
177int usb2_bus_device_enumerate(usb2_bus_helper_t *helper, device_t *dev)
178{
179 int err;
180 usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
181
182 /* Assign an address to the device */
183 if ((err = address_device(helper, dev))) {
184 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
185 return err;
186 }
187
188 /* Read the device descriptor, derive the match ids */
189 if ((err = hc_device_explore(dev))) {
190 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
191 release_address(helper, dev->address);
192 return err;
193 }
194
195 return EOK;
196}
197
198/**
199 * Register an endpoint to the bus. Reserves bandwidth.
200 */
201int usb2_bus_endpoint_register(usb2_bus_helper_t *helper, endpoint_t *ep)
202{
203 assert(ep);
204 assert(fibril_mutex_is_locked(&ep->device->guard));
205
206 size_t bw = helper->bw_accounting->count_bw(ep);
207
208 /* Check for available bandwidth */
209 if (bw > helper->free_bw)
210 return ENOSPC;
211
212 helper->free_bw -= bw;
213
214 return EOK;
215}
216
217/**
218 * Release bandwidth reserved by the given endpoint.
219 */
220void usb2_bus_endpoint_unregister(usb2_bus_helper_t *helper, endpoint_t *ep)
221{
222 assert(helper);
223 assert(ep);
224
225 helper->free_bw += helper->bw_accounting->count_bw(ep);
226}
227
228/**
229 * Initialize to default state.
230 *
231 * @param helper usb_bus_helper structure, non-null.
232 * @param bw_accounting a structure defining bandwidth accounting
233 */
234void usb2_bus_helper_init(usb2_bus_helper_t *helper, const bandwidth_accounting_t *bw_accounting)
235{
236 assert(helper);
237 assert(bw_accounting);
238
239 helper->bw_accounting = bw_accounting;
240 helper->free_bw = bw_accounting->available_bandwidth;
241
242 /*
243 * The first address allocated is for the roothub. This way, its
244 * address will be 127, and the first connected USB device will have
245 * address 1.
246 */
247 helper->last_address = USB_ADDRESS_COUNT - 2;
248}
249
250/**
251 * @}
252 */
Note: See TracBrowser for help on using the repository browser.