source: mainline/uspace/drv/bus/usb/usbhub/port.c@ fafb8e5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fafb8e5 was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2018 Ondrej Hlavaty
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup drvusbhub
32 * @{
33 */
34/** @file
35 * Hub ports functions.
36 */
37
38#include <stdbool.h>
39#include <errno.h>
40#include <str_error.h>
41#include <inttypes.h>
42#include <fibril_synch.h>
43#include <usbhc_iface.h>
44
45#include <usb/debug.h>
46
47#include "port.h"
48#include "usbhub.h"
49#include "status.h"
50
51#define port_log(lvl, port, fmt, ...) do { \
52 usb_log_##lvl("(%p-%u): " fmt, \
53 (port->hub), (port->port_number), ##__VA_ARGS__); \
54 } while (0)
55
56/** Initialize hub port information.
57 *
58 * @param port Port to be initialized.
59 */
60void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub,
61 unsigned int port_number)
62{
63 assert(port);
64 memset(port, 0, sizeof(*port));
65 port->hub = hub;
66 port->port_number = port_number;
67 usb_port_init(&port->base);
68}
69
70static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
71{
72 assert(port);
73 return (usb_hub_port_t *) port;
74}
75
76/**
77 * Inform the HC that the device on port is gone.
78 */
79static void remove_device(usb_port_t *port_base)
80{
81 usb_hub_port_t *port = get_hub_port(port_base);
82
83 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
84 if (!exch) {
85 port_log(error, port, "Cannot remove the device, "
86 "failed creating exchange.");
87 return;
88 }
89
90 const errno_t err = usbhc_device_remove(exch, port->port_number);
91 if (err)
92 port_log(error, port, "Failed to remove device: %s",
93 str_error(err));
94
95 usb_device_bus_exchange_end(exch);
96}
97
98static usb_speed_t get_port_speed(usb_hub_port_t *port, uint32_t status)
99{
100 assert(port);
101 assert(port->hub);
102
103 return usb_port_speed(port->hub->speed, status);
104}
105
106/**
107 * Routine for adding a new device in USB2.
108 */
109static errno_t enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
110{
111 errno_t err;
112
113 port_log(debug, port, "Requesting default address.");
114 err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
115 if (err != EOK) {
116 port_log(error, port, "Failed to reserve default address: %s",
117 str_error(err));
118 return err;
119 }
120
121 /* Reservation of default address could have blocked */
122 if (port->base.state != PORT_CONNECTING)
123 goto out_address;
124
125 port_log(debug, port, "Resetting port.");
126 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
127 USB_HUB_FEATURE_PORT_RESET))) {
128 port_log(warning, port, "Port reset request failed: %s",
129 str_error(err));
130 goto out_address;
131 }
132
133 if ((err = usb_port_wait_for_enabled(&port->base))) {
134 port_log(error, port, "Failed to reset port: %s",
135 str_error(err));
136 goto out_address;
137 }
138
139 port_log(debug, port, "Enumerating device.");
140 if ((err = usbhc_device_enumerate(exch, port->port_number,
141 port->speed))) {
142 port_log(error, port, "Failed to enumerate device: %s",
143 str_error(err));
144 /* Disable the port */
145 usb_hub_clear_port_feature(port->hub, port->port_number,
146 USB2_HUB_FEATURE_PORT_ENABLE);
147 goto out_address;
148 }
149
150 port_log(debug, port, "Device enumerated");
151
152out_address:
153 usb_hub_release_default_address(port->hub, exch);
154 return err;
155}
156
157/**
158 * Routine for adding a new device in USB 3.
159 */
160static errno_t enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
161{
162 errno_t err;
163
164 port_log(debug, port, "Issuing a warm reset.");
165 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
166 USB3_HUB_FEATURE_BH_PORT_RESET))) {
167 port_log(warning, port, "Port reset request failed: %s",
168 str_error(err));
169 return err;
170 }
171
172 if ((err = usb_port_wait_for_enabled(&port->base))) {
173 port_log(error, port, "Failed to reset port: %s",
174 str_error(err));
175 return err;
176 }
177
178 port_log(debug, port, "Enumerating device.");
179 if ((err = usbhc_device_enumerate(exch, port->port_number,
180 port->speed))) {
181 port_log(error, port, "Failed to enumerate device: %s",
182 str_error(err));
183 return err;
184 }
185
186 port_log(debug, port, "Device enumerated");
187 return EOK;
188}
189
190static errno_t enumerate_device(usb_port_t *port_base)
191{
192 usb_hub_port_t *port = get_hub_port(port_base);
193
194 port_log(debug, port, "Setting up new device.");
195 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
196 if (!exch) {
197 port_log(error, port, "Failed to create exchange.");
198 return ENOMEM;
199 }
200
201 const errno_t err = port->hub->speed == USB_SPEED_SUPER ?
202 enumerate_device_usb3(port, exch) :
203 enumerate_device_usb2(port, exch);
204
205 usb_device_bus_exchange_end(exch);
206 return err;
207}
208
209static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
210{
211 const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
212 port_log(debug, port, "Connection change: device %s.", connected ?
213 "attached" : "removed");
214
215 if (connected) {
216 usb_port_connected(&port->base, &enumerate_device);
217 } else {
218 usb_port_disabled(&port->base, &remove_device);
219 }
220}
221
222static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
223{
224 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
225 if (enabled) {
226 port_log(warning, port, "Port unexpectedly changed to enabled.");
227 } else {
228 usb_port_disabled(&port->base, &remove_device);
229 }
230}
231
232static void port_changed_overcurrent(usb_hub_port_t *port,
233 usb_port_status_t status)
234{
235 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
236
237 /*
238 * According to the USB specs:
239 * 11.13.5 Over-current Reporting and Recovery
240 * Hub device is responsible for putting port in power off
241 * mode. USB system software is responsible for powering port
242 * back on when the over-current condition is gone
243 */
244
245 usb_port_disabled(&port->base, &remove_device);
246
247 if (!overcurrent) {
248 const errno_t err = usb_hub_set_port_feature(port->hub,
249 port->port_number, USB_HUB_FEATURE_PORT_POWER);
250 if (err)
251 port_log(error, port, "Failed to set port power "
252 "after OC: %s.", str_error(err));
253 }
254}
255
256static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
257{
258 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
259
260 if (enabled) {
261 port->speed = get_port_speed(port, status);
262 usb_port_enabled(&port->base);
263 } else
264 usb_port_disabled(&port->base, &remove_device);
265}
266
267typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
268
269static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
270 change_handler_t handler, usb_port_status_t mask,
271 usb_hub_class_feature_t feature)
272{
273 if ((*status & mask) == 0)
274 return;
275
276 /* Clear the change so it won't come again */
277 usb_hub_clear_port_feature(port->hub, port->port_number, feature);
278
279 if (handler)
280 handler(port, *status);
281
282 /* Mark the change as resolved */
283 *status &= ~mask;
284}
285
286/**
287 * Process interrupts on given port
288 *
289 * Accepts connection, over current and port reset change.
290 * @param port port structure
291 * @param hub hub representation
292 */
293void usb_hub_port_process_interrupt(usb_hub_port_t *port)
294{
295 assert(port);
296 port_log(debug2, port, "Interrupt.");
297
298 usb_port_status_t status = 0;
299 const errno_t err = usb_hub_get_port_status(port->hub,
300 port->port_number, &status);
301 if (err != EOK) {
302 port_log(error, port, "Failed to get port status: %s.",
303 str_error(err));
304 return;
305 }
306
307 check_port_change(port, &status, &port_changed_connection,
308 USB_HUB_PORT_STATUS_C_CONNECTION,
309 USB_HUB_FEATURE_C_PORT_CONNECTION);
310
311 check_port_change(port, &status, &port_changed_overcurrent,
312 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
313
314 check_port_change(port, &status, &port_changed_reset,
315 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
316
317 if (port->hub->speed <= USB_SPEED_HIGH) {
318 check_port_change(port, &status, &port_changed_enabled,
319 USB2_HUB_PORT_STATUS_C_ENABLE,
320 USB2_HUB_FEATURE_C_PORT_ENABLE);
321 } else {
322 check_port_change(port, &status, &port_changed_reset,
323 USB3_HUB_PORT_STATUS_C_BH_RESET,
324 USB3_HUB_FEATURE_C_BH_PORT_RESET);
325
326 check_port_change(port, &status, NULL,
327 USB3_HUB_PORT_STATUS_C_LINK_STATE,
328 USB3_HUB_FEATURE_C_PORT_LINK_STATE);
329 }
330
331 /* Check for changes we ignored */
332 if (status & 0xffff0000) {
333 port_log(debug, port, "Port status change igored. "
334 "Status: %#08" PRIx32, status);
335 }
336}
337
338/**
339 * @}
340 */
Note: See TracBrowser for help on using the repository browser.