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

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

Const, type-casting and other minor fixes

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