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

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

Fix block comment formatting (ccheck).

  • 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 /*
239 * According to the USB specs:
240 * 11.13.5 Over-current Reporting and Recovery
241 * Hub device is responsible for putting port in power off
242 * mode. USB system software is responsible for powering port
243 * back on when the over-current condition is gone
244 */
245
246 usb_port_disabled(&port->base, &remove_device);
247
248 if (!overcurrent) {
249 const errno_t err = usb_hub_set_port_feature(port->hub,
250 port->port_number, USB_HUB_FEATURE_PORT_POWER);
251 if (err)
252 port_log(error, port, "Failed to set port power "
253 "after OC: %s.", str_error(err));
254 }
255}
256
257static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
258{
259 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
260
261 if (enabled) {
262 port->speed = get_port_speed(port, status);
263 usb_port_enabled(&port->base);
264 } else
265 usb_port_disabled(&port->base, &remove_device);
266}
267
268typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
269
270static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
271 change_handler_t handler, usb_port_status_t mask,
272 usb_hub_class_feature_t feature)
273{
274 if ((*status & mask) == 0)
275 return;
276
277 /* Clear the change so it won't come again */
278 usb_hub_clear_port_feature(port->hub, port->port_number, feature);
279
280 if (handler)
281 handler(port, *status);
282
283 /* Mark the change as resolved */
284 *status &= ~mask;
285}
286
287/**
288 * Process interrupts on given port
289 *
290 * Accepts connection, over current and port reset change.
291 * @param port port structure
292 * @param hub hub representation
293 */
294void usb_hub_port_process_interrupt(usb_hub_port_t *port)
295{
296 assert(port);
297 port_log(debug2, port, "Interrupt.");
298
299 usb_port_status_t status = 0;
300 const errno_t err = usb_hub_get_port_status(port->hub,
301 port->port_number, &status);
302 if (err != EOK) {
303 port_log(error, port, "Failed to get port status: %s.",
304 str_error(err));
305 return;
306 }
307
308 check_port_change(port, &status, &port_changed_connection,
309 USB_HUB_PORT_STATUS_C_CONNECTION,
310 USB_HUB_FEATURE_C_PORT_CONNECTION);
311
312 check_port_change(port, &status, &port_changed_overcurrent,
313 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
314
315 check_port_change(port, &status, &port_changed_reset,
316 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
317
318 if (port->hub->speed <= USB_SPEED_HIGH) {
319 check_port_change(port, &status, &port_changed_enabled,
320 USB2_HUB_PORT_STATUS_C_ENABLE,
321 USB2_HUB_FEATURE_C_PORT_ENABLE);
322 } else {
323 check_port_change(port, &status, &port_changed_reset,
324 USB3_HUB_PORT_STATUS_C_BH_RESET,
325 USB3_HUB_FEATURE_C_BH_PORT_RESET);
326
327 check_port_change(port, &status, NULL,
328 USB3_HUB_PORT_STATUS_C_LINK_STATE,
329 USB3_HUB_FEATURE_C_PORT_LINK_STATE);
330 }
331
332 /* Check for changes we ignored */
333 if (status & 0xffff0000) {
334 port_log(debug, port, "Port status change igored. "
335 "Status: %#08" PRIx32, status);
336 }
337}
338
339
340/**
341 * @}
342 */
Note: See TracBrowser for help on using the repository browser.