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

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

Reduce wait times in uhci reset sequence (helps with bug #98)

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