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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 76d0981d was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • 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
98
99static usb_speed_t get_port_speed(usb_hub_port_t *port, uint32_t status)
100{
101 assert(port);
102 assert(port->hub);
103
104 return usb_port_speed(port->hub->speed, status);
105}
106
107/**
108 * Routine for adding a new device in USB2.
109 */
110static errno_t enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
111{
112 errno_t err;
113
114 port_log(debug, port, "Requesting default address.");
115 err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
116 if (err != EOK) {
117 port_log(error, port, "Failed to reserve default address: %s",
118 str_error(err));
119 return err;
120 }
121
122 /* Reservation of default address could have blocked */
123 if (port->base.state != PORT_CONNECTING)
124 goto out_address;
125
126 port_log(debug, port, "Resetting port.");
127 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
128 USB_HUB_FEATURE_PORT_RESET))) {
129 port_log(warning, port, "Port reset request failed: %s",
130 str_error(err));
131 goto out_address;
132 }
133
134 if ((err = usb_port_wait_for_enabled(&port->base))) {
135 port_log(error, port, "Failed to reset port: %s",
136 str_error(err));
137 goto out_address;
138 }
139
140 port_log(debug, port, "Enumerating device.");
141 if ((err = usbhc_device_enumerate(exch, port->port_number,
142 port->speed))) {
143 port_log(error, port, "Failed to enumerate device: %s",
144 str_error(err));
145 /* Disable the port */
146 usb_hub_clear_port_feature(port->hub, port->port_number,
147 USB2_HUB_FEATURE_PORT_ENABLE);
148 goto out_address;
149 }
150
151 port_log(debug, port, "Device enumerated");
152
153out_address:
154 usb_hub_release_default_address(port->hub, exch);
155 return err;
156}
157
158/**
159 * Routine for adding a new device in USB 3.
160 */
161static errno_t enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
162{
163 errno_t err;
164
165 port_log(debug, port, "Issuing a warm reset.");
166 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
167 USB3_HUB_FEATURE_BH_PORT_RESET))) {
168 port_log(warning, port, "Port reset request failed: %s",
169 str_error(err));
170 return err;
171 }
172
173 if ((err = usb_port_wait_for_enabled(&port->base))) {
174 port_log(error, port, "Failed to reset port: %s",
175 str_error(err));
176 return err;
177 }
178
179 port_log(debug, port, "Enumerating device.");
180 if ((err = usbhc_device_enumerate(exch, port->port_number,
181 port->speed))) {
182 port_log(error, port, "Failed to enumerate device: %s",
183 str_error(err));
184 return err;
185 }
186
187 port_log(debug, port, "Device enumerated");
188 return EOK;
189}
190
191static errno_t enumerate_device(usb_port_t *port_base)
192{
193 usb_hub_port_t *port = get_hub_port(port_base);
194
195 port_log(debug, port, "Setting up new device.");
196 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
197 if (!exch) {
198 port_log(error, port, "Failed to create exchange.");
199 return ENOMEM;
200 }
201
202 const errno_t err = port->hub->speed == USB_SPEED_SUPER
203 ? enumerate_device_usb3(port, exch)
204 : enumerate_device_usb2(port, exch);
205
206 usb_device_bus_exchange_end(exch);
207 return err;
208}
209
210static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
211{
212 const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
213 port_log(debug, port, "Connection change: device %s.", connected
214 ? "attached" : "removed");
215
216 if (connected) {
217 usb_port_connected(&port->base, &enumerate_device);
218 } else {
219 usb_port_disabled(&port->base, &remove_device);
220 }
221}
222
223static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
224{
225 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
226 if (enabled) {
227 port_log(warning, port, "Port unexpectedly changed to enabled.");
228 } else {
229 usb_port_disabled(&port->base, &remove_device);
230 }
231}
232
233static void port_changed_overcurrent(usb_hub_port_t *port,
234 usb_port_status_t status)
235{
236 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
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 usb_port_disabled(&port->base, &remove_device);
245
246 if (!overcurrent) {
247 const errno_t err = usb_hub_set_port_feature(port->hub,
248 port->port_number, USB_HUB_FEATURE_PORT_POWER);
249 if (err)
250 port_log(error, port, "Failed to set port power "
251 "after OC: %s.", str_error(err));
252 }
253}
254
255static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
256{
257 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
258
259 if (enabled) {
260 port->speed = get_port_speed(port, status);
261 usb_port_enabled(&port->base);
262 } else
263 usb_port_disabled(&port->base, &remove_device);
264}
265
266typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
267
268static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
269 change_handler_t handler, usb_port_status_t mask,
270 usb_hub_class_feature_t feature)
271{
272 if ((*status & mask) == 0)
273 return;
274
275 /* Clear the change so it won't come again */
276 usb_hub_clear_port_feature(port->hub, port->port_number, feature);
277
278 if (handler)
279 handler(port, *status);
280
281 /* Mark the change as resolved */
282 *status &= ~mask;
283}
284
285/**
286 * Process interrupts on given port
287 *
288 * Accepts connection, over current and port reset change.
289 * @param port port structure
290 * @param hub hub representation
291 */
292void usb_hub_port_process_interrupt(usb_hub_port_t *port)
293{
294 assert(port);
295 port_log(debug2, port, "Interrupt.");
296
297 usb_port_status_t status = 0;
298 const errno_t err = usb_hub_get_port_status(port->hub,
299 port->port_number, &status);
300 if (err != EOK) {
301 port_log(error, port, "Failed to get port status: %s.",
302 str_error(err));
303 return;
304 }
305
306 check_port_change(port, &status, &port_changed_connection,
307 USB_HUB_PORT_STATUS_C_CONNECTION,
308 USB_HUB_FEATURE_C_PORT_CONNECTION);
309
310 check_port_change(port, &status, &port_changed_overcurrent,
311 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
312
313 check_port_change(port, &status, &port_changed_reset,
314 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
315
316 if (port->hub->speed <= USB_SPEED_HIGH) {
317 check_port_change(port, &status, &port_changed_enabled,
318 USB2_HUB_PORT_STATUS_C_ENABLE,
319 USB2_HUB_FEATURE_C_PORT_ENABLE);
320 } else {
321 check_port_change(port, &status, &port_changed_reset,
322 USB3_HUB_PORT_STATUS_C_BH_RESET,
323 USB3_HUB_FEATURE_C_BH_PORT_RESET);
324
325 check_port_change(port, &status, NULL,
326 USB3_HUB_PORT_STATUS_C_LINK_STATE,
327 USB3_HUB_FEATURE_C_PORT_LINK_STATE);
328 }
329
330 /* Check for changes we ignored */
331 if (status & 0xffff0000) {
332 port_log(debug, port, "Port status change igored. "
333 "Status: %#08" PRIx32, status);
334 }
335}
336
337
338/**
339 * @}
340 */
Note: See TracBrowser for help on using the repository browser.