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

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

Make ccheck-fix again and commit more good files.

  • 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.