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

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

usb: cstyle

  • 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 }
[94f8c363]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,
128 USB_HUB_FEATURE_PORT_RESET))) {
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,
142 port->speed))) {
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,
167 USB3_HUB_FEATURE_BH_PORT_RESET))) {
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,
181 port->speed))) {
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
[5a6cc679]202 const errno_t err = port->hub->speed == USB_SPEED_SUPER
[3f02935]203 ? enumerate_device_usb3(port, exch)
204 : enumerate_device_usb2(port, exch);
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);
[ae3a941]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
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
[94f8c363]244 usb_port_disabled(&port->base, &remove_device);
[c4e84ed6]245
246 if (!overcurrent) {
[ae3a941]247 const errno_t err = usb_hub_set_port_feature(port->hub,
248 port->port_number, USB_HUB_FEATURE_PORT_POWER);
[c4e84ed6]249 if (err)
[ae3a941]250 port_log(error, port, "Failed to set port power "
251 "after OC: %s.", str_error(err));
[c4e84ed6]252 }
253}
254
255static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
256{
[cd3fa47]257 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
[c4e84ed6]258
[0f79283b]259 if (enabled) {
[129b821f]260 port->speed = get_port_speed(port, status);
[0f79283b]261 usb_port_enabled(&port->base);
262 } else
[94f8c363]263 usb_port_disabled(&port->base, &remove_device);
[2ad98fd]264}
265
[c4e84ed6]266typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
267
[cd3fa47]268static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
[ae3a941]269 change_handler_t handler, usb_port_status_t mask,
270 usb_hub_class_feature_t feature)
[cd3fa47]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}
[c4e84ed6]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 */
[94f8c363]292void usb_hub_port_process_interrupt(usb_hub_port_t *port)
[c4e84ed6]293{
294 assert(port);
295 port_log(debug2, port, "Interrupt.");
296
297 usb_port_status_t status = 0;
[ae3a941]298 const errno_t err = usb_hub_get_port_status(port->hub,
299 port->port_number, &status);
[c4e84ed6]300 if (err != EOK) {
[ae3a941]301 port_log(error, port, "Failed to get port status: %s.",
302 str_error(err));
[c4e84ed6]303 return;
304 }
305
[cd3fa47]306 check_port_change(port, &status, &port_changed_connection,
[ae3a941]307 USB_HUB_PORT_STATUS_C_CONNECTION,
308 USB_HUB_FEATURE_C_PORT_CONNECTION);
[32b2a6f2]309
[cd3fa47]310 check_port_change(port, &status, &port_changed_overcurrent,
311 USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
[c4e84ed6]312
[cd3fa47]313 check_port_change(port, &status, &port_changed_reset,
314 USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
[c4e84ed6]315
[cd3fa47]316 if (port->hub->speed <= USB_SPEED_HIGH) {
317 check_port_change(port, &status, &port_changed_enabled,
[ae3a941]318 USB2_HUB_PORT_STATUS_C_ENABLE,
319 USB2_HUB_FEATURE_C_PORT_ENABLE);
[cd3fa47]320 } else {
321 check_port_change(port, &status, &port_changed_reset,
[ae3a941]322 USB3_HUB_PORT_STATUS_C_BH_RESET,
323 USB3_HUB_FEATURE_C_BH_PORT_RESET);
[c4e84ed6]324
[cd3fa47]325 check_port_change(port, &status, NULL,
[ae3a941]326 USB3_HUB_PORT_STATUS_C_LINK_STATE,
327 USB3_HUB_FEATURE_C_PORT_LINK_STATE);
[c4e84ed6]328 }
329
[9d3536e]330 /* Check for changes we ignored */
331 if (status & 0xffff0000) {
[ae3a941]332 port_log(debug, port, "Port status change igored. "
333 "Status: %#08" PRIx32, status);
[9d3536e]334 }
[c4e84ed6]335}
336
337
[2ad98fd]338/**
339 * @}
340 */
Note: See TracBrowser for help on using the repository browser.