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

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

usbhub: be aware of its own speed

This resulted in a bunch of changes just because the roothubs in older
HC's are virtual, and need to be aware of their own speed too.

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
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
30/** @addtogroup drvusbhub
31 * @{
32 */
33/** @file
34 * Hub ports functions.
35 */
36
37#include <stdbool.h>
38#include <errno.h>
39#include <str_error.h>
40#include <inttypes.h>
41#include <fibril_synch.h>
42#include <usbhc_iface.h>
43
44#include <usb/debug.h>
45
46#include "port.h"
47#include "usbhub.h"
48#include "status.h"
49
50#define port_log(lvl, port, fmt, ...) do { usb_log_##lvl("(%p-%u): " fmt, (port->hub), (port->port_number), ##__VA_ARGS__); } while (0)
51
52/** Initialize hub port information.
53 *
54 * @param port Port to be initialized.
55 */
56void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub, unsigned int port_number)
57{
58 assert(port);
59 memset(port, 0, sizeof(*port));
60 port->hub = hub;
61 port->port_number = port_number;
62 usb_port_init(&port->base);
63}
64
65static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
66{
67 assert(port);
68 return (usb_hub_port_t *) port;
69}
70
71/**
72 * Inform the HC that the device on port is gone.
73 */
74static void remove_device(usb_port_t *port_base)
75{
76 usb_hub_port_t *port = get_hub_port(port_base);
77
78 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
79 if (!exch) {
80 port_log(error, port, "Cannot remove the device, failed creating exchange.");
81 return;
82 }
83
84 const int err = usbhc_device_remove(exch, port->port_number);
85 if (err)
86 port_log(error, port, "Failed to remove device: %s", str_error(err));
87
88 usb_device_bus_exchange_end(exch);
89}
90
91
92static usb_speed_t get_port_speed(usb_hub_port_t *port, uint32_t status)
93{
94 assert(port);
95 assert(port->hub);
96
97 return usb_port_speed(port->hub->speed, status);
98}
99
100/**
101 * Routine for adding a new device.
102 *
103 * Separate fibril is needed because the operation blocks on waiting for
104 * requesting default address and resetting port, and we must not block the
105 * control pipe.
106 */
107static int enumerate_device(usb_port_t *port_base)
108{
109 int err;
110 usb_hub_port_t *port = get_hub_port(port_base);
111
112 port_log(debug, port, "Setting up new device.");
113
114 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
115 if (!exch) {
116 port_log(error, port, "Failed to create exchange.");
117 return ENOMEM;
118 }
119
120 /* Reserve default address */
121 err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
122 if (err != EOK) {
123 port_log(error, port, "Failed to reserve default address: %s", str_error(err));
124 goto out_exch;
125 }
126
127 /* Reservation of default address could have blocked */
128 if (port->base.state != PORT_CONNECTING)
129 goto out_address;
130
131 port_log(debug, port, "Got default address. Resetting port.");
132 int rc = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET);
133 if (rc != EOK) {
134 port_log(warning, port, "Port reset request failed: %s", str_error(rc));
135 goto out_address;
136 }
137
138 if ((err = usb_port_wait_for_enabled(&port->base))) {
139 port_log(error, port, "Failed to reset port: %s", str_error(err));
140 goto out_address;
141 }
142
143 port_log(debug, port, "Port reset, enumerating device.");
144
145 if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
146 port_log(error, port, "Failed to enumerate device: %s", str_error(err));
147 /* Disable the port */
148 usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_ENABLE);
149 goto out_address;
150 }
151
152 port_log(debug, port, "Device enumerated");
153
154out_address:
155 usb_hub_release_default_address(port->hub, exch);
156out_exch:
157 usb_device_bus_exchange_end(exch);
158 return err;
159}
160
161static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
162{
163 const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
164 port_log(debug, port, "Connection change: device %s.", connected ? "attached" : "removed");
165
166 if (connected) {
167 usb_port_connected(&port->base, &enumerate_device);
168 } else {
169 usb_port_disabled(&port->base, &remove_device);
170 }
171}
172
173static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
174{
175 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED);
176 if (enabled) {
177 port_log(warning, port, "Port unexpectedly changed to enabled.");
178 } else {
179 usb_port_disabled(&port->base, &remove_device);
180 }
181}
182
183static void port_changed_suspend(usb_hub_port_t *port, usb_port_status_t status)
184{
185 port_log(error, port, "Port unexpectedly suspend. Weird, we do not support suspending!");
186}
187
188static void port_changed_overcurrent(usb_hub_port_t *port, usb_port_status_t status)
189{
190 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
191
192 /* According to the USB specs:
193 * 11.13.5 Over-current Reporting and Recovery
194 * Hub device is responsible for putting port in power off
195 * mode. USB system software is responsible for powering port
196 * back on when the over-current condition is gone */
197
198 usb_port_disabled(&port->base, &remove_device);
199
200 if (!overcurrent) {
201 const int err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_POWER);
202 if (err)
203 port_log(error, port, "Failed to set port power after OC: %s.", str_error(err));
204 }
205}
206
207static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
208{
209 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED);
210
211 if (enabled) {
212 // The connecting fibril do not touch speed until the port is enabled,
213 // so we do not have to lock
214 port->speed = get_port_speed(port, status);
215 usb_port_enabled(&port->base);
216 } else
217 usb_port_disabled(&port->base, &remove_device);
218}
219
220typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
221
222static const change_handler_t port_change_handlers [] = {
223 [USB_HUB_FEATURE_C_PORT_CONNECTION] = &port_changed_connection,
224 [USB_HUB_FEATURE_C_PORT_ENABLE] = &port_changed_enabled,
225 [USB_HUB_FEATURE_C_PORT_SUSPEND] = &port_changed_suspend,
226 [USB_HUB_FEATURE_C_PORT_OVER_CURRENT] = &port_changed_overcurrent,
227 [USB_HUB_FEATURE_C_PORT_RESET] = &port_changed_reset,
228 [sizeof(usb_port_status_t) * 8] = NULL,
229};
230
231/**
232 * Process interrupts on given port
233 *
234 * Accepts connection, over current and port reset change.
235 * @param port port structure
236 * @param hub hub representation
237 */
238void usb_hub_port_process_interrupt(usb_hub_port_t *port)
239{
240 assert(port);
241 port_log(debug2, port, "Interrupt.");
242
243 usb_port_status_t status = 0;
244 const int err = usb_hub_get_port_status(port->hub, port->port_number, &status);
245 if (err != EOK) {
246 port_log(error, port, "Failed to get port status: %s.", str_error(err));
247 return;
248 }
249
250 for (uint32_t feature = 0; feature < sizeof(usb_port_status_t) * 8; ++feature) {
251 uint32_t mask = 1 << feature;
252
253 if ((status & mask) == 0)
254 continue;
255
256 if (!port_change_handlers[feature])
257 continue;
258
259 /* ACK this change */
260 status &= ~mask;
261 usb_hub_clear_port_feature(port->hub, port->port_number, feature);
262
263 port_change_handlers[feature](port, status);
264 }
265
266 port_log(debug2, port, "Port status after handling: %#08" PRIx32, status);
267}
268
269
270/**
271 * @}
272 */
Note: See TracBrowser for help on using the repository browser.