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

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

usbhub: keep trying to power on ports, there might be multiple gangs

  • Property mode set to 100644
File size: 9.2 KB
RevLine 
[2ad98fd]1/*
2 * Copyright (c) 2011 Vojtech Horky
[3b617579]3 * Copyright (c) 2011 Jan Vesely
[2ad98fd]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 */
[58563585]29
[2ad98fd]30/** @addtogroup drvusbhub
31 * @{
32 */
33/** @file
34 * Hub ports functions.
35 */
36
[3e6a98c5]37#include <stdbool.h>
[2ad98fd]38#include <errno.h>
39#include <str_error.h>
40#include <inttypes.h>
41#include <fibril_synch.h>
[41df71f9]42#include <usbhc_iface.h>
[2ad98fd]43
44#include <usb/debug.h>
45
[a590a23]46#include "port.h"
[2ad98fd]47#include "usbhub.h"
[400f363]48#include "status.h"
[2ad98fd]49
[c4e84ed6]50#define port_log(lvl, port, fmt, ...) do { usb_log_##lvl("(%p-%u): " fmt, (port->hub), (port->port_number), ##__VA_ARGS__); } while (0)
[76fbd9a]51
[c4e84ed6]52/** Initialize hub port information.
[aff1880]53 *
[c4e84ed6]54 * @param port Port to be initialized.
[aff1880]55 */
[c4e84ed6]56void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub, unsigned int port_number)
[aff1880]57{
[c0587d90]58 assert(port);
[c4e84ed6]59 memset(port, 0, sizeof(*port));
60 port->hub = hub;
61 port->port_number = port_number;
[94f8c363]62 usb_port_init(&port->base);
[aff1880]63}
[76fbd9a]64
[94f8c363]65static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
[aff1880]66{
[94f8c363]67 assert(port);
68 return (usb_hub_port_t *) port;
[442fa6b]69}
[76fbd9a]70
[2ad98fd]71/**
[c4e84ed6]72 * Inform the HC that the device on port is gone.
[2ad98fd]73 */
[94f8c363]74static void remove_device(usb_port_t *port_base)
[983e135]75{
[94f8c363]76 usb_hub_port_t *port = get_hub_port(port_base);
[d6e2938]77
[c4e84ed6]78 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
[94f8c363]79 if (!exch) {
80 port_log(error, port, "Cannot remove the device, failed creating exchange.");
81 return;
[3df8ea9]82 }
[94f8c363]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));
[c4e84ed6]87
[94f8c363]88 usb_device_bus_exchange_end(exch);
[3df8ea9]89}
90
[129b821f]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
[c4e84ed6]100/**
[3f02935]101 * Routine for adding a new device in USB2.
[2ad98fd]102 */
[3f02935]103static int enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
[d6e2938]104{
[c4e84ed6]105 int err;
[3df8ea9]106
[3f02935]107 port_log(debug, port, "Requesting default address.");
[94f8c363]108 err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
[c4e84ed6]109 if (err != EOK) {
110 port_log(error, port, "Failed to reserve default address: %s", str_error(err));
[3f02935]111 return err;
[51a51be]112 }
113
114 /* Reservation of default address could have blocked */
[94f8c363]115 if (port->base.state != PORT_CONNECTING)
116 goto out_address;
[957ea3a]117
[3f02935]118 port_log(debug, port, "Resetting port.");
119 if ((err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET))) {
120 port_log(warning, port, "Port reset request failed: %s", str_error(err));
[957ea3a]121 goto out_address;
[3df8ea9]122 }
123
[94f8c363]124 if ((err = usb_port_wait_for_enabled(&port->base))) {
125 port_log(error, port, "Failed to reset port: %s", str_error(err));
126 goto out_address;
127 }
[957ea3a]128
[3f02935]129 port_log(debug, port, "Enumerating device.");
[0f79283b]130 if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
[c4e84ed6]131 port_log(error, port, "Failed to enumerate device: %s", str_error(err));
[3f02935]132 /* Disable the port */
133 usb_hub_clear_port_feature(port->hub, port->port_number, USB2_HUB_FEATURE_PORT_ENABLE);
[94f8c363]134 goto out_address;
[957ea3a]135 }
136
[c4e84ed6]137 port_log(debug, port, "Device enumerated");
[957ea3a]138
139out_address:
[51a51be]140 usb_hub_release_default_address(port->hub, exch);
[3f02935]141 return err;
142}
143
144/**
145 * Routine for adding a new device in USB 3.
146 */
147static int enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
148{
149 int err;
150
151 port_log(debug, port, "Issuing a warm reset.");
152 if ((err = usb_hub_set_port_feature(port->hub, port->port_number, USB3_HUB_FEATURE_BH_PORT_RESET))) {
153 port_log(warning, port, "Port reset request failed: %s", str_error(err));
154 return err;
155 }
156
157 if ((err = usb_port_wait_for_enabled(&port->base))) {
158 port_log(error, port, "Failed to reset port: %s", str_error(err));
159 return err;
160 }
161
162 port_log(debug, port, "Enumerating device.");
163 if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
164 port_log(error, port, "Failed to enumerate device: %s", str_error(err));
165 return err;
166 }
167
168 port_log(debug, port, "Device enumerated");
169 return EOK;
170}
171
172static int enumerate_device(usb_port_t *port_base)
173{
174 usb_hub_port_t *port = get_hub_port(port_base);
175
176 port_log(debug, port, "Setting up new device.");
177 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
178 if (!exch) {
179 port_log(error, port, "Failed to create exchange.");
180 return ENOMEM;
181 }
182
183 const int err = port->hub->speed == USB_SPEED_SUPER
184 ? enumerate_device_usb3(port, exch)
185 : enumerate_device_usb2(port, exch);
186
[0f4bff8]187 usb_device_bus_exchange_end(exch);
[94f8c363]188 return err;
[c4e84ed6]189}
190
191static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
192{
193 const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
194 port_log(debug, port, "Connection change: device %s.", connected ? "attached" : "removed");
195
[cd0cf81]196 if (connected) {
[94f8c363]197 usb_port_connected(&port->base, &enumerate_device);
[c4e84ed6]198 } else {
[94f8c363]199 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]200 }
[c4e84ed6]201}
[2ad98fd]202
[c4e84ed6]203static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
204{
[cd3fa47]205 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
[c4e84ed6]206 if (enabled) {
207 port_log(warning, port, "Port unexpectedly changed to enabled.");
208 } else {
[94f8c363]209 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]210 }
[c4e84ed6]211}
[2ad98fd]212
[c4e84ed6]213static void port_changed_overcurrent(usb_hub_port_t *port, usb_port_status_t status)
214{
215 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
216
217 /* According to the USB specs:
218 * 11.13.5 Over-current Reporting and Recovery
219 * Hub device is responsible for putting port in power off
220 * mode. USB system software is responsible for powering port
221 * back on when the over-current condition is gone */
222
[94f8c363]223 usb_port_disabled(&port->base, &remove_device);
[c4e84ed6]224
225 if (!overcurrent) {
226 const int err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_POWER);
227 if (err)
228 port_log(error, port, "Failed to set port power after OC: %s.", str_error(err));
229 }
230}
231
232static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
233{
[cd3fa47]234 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
[c4e84ed6]235
[0f79283b]236 if (enabled) {
[129b821f]237 port->speed = get_port_speed(port, status);
[0f79283b]238 usb_port_enabled(&port->base);
239 } else
[94f8c363]240 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]241}
242
[c4e84ed6]243typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
244
[cd3fa47]245static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
246 change_handler_t handler, usb_port_status_t mask, usb_hub_class_feature_t feature)
247{
248 if ((*status & mask) == 0)
249 return;
250
251 /* Clear the change so it won't come again */
252 usb_hub_clear_port_feature(port->hub, port->port_number, feature);
253
254 if (handler)
255 handler(port, *status);
256
257 /* Mark the change as resolved */
258 *status &= ~mask;
259}
[c4e84ed6]260
261/**
262 * Process interrupts on given port
263 *
264 * Accepts connection, over current and port reset change.
265 * @param port port structure
266 * @param hub hub representation
267 */
[94f8c363]268void usb_hub_port_process_interrupt(usb_hub_port_t *port)
[c4e84ed6]269{
270 assert(port);
271 port_log(debug2, port, "Interrupt.");
272
273 usb_port_status_t status = 0;
274 const int err = usb_hub_get_port_status(port->hub, port->port_number, &status);
275 if (err != EOK) {
276 port_log(error, port, "Failed to get port status: %s.", str_error(err));
277 return;
278 }
279
[cd3fa47]280 check_port_change(port, &status, &port_changed_connection,
281 USB_HUB_PORT_STATUS_C_CONNECTION, USB_HUB_FEATURE_C_PORT_CONNECTION);
[32b2a6f2]282
[cd3fa47]283 check_port_change(port, &status, &port_changed_overcurrent,
284 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
[c4e84ed6]285
[cd3fa47]286 check_port_change(port, &status, &port_changed_reset,
287 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
[c4e84ed6]288
[cd3fa47]289 if (port->hub->speed <= USB_SPEED_HIGH) {
290 check_port_change(port, &status, &port_changed_enabled,
291 USB2_HUB_PORT_STATUS_C_ENABLE, USB2_HUB_FEATURE_C_PORT_ENABLE);
292 } else {
293 check_port_change(port, &status, &port_changed_reset,
294 USB3_HUB_PORT_STATUS_C_BH_RESET, USB3_HUB_FEATURE_C_BH_PORT_RESET);
[c4e84ed6]295
[cd3fa47]296 check_port_change(port, &status, NULL,
297 USB3_HUB_PORT_STATUS_C_LINK_STATE, USB3_HUB_FEATURE_C_PORT_LINK_STATE);
[c4e84ed6]298 }
299
[9d3536e]300 /* Check for changes we ignored */
301 if (status & 0xffff0000) {
302 port_log(debug, port, "Port status change igored. Status: %#08" PRIx32, status);
303 }
[c4e84ed6]304}
305
306
[2ad98fd]307/**
308 * @}
309 */
Note: See TracBrowser for help on using the repository browser.