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

Last change on this file was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

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