usbhub.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 Matus Dekanek
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  */
00035 #include <ddf/driver.h>
00036 #include <bool.h>
00037 #include <errno.h>
00038 #include <str_error.h>
00039 #include <inttypes.h>
00040 
00041 #include <usb_iface.h>
00042 #include <usb/ddfiface.h>
00043 #include <usb/descriptor.h>
00044 #include <usb/dev/recognise.h>
00045 #include <usb/dev/request.h>
00046 #include <usb/classes/hub.h>
00047 #include <usb/dev/poll.h>
00048 #include <stdio.h>
00049 
00050 #include "usbhub.h"
00051 #include "usbhub_private.h"
00052 #include "port_status.h"
00053 #include <usb/usb.h>
00054 #include <usb/dev/pipes.h>
00055 #include <usb/classes/classes.h>
00056 
00057 
00058 static usb_hub_info_t * usb_hub_info_create(usb_device_t *usb_dev);
00059 
00060 static int usb_hub_process_hub_specific_info(usb_hub_info_t *hub_info);
00061 
00062 static int usb_hub_set_configuration(usb_hub_info_t *hub_info);
00063 
00064 static int usb_hub_start_hub_fibril(usb_hub_info_t *hub_info);
00065 
00066 static int usb_process_hub_over_current(usb_hub_info_t *hub_info,
00067     usb_hub_status_t status);
00068 
00069 static int usb_process_hub_local_power_change(usb_hub_info_t *hub_info,
00070     usb_hub_status_t status);
00071 
00072 static void usb_hub_process_global_interrupt(usb_hub_info_t *hub_info);
00073 
00074 static void usb_hub_polling_terminated_callback(usb_device_t *device,
00075     bool was_error, void *data);
00076 
00077 
00078 //*********************************************
00079 //
00080 //  hub driver code, initialization
00081 //
00082 //*********************************************
00083 
00092 int usb_hub_add_device(usb_device_t *usb_dev) {
00093         if (!usb_dev) return EINVAL;
00094         usb_hub_info_t *hub_info = usb_hub_info_create(usb_dev);
00095         //create hc connection
00096         usb_log_debug("Initializing USB wire abstraction.\n");
00097         int opResult = usb_hc_connection_initialize_from_device(
00098             &hub_info->connection,
00099             hub_info->usb_device->ddf_dev);
00100         if (opResult != EOK) {
00101                 usb_log_error("Could not initialize connection to device, "
00102                     " %s\n",
00103                     str_error(opResult));
00104                 free(hub_info);
00105                 return opResult;
00106         }
00107 
00108         //set hub configuration
00109         opResult = usb_hub_set_configuration(hub_info);
00110         if (opResult != EOK) {
00111                 usb_log_error("Could not set hub configuration, %s\n",
00112                     str_error(opResult));
00113                 free(hub_info);
00114                 return opResult;
00115         }
00116         //get port count and create attached_devs
00117         opResult = usb_hub_process_hub_specific_info(hub_info);
00118         if (opResult != EOK) {
00119                 usb_log_error("Could process hub specific info, %s\n",
00120                     str_error(opResult));
00121                 free(hub_info);
00122                 return opResult;
00123         }
00124 
00125         usb_log_debug("Creating 'hub' function in DDF.\n");
00126         ddf_fun_t *hub_fun = ddf_fun_create(hub_info->usb_device->ddf_dev,
00127             fun_exposed, "hub");
00128         assert(hub_fun != NULL);
00129         hub_fun->ops = NULL;
00130 
00131         opResult = ddf_fun_bind(hub_fun);
00132         assert(opResult == EOK);
00133         opResult = ddf_fun_add_to_class(hub_fun, "hub");
00134         assert(opResult == EOK);
00135 
00136         opResult = usb_hub_start_hub_fibril(hub_info);
00137         if (opResult != EOK)
00138                 free(hub_info);
00139         return opResult;
00140 }
00141 
00150 bool hub_port_changes_callback(usb_device_t *dev,
00151     uint8_t *change_bitmap, size_t change_bitmap_size, void *arg) {
00152         usb_log_debug("hub_port_changes_callback\n");
00153         usb_hub_info_t *hub = (usb_hub_info_t *) arg;
00154 
00155         /* FIXME: check that we received enough bytes. */
00156         if (change_bitmap_size == 0) {
00157                 goto leave;
00158         }
00159 
00160         bool change;
00161         change = ((uint8_t*) change_bitmap)[0] & 1;
00162         if (change) {
00163                 usb_hub_process_global_interrupt(hub);
00164         }
00165 
00166         size_t port;
00167         for (port = 1; port < hub->port_count + 1; port++) {
00168                 bool change = (change_bitmap[port / 8] >> (port % 8)) % 2;
00169                 if (change) {
00170                         usb_hub_process_port_interrupt(hub, port);
00171                 }
00172         }
00173 leave:
00174         /* FIXME: proper interval. */
00175         async_usleep(1000 * 250);
00176 
00177         return true;
00178 }
00179 
00180 
00181 //*********************************************
00182 //
00183 //  support functions
00184 //
00185 //*********************************************
00186 
00194 static usb_hub_info_t * usb_hub_info_create(usb_device_t *usb_dev) {
00195         usb_hub_info_t * result = malloc(sizeof (usb_hub_info_t));
00196         if (!result) return NULL;
00197         result->usb_device = usb_dev;
00198         result->status_change_pipe = usb_dev->pipes[0].pipe;
00199         result->control_pipe = &usb_dev->ctrl_pipe;
00200         result->is_default_address_used = false;
00201 
00202         result->ports = NULL;
00203         result->port_count = (size_t) - 1;
00204         fibril_mutex_initialize(&result->port_mutex);
00205 
00206         fibril_mutex_initialize(&result->pending_ops_mutex);
00207         fibril_condvar_initialize(&result->pending_ops_cv);
00208         result->pending_ops_count = 0;
00209         return result;
00210 }
00211 
00222 static int usb_hub_process_hub_specific_info(usb_hub_info_t *hub_info) {
00223         // get hub descriptor
00224         usb_log_debug("Creating serialized descriptor\n");
00225         uint8_t serialized_descriptor[USB_HUB_MAX_DESCRIPTOR_SIZE];
00226         usb_hub_descriptor_t * descriptor;
00227         int opResult;
00228 
00229         size_t received_size;
00230         opResult = usb_request_get_descriptor(hub_info->control_pipe,
00231             USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE,
00232             USB_DESCTYPE_HUB, 0, 0, serialized_descriptor,
00233             USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size);
00234 
00235         if (opResult != EOK) {
00236                 usb_log_error("Failed when receiving hub descriptor, "
00237                     "%s\n",
00238                     str_error(opResult));
00239                 free(serialized_descriptor);
00240                 return opResult;
00241         }
00242         usb_log_debug2("Deserializing descriptor\n");
00243         descriptor = usb_create_deserialized_hub_desriptor(
00244             serialized_descriptor);
00245         if (descriptor == NULL) {
00246                 usb_log_warning("could not deserialize descriptor \n");
00247                 return ENOMEM;
00248         }
00249         usb_log_debug("setting port count to %d\n", descriptor->ports_count);
00250         hub_info->port_count = descriptor->ports_count;
00251         bool is_power_switched =
00252             ((descriptor->hub_characteristics & 1) == 0);
00253         bool has_individual_port_powering =
00254             ((descriptor->hub_characteristics & 1) != 0);
00255         hub_info->ports = malloc(
00256             sizeof (usb_hub_port_t) * (hub_info->port_count + 1));
00257         if (!hub_info->ports) {
00258                 return ENOMEM;
00259         }
00260         size_t port;
00261         for (port = 0; port < hub_info->port_count + 1; ++port) {
00262                 usb_hub_port_init(&hub_info->ports[port]);
00263         }
00264         if (is_power_switched) {
00265                 usb_log_debug("Hub power switched\n");
00266 
00267                 if (!has_individual_port_powering) {
00268                         //this setting actually makes no difference
00269                         usb_log_debug("Hub has global powering\n");
00270                 }
00271 
00272                 for (port = 1; port <= hub_info->port_count; ++port) {
00273                         usb_log_debug("Powering port %zu.\n", port);
00274                         opResult = usb_hub_set_port_feature(hub_info->control_pipe,
00275                             port, USB_HUB_FEATURE_PORT_POWER);
00276                         if (opResult != EOK) {
00277                                 usb_log_error("Cannot power on port %zu: %s.\n",
00278                                     port, str_error(opResult));
00279                         }
00280                 }
00281 
00282         } else {
00283                 usb_log_debug("Power not switched, not going to be powered\n");
00284         }
00285         usb_log_debug2("Freeing data\n");
00286         free(descriptor);
00287         return EOK;
00288 }
00289 
00298 static int usb_hub_set_configuration(usb_hub_info_t *hub_info) {
00299         //device descriptor
00300         usb_standard_device_descriptor_t *std_descriptor
00301             = &hub_info->usb_device->descriptors.device;
00302         usb_log_debug("Hub has %d configurations\n",
00303             std_descriptor->configuration_count);
00304         if (std_descriptor->configuration_count < 1) {
00305                 usb_log_error("There are no configurations available\n");
00306                 return EINVAL;
00307         }
00308 
00309         usb_standard_configuration_descriptor_t *config_descriptor
00310             = (usb_standard_configuration_descriptor_t *)
00311             hub_info->usb_device->descriptors.configuration;
00312 
00313         /* Set configuration. */
00314         int opResult = usb_request_set_configuration(
00315             &hub_info->usb_device->ctrl_pipe,
00316             config_descriptor->configuration_number);
00317 
00318         if (opResult != EOK) {
00319                 usb_log_error("Failed to set hub configuration: %s.\n",
00320                     str_error(opResult));
00321                 return opResult;
00322         }
00323         usb_log_debug("\tUsed configuration %d\n",
00324             config_descriptor->configuration_number);
00325 
00326         return EOK;
00327 }
00328 
00338 static int usb_hub_start_hub_fibril(usb_hub_info_t *hub_info) {
00339         int rc;
00340 
00341         rc = usb_device_auto_poll(hub_info->usb_device, 0,
00342             hub_port_changes_callback, ((hub_info->port_count + 1) / 8) + 1,
00343             usb_hub_polling_terminated_callback, hub_info);
00344         if (rc != EOK) {
00345                 usb_log_error("Failed to create polling fibril: %s.\n",
00346                     str_error(rc));
00347                 free(hub_info);
00348                 return rc;
00349         }
00350 
00351         usb_log_info("Controlling hub `%s' (%zu ports).\n",
00352             hub_info->usb_device->ddf_dev->name, hub_info->port_count);
00353         return EOK;
00354 }
00355 
00356 //*********************************************
00357 //
00358 //  change handling functions
00359 //
00360 //*********************************************
00361 
00370 static int usb_process_hub_over_current(usb_hub_info_t *hub_info,
00371     usb_hub_status_t status) {
00372         int opResult;
00373         if (usb_hub_is_status(status, USB_HUB_FEATURE_HUB_OVER_CURRENT)) {
00374                 //poweroff all ports
00375                 unsigned int port;
00376                 for (port = 1; port <= hub_info->port_count; ++port) {
00377                         opResult = usb_hub_clear_port_feature(
00378                             hub_info->control_pipe, port,
00379                             USB_HUB_FEATURE_PORT_POWER);
00380                         if (opResult != EOK) {
00381                                 usb_log_warning(
00382                                     "Cannot power off port %d;  %s\n",
00383                                     port, str_error(opResult));
00384                         }
00385                 }
00386         } else {
00387                 //power all ports
00388                 unsigned int port;
00389                 for (port = 1; port <= hub_info->port_count; ++port) {
00390                         opResult = usb_hub_set_port_feature(
00391                             hub_info->control_pipe, port,
00392                             USB_HUB_FEATURE_PORT_POWER);
00393                         if (opResult != EOK) {
00394                                 usb_log_warning(
00395                                     "Cannot power off port %d;  %s\n",
00396                                     port, str_error(opResult));
00397                         }
00398                 }
00399         }
00400         return opResult;
00401 }
00402 
00411 static int usb_process_hub_local_power_change(usb_hub_info_t *hub_info,
00412     usb_hub_status_t status) {
00413         int opResult = EOK;
00414         opResult = usb_hub_clear_feature(hub_info->control_pipe,
00415             USB_HUB_FEATURE_C_HUB_LOCAL_POWER);
00416         if (opResult != EOK) {
00417                 usb_log_error("Cannnot clear hub power change flag: "
00418                     "%s\n",
00419                     str_error(opResult));
00420         }
00421         return opResult;
00422 }
00423 
00431 static void usb_hub_process_global_interrupt(usb_hub_info_t *hub_info) {
00432         usb_log_debug("Global interrupt on a hub\n");
00433         usb_pipe_t *pipe = hub_info->control_pipe;
00434         int opResult;
00435 
00436         usb_port_status_t status;
00437         size_t rcvd_size;
00438         usb_device_request_setup_packet_t request;
00439         //int opResult;
00440         usb_hub_set_hub_status_request(&request);
00441         //endpoint 0
00442 
00443         opResult = usb_pipe_control_read(
00444             pipe,
00445             &request, sizeof (usb_device_request_setup_packet_t),
00446             &status, 4, &rcvd_size
00447             );
00448         if (opResult != EOK) {
00449                 usb_log_error("Could not get hub status: %s\n",
00450                     str_error(opResult));
00451                 return;
00452         }
00453         if (rcvd_size != sizeof (usb_port_status_t)) {
00454                 usb_log_error("Received status has incorrect size\n");
00455                 return;
00456         }
00457         //port reset
00458         if (
00459             usb_hub_is_status(status, 16 + USB_HUB_FEATURE_C_HUB_OVER_CURRENT)) {
00460                 usb_process_hub_over_current(hub_info, status);
00461         }
00462         if (
00463             usb_hub_is_status(status, 16 + USB_HUB_FEATURE_C_HUB_LOCAL_POWER)) {
00464                 usb_process_hub_local_power_change(hub_info, status);
00465         }
00466 }
00467 
00476 static void usb_hub_polling_terminated_callback(usb_device_t *device,
00477     bool was_error, void *data) {
00478         usb_hub_info_t * hub = data;
00479         assert(hub);
00480 
00481         fibril_mutex_lock(&hub->pending_ops_mutex);
00482 
00483         /* The device is dead. However there might be some pending operations
00484          * that we need to wait for.
00485          * One of them is device adding in progress.
00486          * The respective fibril is probably waiting for status change
00487          * in port reset (port enable) callback.
00488          * Such change would never come (otherwise we would not be here).
00489          * Thus, we would flush all pending port resets.
00490          */
00491         if (hub->pending_ops_count > 0) {
00492                 fibril_mutex_lock(&hub->port_mutex);
00493                 size_t port;
00494                 for (port = 0; port < hub->port_count; port++) {
00495                         usb_hub_port_t *the_port = hub->ports + port;
00496                         fibril_mutex_lock(&the_port->reset_mutex);
00497                         the_port->reset_completed = true;
00498                         the_port->reset_okay = false;
00499                         fibril_condvar_broadcast(&the_port->reset_cv);
00500                         fibril_mutex_unlock(&the_port->reset_mutex);
00501                 }
00502                 fibril_mutex_unlock(&hub->port_mutex);
00503         }
00504         /* And now wait for them. */
00505         while (hub->pending_ops_count > 0) {
00506                 fibril_condvar_wait(&hub->pending_ops_cv,
00507                     &hub->pending_ops_mutex);
00508         }
00509         fibril_mutex_unlock(&hub->pending_ops_mutex);
00510 
00511         usb_device_destroy(hub->usb_device);
00512 
00513         free(hub->ports);
00514         free(hub);
00515 }
00516 
00517 
00518 
00519 

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