source: mainline/uspace/drv/bus/usb/uhcirh/port.c@ 0d103aef

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

uhcirh: make sure connection to hc is opoen during device removal.

Add error message and change error code to ENOTCONN if there is no connection to the hc.

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