port.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Jan Vesely
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00034 #include <libarch/ddi.h>  /* pio_read and pio_write */
00035 #include <fibril_synch.h> /* async_usleep */
00036 #include <errno.h>
00037 #include <str_error.h>
00038 #include <async.h>
00039 
00040 #include <usb/usb.h>    /* usb_address_t */
00041 #include <usb/dev/hub.h>    /* usb_hc_new_device_wrapper */
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         // TODO: Kill fibril here
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                 /* Read register value */
00156                 const port_status_t port_status =
00157                     uhci_port_read_status(instance);
00158 
00159                 /* Print the value if it's interesting */
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                 /* Remove any old device */
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                         /* New device */
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                         /* Write one to WC bits, to ack changes */
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          * Resets from root ports should be nominally 50ms (USB spec 7.1.7.3)
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         /* PIO delay, should not be longer than 3ms as the device might
00236          * enter suspend state. */
00237         udelay(10);
00238         /* Enable the port. */
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         /* Read register value */
00306         port_status_t port_status = uhci_port_read_status(port);
00307 
00308         /* Set enabled bit */
00309         if (enabled) {
00310                 port_status |= STATUS_ENABLED;
00311         } else {
00312                 port_status &= ~STATUS_ENABLED;
00313         }
00314 
00315         /* Write new value. */
00316         uhci_port_write_status(port, port_status);
00317 
00318         /* Wait for port to become enabled */
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 }

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7