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

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

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

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