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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d6c953e9 was e231d26, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

usbhub: Fix compile issues.

  • Property mode set to 100644
File size: 14.1 KB
RevLine 
[2ad98fd]1/*
2 * Copyright (c) 2011 Vojtech Horky
[3b617579]3 * Copyright (c) 2011 Jan Vesely
[2ad98fd]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup drvusbhub
30 * @{
31 */
32/** @file
33 * Hub ports functions.
34 */
35
36#include <bool.h>
[d650494]37#include <devman.h>
[2ad98fd]38#include <errno.h>
39#include <str_error.h>
40#include <inttypes.h>
41#include <fibril_synch.h>
42
43#include <usb/debug.h>
[d650494]44#include <usb/dev/hub.h>
[2ad98fd]45
[a590a23]46#include "port.h"
[2ad98fd]47#include "usbhub.h"
[400f363]48#include "status.h"
[2ad98fd]49
50/** Information for fibril for device discovery. */
51struct add_device_phase1 {
52 usb_hub_info_t *hub;
[aefa0d5]53 usb_hub_port_t *port;
[2ad98fd]54 usb_speed_t speed;
55};
56
[d650494]57static void usb_hub_port_removed_device(usb_hub_port_t *port,
58 usb_hub_info_t *hub);
[442fa6b]59static void usb_hub_port_reset_completed(usb_hub_port_t *port,
60 usb_port_status_t status);
61static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
[2ad98fd]62static int enable_port_callback(int port_no, void *arg);
63static int add_device_phase1_worker_fibril(void *arg);
[aefa0d5]64static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_info_t *hub,
[2ad98fd]65 usb_speed_t speed);
66
[aff1880]67/**
68 * Clear feature on hub port.
69 *
70 * @param hc Host controller telephone
71 * @param address Hub address
72 * @param port_index Port
73 * @param feature Feature selector
74 * @return Operation result
75 */
[442fa6b]76int usb_hub_port_clear_feature(
[c0587d90]77 usb_hub_port_t *port, usb_hub_class_feature_t feature)
[aff1880]78{
[c0587d90]79 assert(port);
[aff1880]80 usb_device_request_setup_packet_t clear_request = {
81 .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE,
82 .request = USB_DEVREQ_CLEAR_FEATURE,
83 .value = feature,
[c0587d90]84 .index = port->port_number,
[aff1880]85 .length = 0,
86 };
[c0587d90]87 return usb_pipe_control_write(port->control_pipe, &clear_request,
[aff1880]88 sizeof(clear_request), NULL, 0);
89}
90/*----------------------------------------------------------------------------*/
91/**
92 * Clear feature on hub port.
93 *
94 * @param hc Host controller telephone
95 * @param address Hub address
96 * @param port_index Port
97 * @param feature Feature selector
98 * @return Operation result
99 */
[442fa6b]100int usb_hub_port_set_feature(
[c0587d90]101 usb_hub_port_t *port, usb_hub_class_feature_t feature)
[aff1880]102{
[c0587d90]103 assert(port);
[aff1880]104 usb_device_request_setup_packet_t clear_request = {
105 .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE,
106 .request = USB_DEVREQ_SET_FEATURE,
[c0587d90]107 .index = port->port_number,
[aff1880]108 .value = feature,
109 .length = 0,
110 };
[c0587d90]111 return usb_pipe_control_write(port->control_pipe, &clear_request,
[aff1880]112 sizeof(clear_request), NULL, 0);
113}
[442fa6b]114/*----------------------------------------------------------------------------*/
115void usb_hub_port_reset_fail(usb_hub_port_t *port)
116{
117 assert(port);
118 fibril_mutex_lock(&port->mutex);
119 port->reset_completed = true;
120 port->reset_okay = false;
121 fibril_condvar_broadcast(&port->reset_cv);
122 fibril_mutex_unlock(&port->mutex);
123}
124/*----------------------------------------------------------------------------*/
[2ad98fd]125/**
126 * Process interrupts on given hub port
127 *
128 * Accepts connection, over current and port reset change.
129 * @param hub hub representation
130 * @param port port number, starting from 1
131 */
[0212751]132void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_info_t *hub)
[983e135]133{
[0212751]134 assert(port);
135 assert(hub);
136 usb_log_debug("Interrupt at port %zu\n", port->port_number);
[2ad98fd]137
138 usb_port_status_t status;
[0212751]139 const int opResult = get_port_status(port, &status);
[2ad98fd]140 if (opResult != EOK) {
141 usb_log_error("Failed to get port %zu status: %s.\n",
[0212751]142 port->port_number, str_error(opResult));
[2ad98fd]143 return;
144 }
[d6e2938]145
146 /* Connection change */
147 if (status & USB_HUB_PORT_C_STATUS_CONNECTION) {
[0212751]148 const bool connected =
[d6e2938]149 (status & USB_HUB_PORT_STATUS_CONNECTION) != 0;
150 usb_log_debug("Connection change on port %zu: device %s.\n",
[0212751]151 port->port_number, connected ? "attached" : "removed");
[d650494]152
[d6e2938]153 /* ACK the change */
[0212751]154 const int opResult = usb_hub_port_clear_feature(port,
155 USB_HUB_FEATURE_C_PORT_CONNECTION);
[d6e2938]156 if (opResult != EOK) {
[0212751]157 usb_log_warning("Failed to clear port-change-connection"
158 " flag: %s.\n", str_error(opResult));
[d6e2938]159 }
[2ad98fd]160
[0212751]161 if (connected) {
[aefa0d5]162 const int opResult = create_add_device_fibril(port, hub,
163 usb_port_speed(status));
[2ad98fd]164 if (opResult != EOK) {
165 usb_log_error(
166 "Cannot handle change on port %zu: %s.\n",
[0212751]167 port->port_number, str_error(opResult));
[2ad98fd]168 }
169 } else {
[d650494]170 /* If enabled change was reported leave the removal
171 * to that handler, it shall ACK the change too. */
172 if (!(status & USB_HUB_PORT_C_STATUS_ENABLED)) {
173 usb_hub_port_removed_device(port, hub);
174 }
[2ad98fd]175 }
176 }
[d6e2938]177
178 /* Enable change, ports are automatically disabled on errors. */
179 if (status & USB_HUB_PORT_C_STATUS_ENABLED) {
[d650494]180 usb_log_info("Port %zu, disabled because of errors.\n",
181 port->port_number);
182 usb_hub_port_removed_device(port, hub);
[0212751]183 const int rc = usb_hub_port_clear_feature(port,
184 USB_HUB_FEATURE_C_PORT_ENABLE);
185 if (rc != EOK) {
186 usb_log_error(
187 "Failed to clear port %zu enable change feature: "
188 "%s.\n", port->port_number, str_error(rc));
189 }
[d6e2938]190
[2ad98fd]191 }
[d6e2938]192
193 /* Suspend change */
194 if (status & USB_HUB_PORT_C_STATUS_SUSPEND) {
195 usb_log_error("Port %zu went to suspend state, this should"
[0212751]196 "NOT happen as we do not support suspend state!",
197 port->port_number);
198 const int rc = usb_hub_port_clear_feature(port,
199 USB_HUB_FEATURE_C_PORT_SUSPEND);
200 if (rc != EOK) {
201 usb_log_error(
202 "Failed to clear port %zu suspend change feature: "
203 "%s.\n", port->port_number, str_error(rc));
204 }
[2ad98fd]205 }
[d6e2938]206
207 /* Over current */
208 if (status & USB_HUB_PORT_C_STATUS_OC) {
209 /* According to the USB specs:
210 * 11.13.5 Over-current Reporting and Recovery
211 * Hub device is responsible for putting port in power off
212 * mode. USB system software is responsible for powering port
[0212751]213 * back on when the over-current condition is gone */
214 const int rc = usb_hub_port_clear_feature(port,
215 USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
216 if (rc != EOK) {
217 usb_log_error(
218 "Failed to clear port %zu OC change feature: %s.\n",
219 port->port_number, str_error(rc));
220 }
[d6e2938]221 if (!(status & ~USB_HUB_PORT_STATUS_OC)) {
[0212751]222 const int rc = usb_hub_port_set_feature(
223 port, USB_HUB_FEATURE_PORT_POWER);
224 if (rc != EOK) {
225 usb_log_error(
[e231d26]226 "Failed to set port %zu power after OC:"
[0212751]227 " %s.\n", port->port_number, str_error(rc));
228 }
[d0c060b]229 }
230 }
[d6e2938]231
232 /* Port reset, set on port reset complete. */
233 if (status & USB_HUB_PORT_C_STATUS_RESET) {
[0212751]234 usb_hub_port_reset_completed(port, status);
[192ba25]235 }
[d6e2938]236
[0212751]237 usb_log_debug("Port %zu status 0x%08" PRIx32 "\n",
238 port->port_number, status);
[2ad98fd]239}
240
241/**
242 * routine called when a device on port has been removed
243 *
244 * If the device on port had default address, it releases default address.
245 * Otherwise does not do anything, because DDF does not allow to remove device
246 * from it`s device tree.
247 * @param hub hub representation
248 * @param port port number, starting from 1
249 */
[d650494]250static void usb_hub_port_removed_device(usb_hub_port_t *port,
251 usb_hub_info_t *hub)
[d6e2938]252{
[442fa6b]253 assert(port);
[d650494]254 assert(hub);
[442fa6b]255 if (port->attached_device.address >= 0) {
256 fibril_mutex_lock(&port->mutex);
257 port->attached_device.address = -1;
258 port->attached_device.handle = 0;
259 fibril_mutex_unlock(&port->mutex);
[d650494]260 usb_log_info("Removed device on port %zu.\n",
261 port->port_number);
[2ad98fd]262 } else {
[d650494]263 usb_log_warning(
264 "Device on port %zu removed before being registered.\n",
265 port->port_number);
[46e078a]266
267 /*
268 * Device was removed before port reset completed.
269 * We will announce a failed port reset to unblock the
270 * port reset callback from new device wrapper.
271 */
[442fa6b]272 usb_hub_port_reset_fail(port);
[2ad98fd]273 }
274}
275
276/**
277 * Process port reset change
278 *
[d650494]279 * After this change port should be enabled, unless some problem occurred.
[2ad98fd]280 * This functions triggers second phase of enabling new device.
281 * @param hub
282 * @param port
283 * @param status
284 */
[442fa6b]285static void usb_hub_port_reset_completed(usb_hub_port_t *port,
286 usb_port_status_t status)
[d6e2938]287{
[442fa6b]288 assert(port);
289 fibril_mutex_lock(&port->mutex);
[aff1880]290 /* Finalize device adding. */
[442fa6b]291 port->reset_completed = true;
292 port->reset_okay = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
[aff1880]293
[442fa6b]294 if (port->reset_okay) {
295 usb_log_debug("Port %zu reset complete.\n", port->port_number);
[2ad98fd]296 } else {
297 usb_log_warning(
[442fa6b]298 "Port %zu reset complete but port not enabled.\n",
299 port->port_number);
[2ad98fd]300 }
[442fa6b]301 fibril_condvar_broadcast(&port->reset_cv);
302 fibril_mutex_unlock(&port->mutex);
[aff1880]303
[d0c060b]304 /* Clear the port reset change. */
[442fa6b]305 int rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET);
[d0c060b]306 if (rc != EOK) {
[442fa6b]307 usb_log_error(
[e231d26]308 "Failed to clear port %zu reset change feature: %s.\n",
[442fa6b]309 port->port_number, str_error(rc));
[d0c060b]310 }
[2ad98fd]311}
[aff1880]312/*----------------------------------------------------------------------------*/
[2ad98fd]313/** Retrieve port status.
314 *
315 * @param[in] ctrl_pipe Control pipe to use.
316 * @param[in] port Port number (starting at 1).
317 * @param[out] status Where to store the port status.
318 * @return Error code.
319 */
[442fa6b]320static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status)
[b3433a2]321{
[442fa6b]322 assert(port);
[d6e2938]323 /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6
324 * Generic GET_STATUS request cannot be used because of the difference
325 * in status data size (2B vs. 4B)*/
[b3433a2]326 const usb_device_request_setup_packet_t request = {
327 .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS,
328 .request = USB_HUB_REQUEST_GET_STATUS,
329 .value = 0,
[442fa6b]330 .index = port->port_number,
[b3433a2]331 .length = sizeof(usb_port_status_t),
332 };
[442fa6b]333 size_t recv_size;
334 usb_port_status_t status_tmp;
[b3433a2]335
[442fa6b]336 const int rc = usb_pipe_control_read(port->control_pipe,
[b3433a2]337 &request, sizeof(usb_device_request_setup_packet_t),
338 &status_tmp, sizeof(status_tmp), &recv_size);
[2ad98fd]339 if (rc != EOK) {
340 return rc;
341 }
342
343 if (recv_size != sizeof (status_tmp)) {
344 return ELIMIT;
345 }
346
347 if (status != NULL) {
348 *status = status_tmp;
349 }
350
351 return EOK;
352}
[d6e2938]353/*----------------------------------------------------------------------------*/
[2ad98fd]354/** Callback for enabling a specific port.
355 *
356 * We wait on a CV until port is reseted.
357 * That is announced via change on interrupt pipe.
358 *
359 * @param port_no Port number (starting at 1).
360 * @param arg Custom argument, points to @c usb_hub_info_t.
361 * @return Error code.
362 */
[d6e2938]363static int enable_port_callback(int port_no, void *arg)
364{
[c0587d90]365 usb_hub_port_t *port = arg;
366 const int rc =
[442fa6b]367 usb_hub_port_set_feature(port, USB_HUB_FEATURE_PORT_RESET);
[2ad98fd]368 if (rc != EOK) {
369 usb_log_warning("Port reset failed: %s.\n", str_error(rc));
370 return rc;
371 }
372
373 /*
374 * Wait until reset completes.
375 */
[442fa6b]376 fibril_mutex_lock(&port->mutex);
[c0587d90]377 while (!port->reset_completed) {
[442fa6b]378 fibril_condvar_wait(&port->reset_cv, &port->mutex);
[2ad98fd]379 }
[442fa6b]380 fibril_mutex_unlock(&port->mutex);
[2ad98fd]381
[c0587d90]382 if (port->reset_okay) {
[46e078a]383 return EOK;
384 } else {
385 return ESTALL;
386 }
[2ad98fd]387}
388
389/** Fibril for adding a new device.
390 *
391 * Separate fibril is needed because the port reset completion is announced
392 * via interrupt pipe and thus we cannot block here.
393 *
394 * @param arg Pointer to struct add_device_phase1.
395 * @return 0 Always.
396 */
[d6e2938]397static int add_device_phase1_worker_fibril(void *arg)
398{
399 struct add_device_phase1 *data = arg;
400 assert(data);
[2ad98fd]401
402 usb_address_t new_address;
403 devman_handle_t child_handle;
404
[d6e2938]405 const int rc = usb_hc_new_device_wrapper(data->hub->usb_device->ddf_dev,
[2ad98fd]406 &data->hub->connection, data->speed,
[aefa0d5]407 enable_port_callback, (int) data->port->port_number,
408 data->port, &new_address, &child_handle,
[2ad98fd]409 NULL, NULL, NULL);
410
411 if (rc != EOK) {
412 usb_log_error("Failed registering device on port %zu: %s.\n",
[aefa0d5]413 data->port->port_number, str_error(rc));
[2ad98fd]414 goto leave;
415 }
416
[aefa0d5]417 fibril_mutex_lock(&data->port->mutex);
418 data->port->attached_device.handle = child_handle;
419 data->port->attached_device.address = new_address;
420 fibril_mutex_unlock(&data->port->mutex);
[2ad98fd]421
422 usb_log_info("Detected new device on `%s' (port %zu), "
423 "address %d (handle %" PRIun ").\n",
[aefa0d5]424 data->hub->usb_device->ddf_dev->name, data->port->port_number,
[2ad98fd]425 new_address, child_handle);
426
427leave:
[3fb5a3e]428 fibril_mutex_lock(&data->hub->pending_ops_mutex);
429 assert(data->hub->pending_ops_count > 0);
[aefa0d5]430 --data->hub->pending_ops_count;
[3fb5a3e]431 fibril_condvar_signal(&data->hub->pending_ops_cv);
432 fibril_mutex_unlock(&data->hub->pending_ops_mutex);
433
[aefa0d5]434 free(arg);
[3fb5a3e]435
[2ad98fd]436 return EOK;
437}
438
439/** Start device adding when connection change is detected.
440 *
441 * This fires a new fibril to complete the device addition.
442 *
443 * @param hub Hub where the change occured.
444 * @param port Port index (starting at 1).
445 * @param speed Speed of the device.
446 * @return Error code.
447 */
[aefa0d5]448static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_info_t *hub,
[d6e2938]449 usb_speed_t speed)
450{
[aefa0d5]451 assert(hub);
452 assert(port);
[2ad98fd]453 struct add_device_phase1 *data
[5c1a65e]454 = malloc(sizeof (struct add_device_phase1));
[2ad98fd]455 if (data == NULL) {
456 return ENOMEM;
457 }
458 data->hub = hub;
459 data->port = port;
460 data->speed = speed;
461
[aefa0d5]462 fibril_mutex_lock(&port->mutex);
463 port->reset_completed = false;
464 fibril_mutex_unlock(&port->mutex);
[2ad98fd]465
466 fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
467 if (fibril == 0) {
468 free(data);
469 return ENOMEM;
470 }
[3fb5a3e]471 fibril_mutex_lock(&hub->pending_ops_mutex);
[aefa0d5]472 ++hub->pending_ops_count;
[3fb5a3e]473 fibril_mutex_unlock(&hub->pending_ops_mutex);
[2ad98fd]474 fibril_add_ready(fibril);
475
476 return EOK;
477}
478
479/**
480 * @}
481 */
Note: See TracBrowser for help on using the repository browser.