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

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

Refactoring and dead code removal

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