source: mainline/uspace/drv/usbhub/ports.c@ 69df9373

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 69df9373 was 69df9373, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Add missing documentation

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbhub
30 * @{
31 */
32/** @file
33 * Hub ports functions.
34 */
35#include "port_status.h"
36#include <inttypes.h>
37#include <errno.h>
38#include <str_error.h>
39#include <usb/request.h>
40#include <usb/debug.h>
41
42/** Retrieve port status.
43 *
44 * @param[in] ctrl_pipe Control pipe to use.
45 * @param[in] port Port number (starting at 1).
46 * @param[out] status Where to store the port status.
47 * @return Error code.
48 */
49static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
50 usb_port_status_t *status)
51{
52 size_t recv_size;
53 usb_device_request_setup_packet_t request;
54 usb_port_status_t status_tmp;
55
56 usb_hub_set_port_status_request(&request, port);
57 int rc = usb_pipe_control_read(ctrl_pipe,
58 &request, sizeof(usb_device_request_setup_packet_t),
59 &status_tmp, sizeof(status_tmp), &recv_size);
60 if (rc != EOK) {
61 return rc;
62 }
63
64 if (recv_size != sizeof (status_tmp)) {
65 return ELIMIT;
66 }
67
68 if (status != NULL) {
69 *status = status_tmp;
70 }
71
72 return EOK;
73}
74
75/** Information for fibril for device discovery. */
76struct add_device_phase1 {
77 usb_hub_info_t *hub;
78 size_t port;
79 usb_speed_t speed;
80};
81
82/** Callback for enabling a specific port.
83 *
84 * We wait on a CV until port is reseted.
85 * That is announced via change on interrupt pipe.
86 *
87 * @param port_no Port number (starting at 1).
88 * @param arg Custom argument, points to @c usb_hub_info_t.
89 * @return Error code.
90 */
91static int enable_port_callback(int port_no, void *arg)
92{
93 usb_hub_info_t *hub = (usb_hub_info_t *) arg;
94 int rc;
95 usb_device_request_setup_packet_t request;
96 usb_hub_port_t *my_port = hub->ports + port_no;
97
98 usb_hub_set_reset_port_request(&request, port_no);
99 rc = usb_pipe_control_write(hub->control_pipe,
100 &request, sizeof(request), NULL, 0);
101 if (rc != EOK) {
102 usb_log_warning("Port reset failed: %s.\n", str_error(rc));
103 return rc;
104 }
105
106 /*
107 * Wait until reset completes.
108 */
109 fibril_mutex_lock(&my_port->reset_mutex);
110 while (!my_port->reset_completed) {
111 fibril_condvar_wait(&my_port->reset_cv, &my_port->reset_mutex);
112 }
113 fibril_mutex_unlock(&my_port->reset_mutex);
114
115 /* Clear the port reset change. */
116 rc = usb_hub_clear_port_feature(hub->control_pipe,
117 port_no, USB_HUB_FEATURE_C_PORT_RESET);
118 if (rc != EOK) {
119 usb_log_error("Failed to clear port %d reset feature: %s.\n",
120 port_no, str_error(rc));
121 return rc;
122 }
123
124 return EOK;
125}
126
127/** Fibril for adding a new device.
128 *
129 * Separate fibril is needed because the port reset completion is announced
130 * via interrupt pipe and thus we cannot block here.
131 *
132 * @param arg Pointer to struct add_device_phase1.
133 * @return 0 Always.
134 */
135static int add_device_phase1_worker_fibril(void *arg)
136{
137 struct add_device_phase1 *data
138 = (struct add_device_phase1 *) arg;
139
140 usb_address_t new_address;
141 devman_handle_t child_handle;
142
143 int rc = usb_hc_new_device_wrapper(data->hub->usb_device->ddf_dev,
144 &data->hub->connection, data->speed,
145 enable_port_callback, (int) data->port, data->hub,
146 &new_address, &child_handle,
147 NULL, NULL, NULL);
148
149 if (rc != EOK) {
150 usb_log_error("Failed registering device on port %zu: %s.\n",
151 data->port, str_error(rc));
152 goto leave;
153 }
154
155 data->hub->ports[data->port].attached_device.handle = child_handle;
156 data->hub->ports[data->port].attached_device.address = new_address;
157
158 usb_log_info("Detected new device on `%s' (port %zu), " \
159 "address %d (handle %" PRIun ").\n",
160 data->hub->usb_device->ddf_dev->name, data->port,
161 new_address, child_handle);
162
163leave:
164 free(arg);
165
166 return EOK;
167}
168
169/** Start device adding when connection change is detected.
170 *
171 * This fires a new fibril to complete the device addition.
172 *
173 * @param hub Hub where the change occured.
174 * @param port Port index (starting at 1).
175 * @param speed Speed of the device.
176 * @return Error code.
177 */
178static int add_device_phase1_new_fibril(usb_hub_info_t *hub, size_t port,
179 usb_speed_t speed)
180{
181 struct add_device_phase1 *data
182 = malloc(sizeof(struct add_device_phase1));
183 if (data == NULL) {
184 return ENOMEM;
185 }
186 data->hub = hub;
187 data->port = port;
188 data->speed = speed;
189
190 usb_hub_port_t *the_port = hub->ports + port;
191
192 fibril_mutex_lock(&the_port->reset_mutex);
193 the_port->reset_completed = false;
194 fibril_mutex_unlock(&the_port->reset_mutex);
195
196 int rc = usb_hub_clear_port_feature(hub->control_pipe, port,
197 USB_HUB_FEATURE_C_PORT_CONNECTION);
198 if (rc != EOK) {
199 free(data);
200 usb_log_warning("Failed to clear port change flag: %s.\n",
201 str_error(rc));
202 return rc;
203 }
204
205 fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
206 if (fibril == 0) {
207 free(data);
208 return ENOMEM;
209 }
210 fibril_add_ready(fibril);
211
212 return EOK;
213}
214
215/** Process change on a single port.
216 *
217 * @param hub Hub to which the port belongs.
218 * @param port Port index (starting at 1).
219 */
220static void process_port_change(usb_hub_info_t *hub, size_t port)
221{
222 int rc;
223
224 usb_port_status_t port_status;
225
226 rc = get_port_status(&hub->usb_device->ctrl_pipe, port, &port_status);
227 if (rc != EOK) {
228 usb_log_error("Failed to get port %zu status: %s.\n",
229 port, str_error(rc));
230 return;
231 }
232
233 /*
234 * Check exact nature of the change.
235 */
236 usb_log_debug("Port %zu change status: %x.\n", port,
237 (unsigned int) port_status);
238
239 if (usb_port_connect_change(&port_status)) {
240 bool device_connected = usb_port_dev_connected(&port_status);
241 usb_log_debug("Connection change on port %zu: %s.\n", port,
242 device_connected ? "device attached" : "device removed");
243
244 if (device_connected) {
245 rc = add_device_phase1_new_fibril(hub, port,
246 usb_port_speed(&port_status));
247 if (rc != EOK) {
248 usb_log_error(
249 "Cannot handle change on port %zu: %s.\n",
250 str_error(rc));
251 }
252 } else {
253 usb_hub_removed_device(hub, port);
254 }
255 }
256
257 if (usb_port_overcurrent_change(&port_status)) {
258 if (usb_port_over_current(&port_status)) {
259 usb_log_warning("Overcurrent on port %zu.\n", port);
260 usb_hub_over_current(hub, port);
261 } else {
262 usb_log_debug("Overcurrent on port %zu autoresolved.\n",
263 port);
264 }
265 }
266
267 if (usb_port_reset_completed(&port_status)) {
268 usb_log_debug("Port %zu reset complete.\n", port);
269 if (usb_port_enabled(&port_status)) {
270 /* Finalize device adding. */
271 usb_hub_port_t *the_port = hub->ports + port;
272 fibril_mutex_lock(&the_port->reset_mutex);
273 the_port->reset_completed = true;
274 fibril_condvar_broadcast(&the_port->reset_cv);
275 fibril_mutex_unlock(&the_port->reset_mutex);
276 } else {
277 usb_log_warning(
278 "Port %zu reset complete but port not enabled.\n",
279 port);
280 }
281 }
282
283 usb_port_set_connect_change(&port_status, false);
284 usb_port_set_reset(&port_status, false);
285 usb_port_set_reset_completed(&port_status, false);
286 usb_port_set_dev_connected(&port_status, false);
287 if (port_status >> 16) {
288 usb_log_warning("Unsupported change on port %zu: %x.\n",
289 port, (unsigned int) port_status);
290 }
291}
292
293
294/** Callback for polling hub for port changes.
295 *
296 * @param dev Device where the change occured.
297 * @param change_bitmap Bitmap of changed ports.
298 * @param change_bitmap_size Size of the bitmap in bytes.
299 * @param arg Custom argument, points to @c usb_hub_info_t.
300 * @return Whether to continue polling.
301 */
302bool hub_port_changes_callback(usb_device_t *dev,
303 uint8_t *change_bitmap, size_t change_bitmap_size, void *arg)
304{
305 usb_hub_info_t *hub = (usb_hub_info_t *) arg;
306
307 /* FIXME: check that we received enough bytes. */
308 if (change_bitmap_size == 0) {
309 goto leave;
310 }
311
312 size_t port;
313 for (port = 1; port < hub->port_count + 1; port++) {
314 bool change = (change_bitmap[port / 8] >> (port % 8)) % 2;
315 if (change) {
316 process_port_change(hub, port);
317 }
318 }
319
320
321leave:
322 /* FIXME: proper interval. */
323 async_usleep(1000 * 1000 * 10 );
324
325 return true;
326}
327
328
329/**
330 * @}
331 */
Note: See TracBrowser for help on using the repository browser.