source: mainline/uspace/drv/uhci-rhd/port.c@ 574f276

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

Further cleanup, includes, const, …

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