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
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]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
[c4e84ed6]107/**
[3f02935]108 * Routine for adding a new device in USB2.
[2ad98fd]109 */
[5a6cc679]110static errno_t enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
[d6e2938]111{
[5a6cc679]112 errno_t err;
[3df8ea9]113
[3f02935]114 port_log(debug, port, "Requesting default address.");
[94f8c363]115 err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
[c4e84ed6]116 if (err != EOK) {
[ae3a941]117 port_log(error, port, "Failed to reserve default address: %s",
118 str_error(err));
[3f02935]119 return err;
[51a51be]120 }
121
122 /* Reservation of default address could have blocked */
[94f8c363]123 if (port->base.state != PORT_CONNECTING)
124 goto out_address;
[957ea3a]125
[3f02935]126 port_log(debug, port, "Resetting port.");
[ae3a941]127 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
[3bacee1]128 USB_HUB_FEATURE_PORT_RESET))) {
[ae3a941]129 port_log(warning, port, "Port reset request failed: %s",
130 str_error(err));
[957ea3a]131 goto out_address;
[3df8ea9]132 }
133
[94f8c363]134 if ((err = usb_port_wait_for_enabled(&port->base))) {
[ae3a941]135 port_log(error, port, "Failed to reset port: %s",
136 str_error(err));
[94f8c363]137 goto out_address;
138 }
[957ea3a]139
[3f02935]140 port_log(debug, port, "Enumerating device.");
[ae3a941]141 if ((err = usbhc_device_enumerate(exch, port->port_number,
[3bacee1]142 port->speed))) {
[ae3a941]143 port_log(error, port, "Failed to enumerate device: %s",
144 str_error(err));
[3f02935]145 /* Disable the port */
[ae3a941]146 usb_hub_clear_port_feature(port->hub, port->port_number,
147 USB2_HUB_FEATURE_PORT_ENABLE);
[94f8c363]148 goto out_address;
[957ea3a]149 }
150
[c4e84ed6]151 port_log(debug, port, "Device enumerated");
[957ea3a]152
153out_address:
[51a51be]154 usb_hub_release_default_address(port->hub, exch);
[3f02935]155 return err;
156}
157
158/**
159 * Routine for adding a new device in USB 3.
160 */
[5a6cc679]161static errno_t enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
[3f02935]162{
[5a6cc679]163 errno_t err;
[3f02935]164
165 port_log(debug, port, "Issuing a warm reset.");
[ae3a941]166 if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
[3bacee1]167 USB3_HUB_FEATURE_BH_PORT_RESET))) {
[ae3a941]168 port_log(warning, port, "Port reset request failed: %s",
169 str_error(err));
[3f02935]170 return err;
171 }
172
173 if ((err = usb_port_wait_for_enabled(&port->base))) {
[ae3a941]174 port_log(error, port, "Failed to reset port: %s",
175 str_error(err));
[3f02935]176 return err;
177 }
178
179 port_log(debug, port, "Enumerating device.");
[ae3a941]180 if ((err = usbhc_device_enumerate(exch, port->port_number,
[3bacee1]181 port->speed))) {
[ae3a941]182 port_log(error, port, "Failed to enumerate device: %s",
183 str_error(err));
[3f02935]184 return err;
185 }
186
187 port_log(debug, port, "Device enumerated");
188 return EOK;
189}
190
[5a6cc679]191static errno_t enumerate_device(usb_port_t *port_base)
[3f02935]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
[3bacee1]202 const errno_t err = port->hub->speed == USB_SPEED_SUPER ?
203 enumerate_device_usb3(port, exch) :
204 enumerate_device_usb2(port, exch);
[3f02935]205
[0f4bff8]206 usb_device_bus_exchange_end(exch);
[94f8c363]207 return err;
[c4e84ed6]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);
[3bacee1]213 port_log(debug, port, "Connection change: device %s.", connected ?
214 "attached" : "removed");
[c4e84ed6]215
[cd0cf81]216 if (connected) {
[94f8c363]217 usb_port_connected(&port->base, &enumerate_device);
[c4e84ed6]218 } else {
[94f8c363]219 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]220 }
[c4e84ed6]221}
[2ad98fd]222
[c4e84ed6]223static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
224{
[cd3fa47]225 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
[c4e84ed6]226 if (enabled) {
227 port_log(warning, port, "Port unexpectedly changed to enabled.");
228 } else {
[94f8c363]229 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]230 }
[c4e84ed6]231}
[2ad98fd]232
[ae3a941]233static void port_changed_overcurrent(usb_hub_port_t *port,
234 usb_port_status_t status)
[c4e84ed6]235{
236 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
237
[7c3fb9b]238 /*
239 * According to the USB specs:
[c4e84ed6]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
[7c3fb9b]243 * back on when the over-current condition is gone
244 */
[c4e84ed6]245
[94f8c363]246 usb_port_disabled(&port->base, &remove_device);
[c4e84ed6]247
248 if (!overcurrent) {
[ae3a941]249 const errno_t err = usb_hub_set_port_feature(port->hub,
250 port->port_number, USB_HUB_FEATURE_PORT_POWER);
[c4e84ed6]251 if (err)
[ae3a941]252 port_log(error, port, "Failed to set port power "
253 "after OC: %s.", str_error(err));
[c4e84ed6]254 }
255}
256
257static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
258{
[cd3fa47]259 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
[c4e84ed6]260
[0f79283b]261 if (enabled) {
[129b821f]262 port->speed = get_port_speed(port, status);
[0f79283b]263 usb_port_enabled(&port->base);
264 } else
[94f8c363]265 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]266}
267
[c4e84ed6]268typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
269
[cd3fa47]270static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
[ae3a941]271 change_handler_t handler, usb_port_status_t mask,
272 usb_hub_class_feature_t feature)
[cd3fa47]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}
[c4e84ed6]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 */
[94f8c363]294void usb_hub_port_process_interrupt(usb_hub_port_t *port)
[c4e84ed6]295{
296 assert(port);
297 port_log(debug2, port, "Interrupt.");
298
299 usb_port_status_t status = 0;
[ae3a941]300 const errno_t err = usb_hub_get_port_status(port->hub,
301 port->port_number, &status);
[c4e84ed6]302 if (err != EOK) {
[ae3a941]303 port_log(error, port, "Failed to get port status: %s.",
304 str_error(err));
[c4e84ed6]305 return;
306 }
307
[cd3fa47]308 check_port_change(port, &status, &port_changed_connection,
[ae3a941]309 USB_HUB_PORT_STATUS_C_CONNECTION,
310 USB_HUB_FEATURE_C_PORT_CONNECTION);
[32b2a6f2]311
[cd3fa47]312 check_port_change(port, &status, &port_changed_overcurrent,
313 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
[c4e84ed6]314
[cd3fa47]315 check_port_change(port, &status, &port_changed_reset,
316 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
[c4e84ed6]317
[cd3fa47]318 if (port->hub->speed <= USB_SPEED_HIGH) {
319 check_port_change(port, &status, &port_changed_enabled,
[ae3a941]320 USB2_HUB_PORT_STATUS_C_ENABLE,
321 USB2_HUB_FEATURE_C_PORT_ENABLE);
[cd3fa47]322 } else {
323 check_port_change(port, &status, &port_changed_reset,
[ae3a941]324 USB3_HUB_PORT_STATUS_C_BH_RESET,
325 USB3_HUB_FEATURE_C_BH_PORT_RESET);
[c4e84ed6]326
[cd3fa47]327 check_port_change(port, &status, NULL,
[ae3a941]328 USB3_HUB_PORT_STATUS_C_LINK_STATE,
329 USB3_HUB_FEATURE_C_PORT_LINK_STATE);
[c4e84ed6]330 }
331
[9d3536e]332 /* Check for changes we ignored */
333 if (status & 0xffff0000) {
[ae3a941]334 port_log(debug, port, "Port status change igored. "
335 "Status: %#08" PRIx32, status);
[9d3536e]336 }
[c4e84ed6]337}
338
339
[2ad98fd]340/**
341 * @}
342 */
Note: See TracBrowser for help on using the repository browser.