source: mainline/uspace/drv/bus/usb/uhcirh/port.c@ 67f55e7b

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

uhcirh: Improve error handling.

  • Property mode set to 100644
File size: 11.7 KB
RevLine 
[c56dbe0]1/*
2 * Copyright (c) 2011 Jan Vesely
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 */
[f123909]28/** @addtogroup drvusbuhcirh
[c56dbe0]29 * @{
30 */
31/** @file
[f123909]32 * @brief UHCI root hub port routines
[c56dbe0]33 */
[d394e57a]34#include <libarch/ddi.h> /* pio_read and pio_write */
35#include <fibril_synch.h> /* async_usleep */
[92f924c8]36#include <errno.h>
[4e8e1f5]37#include <str_error.h>
[3375bd4]38#include <async.h>
[aa1922c]39#include <devman.h>
[7ce0fe3]40
[245b56b5]41#include <usb/usb.h> /* usb_address_t */
[7ce0fe3]42#include <usb/debug.h>
[28f660d]43
44#include "port.h"
45
[612af1a0]46#define MAX_ERROR_COUNT 5
47
[f9c03b5]48static int uhci_port_check(void *port);
[32ec5671]49static int uhci_port_reset_enable(void *arg);
[275bf456]50static int uhci_port_new_device(uhci_port_t *port, usb_speed_t speed);
[0bd2879]51static int uhci_port_remove_device(uhci_port_t *port);
[28f660d]52static int uhci_port_set_enabled(uhci_port_t *port, bool enabled);
[f123909]53static void uhci_port_print_status(
54 uhci_port_t *port, const port_status_t value);
55
56/** Register reading helper function.
57 *
58 * @param[in] port Structure to use.
59 * @return Error code. (Always EOK)
60 */
61static inline port_status_t uhci_port_read_status(uhci_port_t *port)
62{
63 assert(port);
64 return pio_read_16(port->address);
65}
66/*----------------------------------------------------------------------------*/
67/** Register writing helper function.
68 *
69 * @param[in] port Structure to use.
[23f40280]70 * @param[in] val New register value.
[f123909]71 * @return Error code. (Always EOK)
72 */
[540abef]73static inline void uhci_port_write_status(uhci_port_t *port, port_status_t val)
[f123909]74{
75 assert(port);
[540abef]76 pio_write_16(port->address, val);
[f123909]77}
[275bf456]78/*----------------------------------------------------------------------------*/
[f123909]79/** Initialize UHCI root hub port instance.
[275bf456]80 *
81 * @param[in] port Memory structure to use.
[23f40280]82 * @param[in] address Address of I/O register.
[275bf456]83 * @param[in] number Port number.
84 * @param[in] usec Polling interval.
[563ead9]85 * @param[in] rh Pointer to ddf instance of the root hub driver.
[275bf456]86 * @return Error code.
87 *
[f123909]88 * Creates and starts the polling fibril.
[275bf456]89 */
90int uhci_port_init(uhci_port_t *port,
91 port_status_t *address, unsigned number, unsigned usec, ddf_dev_t *rh)
[1256a0a]92{
93 assert(port);
[563ead9]94 char *id_string;
95 asprintf(&id_string, "Port (%p - %u)", port, number);
96 if (id_string == NULL) {
[ab5a43d1]97 return ENOMEM;
98 }
[275bf456]99
[563ead9]100 port->id_string = id_string;
[1256a0a]101 port->address = address;
102 port->number = number;
103 port->wait_period_usec = usec;
[90994fa]104 port->attached_device.fun = NULL;
[aa1922c]105 port->attached_device.address = -1;
[1256a0a]106 port->rh = rh;
[275bf456]107
[540abef]108 int ret =
109 usb_hc_connection_initialize_from_device(&port->hc_connection, rh);
110 if (ret != EOK) {
[563ead9]111 usb_log_error("%s: failed to initialize connection to HC.",
112 port->id_string);
113 free(id_string);
[540abef]114 return ret;
[f673f60]115 }
[1256a0a]116
117 port->checker = fibril_create(uhci_port_check, port);
118 if (port->checker == 0) {
[f123909]119 usb_log_error("%s: failed to create polling fibril.",
120 port->id_string);
[563ead9]121 free(id_string);
[1256a0a]122 return ENOMEM;
123 }
[275bf456]124
[1256a0a]125 fibril_add_ready(port->checker);
[4125b7d]126 usb_log_debug("%s: Started polling fibril (%" PRIun ").\n",
[f123909]127 port->id_string, port->checker);
[1256a0a]128 return EOK;
129}
130/*----------------------------------------------------------------------------*/
[f123909]131/** Cleanup UHCI root hub port instance.
[275bf456]132 *
133 * @param[in] port Memory structure to use.
134 *
135 * Stops the polling fibril.
136 */
[1256a0a]137void uhci_port_fini(uhci_port_t *port)
138{
[f123909]139 assert(port);
140 free(port->id_string);
[563ead9]141 // TODO: Kill fibril here
[1256a0a]142 return;
143}
[28f660d]144/*----------------------------------------------------------------------------*/
[275bf456]145/** Periodically checks port status and reports new devices.
146 *
[f123909]147 * @param[in] port Port structure to use.
[275bf456]148 * @return Error code.
149 */
[28f660d]150int uhci_port_check(void *port)
151{
[275bf456]152 uhci_port_t *instance = port;
153 assert(instance);
[1ae51ae]154
[612af1a0]155 unsigned allowed_failures = MAX_ERROR_COUNT;
156#define CHECK_RET_FAIL(ret, msg...) \
157 if (ret != EOK) { \
158 usb_log_error(msg); \
159 if (!(allowed_failures-- > 0)) { \
160 usb_log_fatal( \
161 "Maximum number of failures reached, " \
162 "bailing out.\n"); \
163 return ret; \
164 } \
165 continue; \
166 } else (void)0
167
[28f660d]168 while (1) {
[275bf456]169 async_usleep(instance->wait_period_usec);
[1ae51ae]170
[f123909]171 /* Read register value */
[563ead9]172 const port_status_t port_status =
173 uhci_port_read_status(instance);
[28f660d]174
[f123909]175 /* Print the value if it's interesting */
[67352d2]176 if (port_status & ~STATUS_ALWAYS_ONE)
177 uhci_port_print_status(instance, port_status);
[28f660d]178
[275bf456]179 if ((port_status & STATUS_CONNECTED_CHANGED) == 0)
180 continue;
[1ae51ae]181
[ab5a43d1]182 usb_log_debug("%s: Connected change detected: %x.\n",
183 instance->id_string, port_status);
[1ae51ae]184
[612af1a0]185 int ret = usb_hc_connection_open(&instance->hc_connection);
186 CHECK_RET_FAIL(ret, "%s: Failed to connect to HC %s.\n",
187 instance->id_string, str_error(ret));
188
[275bf456]189 /* Remove any old device */
[90994fa]190 if (instance->attached_device.fun) {
[275bf456]191 uhci_port_remove_device(instance);
192 }
[f20f9e2]193
[275bf456]194 if ((port_status & STATUS_CONNECTED) != 0) {
[612af1a0]195 /* New device, this will take care of WC bits */
[275bf456]196 const usb_speed_t speed =
197 ((port_status & STATUS_LOW_SPEED) != 0) ?
198 USB_SPEED_LOW : USB_SPEED_FULL;
199 uhci_port_new_device(instance, speed);
200 } else {
201 /* Write one to WC bits, to ack changes */
[3005db6]202 uhci_port_write_status(instance, port_status);
[7f810b3]203 usb_log_debug("%s: status change ACK.\n",
[ab5a43d1]204 instance->id_string);
[275bf456]205 }
[f673f60]206
[563ead9]207 ret = usb_hc_connection_close(&instance->hc_connection);
[612af1a0]208 CHECK_RET_FAIL(ret, "%s: Failed to disconnect from hc: %s.\n",
209 instance->id_string, str_error(ret));
[28f660d]210 }
211 return EOK;
212}
[275bf456]213/*----------------------------------------------------------------------------*/
[9df965ec]214/** Callback for enabling port during adding a new device.
215 *
216 * @param portno Port number (unused).
217 * @param arg Pointer to uhci_port_t of port with the new device.
218 * @return Error code.
[f123909]219 *
220 * Resets and enables the ub port.
[9df965ec]221 */
[32ec5671]222int uhci_port_reset_enable(void *arg)
[9df965ec]223{
[e247d83]224 uhci_port_t *port = arg;
[563ead9]225 assert(port);
[1f5c1e61]226
[fb1d4990]227 usb_log_debug2("%s: new_device_enable_port.\n", port->id_string);
[28f660d]228
[fb1d4990]229 /*
[11349a85]230 * Resets from root ports should be nominally 50ms (USB spec 7.1.7.3)
[e68de30]231 */
[1f5c1e61]232 {
[fb1d4990]233 usb_log_debug("%s: Reset Signal start.\n", port->id_string);
[3005db6]234 port_status_t port_status = uhci_port_read_status(port);
[1f5c1e61]235 port_status |= STATUS_IN_RESET;
[3005db6]236 uhci_port_write_status(port, port_status);
[fb1d4990]237 async_usleep(50000);
[3005db6]238 port_status = uhci_port_read_status(port);
[1f5c1e61]239 port_status &= ~STATUS_IN_RESET;
[3005db6]240 uhci_port_write_status(port, port_status);
[11349a85]241 while (uhci_port_read_status(port) & STATUS_IN_RESET);
[1f5c1e61]242 }
[5f94a0c]243 /* PIO delay, should not be longer than 3ms as the device might
244 * enter suspend state. */
[81db65b]245 udelay(10);
[013e4ca4]246 /* Enable the port. */
247 uhci_port_set_enabled(port, true);
[9df965ec]248 return EOK;
249}
250/*----------------------------------------------------------------------------*/
[f123909]251/** Initialize and report connected device.
[275bf456]252 *
[f123909]253 * @param[in] port Port structure to use.
[275bf456]254 * @param[in] speed Detected speed.
255 * @return Error code.
256 *
257 * Uses libUSB function to do the actual work.
258 */
259int uhci_port_new_device(uhci_port_t *port, usb_speed_t speed)
[9df965ec]260{
261 assert(port);
262 assert(usb_hc_connection_is_opened(&port->hc_connection));
[28f660d]263
[fbefd0e]264 usb_log_debug("%s: Detected new device.\n", port->id_string);
[0bd2879]265
[612af1a0]266 int ret, count = MAX_ERROR_COUNT;
[11349a85]267 do {
268 ret = usb_hc_new_device_wrapper(port->rh, &port->hc_connection,
[32ec5671]269 speed, uhci_port_reset_enable, port,
[013517b]270 &port->attached_device.address, NULL, NULL,
[90994fa]271 &port->attached_device.fun);
[612af1a0]272 } while (ret != EOK && count-- > 0);
[1ae51ae]273
[f9c03b5]274 if (ret != EOK) {
[ab5a43d1]275 usb_log_error("%s: Failed(%d) to add device: %s.\n",
[f9c03b5]276 port->id_string, ret, str_error(ret));
[f9dd44d]277 uhci_port_set_enabled(port, false);
[f9c03b5]278 return ret;
[f9dd44d]279 }
[0bd2879]280
[612af1a0]281 usb_log_info("%s: New device, address %d (handle %" PRIun ").\n",
282 port->id_string, port->attached_device.address,
[90994fa]283 port->attached_device.fun->handle);
[28f660d]284 return EOK;
285}
286/*----------------------------------------------------------------------------*/
[f123909]287/** Remove device.
[275bf456]288 *
[aa1922c]289 * @param[in] port Port instance to use.
[275bf456]290 * @return Error code.
291 */
292int uhci_port_remove_device(uhci_port_t *port)
[0bd2879]293{
[7be3638]294 assert(port);
295 /* There is nothing to remove. */
[90994fa]296 if (port->attached_device.fun == NULL) {
[7be3638]297 usb_log_warning("%s: Removed a ghost device.\n",
298 port->id_string);
299 assert(port->attached_device.address == -1);
300 return EOK;
301 }
302
303 usb_log_debug("%s: Removing device.\n", port->id_string);
[aa1922c]304
305 /* Stop driver first */
[90994fa]306 int ret = ddf_fun_unbind(port->attached_device.fun);
[aa1922c]307 if (ret != EOK) {
[7be3638]308 usb_log_error("%s: Failed to remove child function: %s.\n",
309 port->id_string, str_error(ret));
[aa1922c]310 return ret;
311 }
[90994fa]312 ddf_fun_destroy(port->attached_device.fun);
313 port->attached_device.fun = NULL;
[aa1922c]314
315 /* Driver stopped, free used address */
316 ret = usb_hc_unregister_device(&port->hc_connection,
317 port->attached_device.address);
318 if (ret != EOK) {
[7be3638]319 usb_log_error("%s: Failed to unregister address of removed "
320 "device: %s.\n", port->id_string, str_error(ret));
[aa1922c]321 return ret;
322 }
323 port->attached_device.address = -1;
[7be3638]324
325 usb_log_info("%s: Removed attached device.\n", port->id_string);
[aa1922c]326 return EOK;
[0bd2879]327}
328/*----------------------------------------------------------------------------*/
[f123909]329/** Enable or disable root hub port.
[275bf456]330 *
[f123909]331 * @param[in] port Port structure to use.
332 * @param[in] enabled Port status to set.
[275bf456]333 * @return Error code. (Always EOK)
334 */
335int uhci_port_set_enabled(uhci_port_t *port, bool enabled)
[28f660d]336{
337 assert(port);
338
[275bf456]339 /* Read register value */
[3005db6]340 port_status_t port_status = uhci_port_read_status(port);
[28f660d]341
[275bf456]342 /* Set enabled bit */
[8f748215]343 if (enabled) {
344 port_status |= STATUS_ENABLED;
345 } else {
346 port_status &= ~STATUS_ENABLED;
347 }
[275bf456]348
349 /* Write new value. */
[3005db6]350 uhci_port_write_status(port, port_status);
[8f748215]351
[c0cea918]352 /* Wait for port to become enabled */
353 do {
354 port_status = uhci_port_read_status(port);
355 } while ((port_status & STATUS_CONNECTED) &&
356 !(port_status & STATUS_ENABLED));
357
[fbefd0e]358 usb_log_debug("%s: %sabled port.\n",
[ab5a43d1]359 port->id_string, enabled ? "En" : "Dis");
[28f660d]360 return EOK;
361}
362/*----------------------------------------------------------------------------*/
[f123909]363/** Print the port status value in a human friendly way
364 *
365 * @param[in] port Port structure to use.
366 * @param[in] value Port register value to print.
367 * @return Error code. (Always EOK)
368 */
369void uhci_port_print_status(uhci_port_t *port, const port_status_t value)
370{
371 assert(port);
372 usb_log_debug2("%s Port status(%#x):%s%s%s%s%s%s%s%s%s%s%s.\n",
373 port->id_string, value,
374 (value & STATUS_SUSPEND) ? " SUSPENDED," : "",
375 (value & STATUS_RESUME) ? " IN RESUME," : "",
376 (value & STATUS_IN_RESET) ? " IN RESET," : "",
377 (value & STATUS_LINE_D_MINUS) ? " VD-," : "",
378 (value & STATUS_LINE_D_PLUS) ? " VD+," : "",
379 (value & STATUS_LOW_SPEED) ? " LOWSPEED," : "",
380 (value & STATUS_ENABLED_CHANGED) ? " ENABLED-CHANGE," : "",
381 (value & STATUS_ENABLED) ? " ENABLED," : "",
382 (value & STATUS_CONNECTED_CHANGED) ? " CONNECTED-CHANGE," : "",
383 (value & STATUS_CONNECTED) ? " CONNECTED," : "",
[f9c03b5]384 (value & STATUS_ALWAYS_ONE) ? " ALWAYS ONE" : " ERR: NO ALWAYS ONE"
[f123909]385 );
386}
[c56dbe0]387/**
388 * @}
389 */
Note: See TracBrowser for help on using the repository browser.