00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00034 #include <libarch/ddi.h>
00035 #include <fibril_synch.h>
00036 #include <errno.h>
00037 #include <str_error.h>
00038 #include <async.h>
00039
00040 #include <usb/usb.h>
00041 #include <usb/dev/hub.h>
00042 #include <usb/debug.h>
00043
00044 #include "port.h"
00045
00046 static int uhci_port_check(void *port);
00047 static int uhci_port_reset_enable(int portno, void *arg);
00048 static int uhci_port_new_device(uhci_port_t *port, usb_speed_t speed);
00049 static int uhci_port_remove_device(uhci_port_t *port);
00050 static int uhci_port_set_enabled(uhci_port_t *port, bool enabled);
00051 static void uhci_port_print_status(
00052 uhci_port_t *port, const port_status_t value);
00053
00059 static inline port_status_t uhci_port_read_status(uhci_port_t *port)
00060 {
00061 assert(port);
00062 return pio_read_16(port->address);
00063 }
00064
00071 static inline void uhci_port_write_status(uhci_port_t *port, port_status_t val)
00072 {
00073 assert(port);
00074 pio_write_16(port->address, val);
00075 }
00076
00088 int uhci_port_init(uhci_port_t *port,
00089 port_status_t *address, unsigned number, unsigned usec, ddf_dev_t *rh)
00090 {
00091 assert(port);
00092 char *id_string;
00093 asprintf(&id_string, "Port (%p - %u)", port, number);
00094 if (id_string == NULL) {
00095 return ENOMEM;
00096 }
00097
00098 port->id_string = id_string;
00099 port->address = address;
00100 port->number = number;
00101 port->wait_period_usec = usec;
00102 port->attached_device = 0;
00103 port->rh = rh;
00104
00105 int ret =
00106 usb_hc_connection_initialize_from_device(&port->hc_connection, rh);
00107 if (ret != EOK) {
00108 usb_log_error("%s: failed to initialize connection to HC.",
00109 port->id_string);
00110 free(id_string);
00111 return ret;
00112 }
00113
00114 port->checker = fibril_create(uhci_port_check, port);
00115 if (port->checker == 0) {
00116 usb_log_error("%s: failed to create polling fibril.",
00117 port->id_string);
00118 free(id_string);
00119 return ENOMEM;
00120 }
00121
00122 fibril_add_ready(port->checker);
00123 usb_log_debug("%s: Started polling fibril (%" PRIun ").\n",
00124 port->id_string, port->checker);
00125 return EOK;
00126 }
00127
00134 void uhci_port_fini(uhci_port_t *port)
00135 {
00136 assert(port);
00137 free(port->id_string);
00138
00139 return;
00140 }
00141
00147 int uhci_port_check(void *port)
00148 {
00149 uhci_port_t *instance = port;
00150 assert(instance);
00151
00152 while (1) {
00153 async_usleep(instance->wait_period_usec);
00154
00155
00156 const port_status_t port_status =
00157 uhci_port_read_status(instance);
00158
00159
00160 if (port_status & ~STATUS_ALWAYS_ONE)
00161 uhci_port_print_status(instance, port_status);
00162
00163 if ((port_status & STATUS_CONNECTED_CHANGED) == 0)
00164 continue;
00165
00166 usb_log_debug("%s: Connected change detected: %x.\n",
00167 instance->id_string, port_status);
00168
00169
00170 if (instance->attached_device) {
00171 usb_log_debug2("%s: Removing device.\n",
00172 instance->id_string);
00173 uhci_port_remove_device(instance);
00174 }
00175
00176 int ret =
00177 usb_hc_connection_open(&instance->hc_connection);
00178 if (ret != EOK) {
00179 usb_log_error("%s: Failed to connect to HC.",
00180 instance->id_string);
00181 continue;
00182 }
00183
00184 if ((port_status & STATUS_CONNECTED) != 0) {
00185
00186 const usb_speed_t speed =
00187 ((port_status & STATUS_LOW_SPEED) != 0) ?
00188 USB_SPEED_LOW : USB_SPEED_FULL;
00189 uhci_port_new_device(instance, speed);
00190 } else {
00191
00192 uhci_port_write_status(instance, port_status);
00193 usb_log_debug("%s: status change ACK.\n",
00194 instance->id_string);
00195 }
00196
00197 ret = usb_hc_connection_close(&instance->hc_connection);
00198 if (ret != EOK) {
00199 usb_log_error("%s: Failed to disconnect.",
00200 instance->id_string);
00201 }
00202 }
00203 return EOK;
00204 }
00205
00214 int uhci_port_reset_enable(int portno, void *arg)
00215 {
00216 uhci_port_t *port = arg;
00217 assert(port);
00218
00219 usb_log_debug2("%s: new_device_enable_port.\n", port->id_string);
00220
00221
00222
00223
00224 {
00225 usb_log_debug("%s: Reset Signal start.\n", port->id_string);
00226 port_status_t port_status = uhci_port_read_status(port);
00227 port_status |= STATUS_IN_RESET;
00228 uhci_port_write_status(port, port_status);
00229 async_usleep(50000);
00230 port_status = uhci_port_read_status(port);
00231 port_status &= ~STATUS_IN_RESET;
00232 uhci_port_write_status(port, port_status);
00233 while (uhci_port_read_status(port) & STATUS_IN_RESET);
00234 }
00235
00236
00237 udelay(10);
00238
00239 uhci_port_set_enabled(port, true);
00240 return EOK;
00241 }
00242
00251 int uhci_port_new_device(uhci_port_t *port, usb_speed_t speed)
00252 {
00253 assert(port);
00254 assert(usb_hc_connection_is_opened(&port->hc_connection));
00255
00256 usb_log_debug("%s: Detected new device.\n", port->id_string);
00257
00258 int ret, count = 0;
00259 usb_address_t dev_addr;
00260 do {
00261 ret = usb_hc_new_device_wrapper(port->rh, &port->hc_connection,
00262 speed, uhci_port_reset_enable, port->number, port,
00263 &dev_addr, &port->attached_device, NULL, NULL, NULL);
00264 } while (ret != EOK && ++count < 4);
00265
00266 if (ret != EOK) {
00267 usb_log_error("%s: Failed(%d) to add device: %s.\n",
00268 port->id_string, ret, str_error(ret));
00269 uhci_port_set_enabled(port, false);
00270 return ret;
00271 }
00272
00273 usb_log_info("New device at port %u, address %d (handle %" PRIun ").\n",
00274 port->number, dev_addr, port->attached_device);
00275 return EOK;
00276 }
00277
00287 int uhci_port_remove_device(uhci_port_t *port)
00288 {
00289 usb_log_error("%s: Don't know how to remove device %" PRIun ".\n",
00290 port->id_string, port->attached_device);
00291 port->attached_device = 0;
00292 return ENOTSUP;
00293 }
00294
00301 int uhci_port_set_enabled(uhci_port_t *port, bool enabled)
00302 {
00303 assert(port);
00304
00305
00306 port_status_t port_status = uhci_port_read_status(port);
00307
00308
00309 if (enabled) {
00310 port_status |= STATUS_ENABLED;
00311 } else {
00312 port_status &= ~STATUS_ENABLED;
00313 }
00314
00315
00316 uhci_port_write_status(port, port_status);
00317
00318
00319 do {
00320 port_status = uhci_port_read_status(port);
00321 } while ((port_status & STATUS_CONNECTED) &&
00322 !(port_status & STATUS_ENABLED));
00323
00324 usb_log_debug("%s: %sabled port.\n",
00325 port->id_string, enabled ? "En" : "Dis");
00326 return EOK;
00327 }
00328
00335 void uhci_port_print_status(uhci_port_t *port, const port_status_t value)
00336 {
00337 assert(port);
00338 usb_log_debug2("%s Port status(%#x):%s%s%s%s%s%s%s%s%s%s%s.\n",
00339 port->id_string, value,
00340 (value & STATUS_SUSPEND) ? " SUSPENDED," : "",
00341 (value & STATUS_RESUME) ? " IN RESUME," : "",
00342 (value & STATUS_IN_RESET) ? " IN RESET," : "",
00343 (value & STATUS_LINE_D_MINUS) ? " VD-," : "",
00344 (value & STATUS_LINE_D_PLUS) ? " VD+," : "",
00345 (value & STATUS_LOW_SPEED) ? " LOWSPEED," : "",
00346 (value & STATUS_ENABLED_CHANGED) ? " ENABLED-CHANGE," : "",
00347 (value & STATUS_ENABLED) ? " ENABLED," : "",
00348 (value & STATUS_CONNECTED_CHANGED) ? " CONNECTED-CHANGE," : "",
00349 (value & STATUS_CONNECTED) ? " CONNECTED," : "",
00350 (value & STATUS_ALWAYS_ONE) ? " ALWAYS ONE" : " ERR: NO ALWAYS ONE"
00351 );
00352 }