Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/usbhub/usbhub.c

    r54d1ad9 r065064e6  
    6868
    6969static int usb_set_first_configuration(usb_device_t *usb_device);
     70static usb_hub_dev_t * usb_hub_dev_create(usb_device_t *usb_dev);
    7071static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev);
    7172static void usb_hub_over_current(const usb_hub_dev_t *hub_dev,
     
    7475static void usb_hub_polling_terminated_callback(usb_device_t *device,
    7576    bool was_error, void *data);
    76 
    77 /**
    78  * Initialize hub device driver structure.
     77/**
     78 * Initialize hub device driver fibril
    7979 *
    8080 * Creates hub representation and fibril that periodically checks hub's status.
    8181 * Hub representation is passed to the fibril.
    82  * @param usb_dev generic usb device information
    83  * @return error code
    84  */
    85 int usb_hub_device_add(usb_device_t *usb_dev)
    86 {
    87         assert(usb_dev);
    88         /* Create driver soft-state structure */
    89         usb_hub_dev_t *hub_dev =
    90             usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t));
    91         if (hub_dev == NULL) {
    92                 usb_log_error("Failed to create hub driver structure.\n");
    93                 return ENOMEM;
    94         }
    95         hub_dev->usb_device = usb_dev;
    96         hub_dev->pending_ops_count = 0;
    97         hub_dev->running = false;
    98         fibril_mutex_initialize(&hub_dev->pending_ops_mutex);
    99         fibril_condvar_initialize(&hub_dev->pending_ops_cv);
    100 
    101         /* Create hc connection */
    102         usb_log_debug("Initializing USB wire abstraction.\n");
    103         int opResult = usb_hc_connection_initialize_from_device(
    104             &hub_dev->connection, hub_dev->usb_device->ddf_dev);
    105         if (opResult != EOK) {
    106                 usb_log_error("Could not initialize connection to device: %s\n",
    107                     str_error(opResult));
    108                 return opResult;
    109         }
    110 
    111         /* Set hub's first configuration. (There should be only one) */
    112         opResult = usb_set_first_configuration(usb_dev);
    113         if (opResult != EOK) {
    114                 usb_log_error("Could not set hub configuration: %s\n",
    115                     str_error(opResult));
    116                 return opResult;
    117         }
    118 
    119         /* Get port count and create attached_devices. */
    120         opResult = usb_hub_process_hub_specific_info(hub_dev);
    121         if (opResult != EOK) {
    122                 usb_log_error("Could process hub specific info, %s\n",
    123                     str_error(opResult));
    124                 return opResult;
    125         }
    126 
    127         /* Create hub control function. */
    128         usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.\n");
    129         hub_dev->hub_fun = ddf_fun_create(hub_dev->usb_device->ddf_dev,
    130             fun_exposed, HUB_FNC_NAME);
    131         if (hub_dev->hub_fun == NULL) {
    132                 usb_log_error("Failed to create hub function.\n");
    133                 return ENOMEM;
    134         }
    135 
    136         /* Bind hub control function. */
    137         opResult = ddf_fun_bind(hub_dev->hub_fun);
    138         if (opResult != EOK) {
    139                 usb_log_error("Failed to bind hub function: %s.\n",
    140                    str_error(opResult));
    141                 ddf_fun_destroy(hub_dev->hub_fun);
    142                 return opResult;
    143         }
    144 
    145         /* Start hub operation. */
    146         opResult = usb_device_auto_poll(hub_dev->usb_device, 0,
    147             hub_port_changes_callback, ((hub_dev->port_count + 1 + 8) / 8),
    148             usb_hub_polling_terminated_callback, hub_dev);
    149         if (opResult != EOK) {
    150                 /* Function is already bound */
    151                 ddf_fun_unbind(hub_dev->hub_fun);
    152                 ddf_fun_destroy(hub_dev->hub_fun);
    153                 usb_log_error("Failed to create polling fibril: %s.\n",
    154                     str_error(opResult));
    155                 return opResult;
    156         }
    157         hub_dev->running = true;
    158         usb_log_info("Controlling hub '%s' (%zu ports).\n",
    159             hub_dev->usb_device->ddf_dev->name, hub_dev->port_count);
    160 
    161         return EOK;
    162 }
    163 /*----------------------------------------------------------------------------*/
    164 /**
    165  * Turn off power to all ports.
    166  *
    167  * @param usb_dev generic usb device information
    168  * @return error code
    169  */
    170 int usb_hub_device_remove(usb_device_t *usb_dev)
    171 {
    172         assert(usb_dev);
    173         usb_hub_dev_t *hub_dev = usb_dev->driver_data;
    174         assert(hub_dev);
    175         //TODO: Cascade the call here.
    176         //TODO: Enable after cascading is implemented.
    177         return ENOTSUP;
    178         if (!hub_dev->power_switched) {
    179                 /* That is all we can do. */
    180                 return EOK;
    181         }
    182         int ret = EOK;
    183         usb_log_info("Hub is about to be removed, powering down all ports.\n");
    184         for (size_t port = 0; port < hub_dev->port_count; ++port) {
    185                 usb_log_debug("Powering down port %zu.\n", port);
    186                 int pret = usb_hub_port_clear_feature(
    187                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    188                 if (pret != EOK) {
    189                         usb_log_error("Cannot power down port %zu: %s.\n",
    190                             hub_dev->ports[port].port_number, str_error(pret));
    191                         ret = pret;
    192                 } else {
    193                         if (!hub_dev->per_port_power) {
    194                                 usb_log_debug("Ganged power switching mode, "
    195                                    "one port is enough.\n");
    196                                 break;
    197                         }
    198                 }
    199         }
    200         return ret;
    201 }
    202 /*----------------------------------------------------------------------------*/
    203 /**
    204  * Remove all attached devices
    20582 * @param usb_dev generic usb device information
    20683 * @return error code
     
    244121}
    245122/*----------------------------------------------------------------------------*/
     123/**
     124 * Initialize hub device driver fibril
     125 *
     126 * Creates hub representation and fibril that periodically checks hub's status.
     127 * Hub representation is passed to the fibril.
     128 * @param usb_dev generic usb device information
     129 * @return error code
     130 */
     131int usb_hub_device_add(usb_device_t *usb_dev)
     132{
     133        assert(usb_dev);
     134        /* Create driver soft-state structure */
     135        usb_hub_dev_t *hub_dev = usb_hub_dev_create(usb_dev);
     136        if (hub_dev == NULL) {
     137                usb_log_error("Failed to create hun driver structure.\n");
     138                return ENOMEM;
     139        }
     140
     141        /* Create hc connection */
     142        usb_log_debug("Initializing USB wire abstraction.\n");
     143        int opResult = usb_hc_connection_initialize_from_device(
     144            &hub_dev->connection, hub_dev->usb_device->ddf_dev);
     145        if (opResult != EOK) {
     146                usb_log_error("Could not initialize connection to device: %s\n",
     147                    str_error(opResult));
     148                free(hub_dev);
     149                return opResult;
     150        }
     151
     152        /* Set hub's first configuration. (There should be only one) */
     153        opResult = usb_set_first_configuration(usb_dev);
     154        if (opResult != EOK) {
     155                usb_log_error("Could not set hub configuration: %s\n",
     156                    str_error(opResult));
     157                free(hub_dev);
     158                return opResult;
     159        }
     160
     161        /* Get port count and create attached_devices. */
     162        opResult = usb_hub_process_hub_specific_info(hub_dev);
     163        if (opResult != EOK) {
     164                usb_log_error("Could process hub specific info, %s\n",
     165                    str_error(opResult));
     166                free(hub_dev);
     167                return opResult;
     168        }
     169
     170        usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.\n");
     171        hub_dev->hub_fun = ddf_fun_create(hub_dev->usb_device->ddf_dev,
     172            fun_exposed, HUB_FNC_NAME);
     173        if (hub_dev->hub_fun == NULL) {
     174                usb_log_error("Failed to create hub function.\n");
     175                free(hub_dev);
     176                return ENOMEM;
     177        }
     178
     179        opResult = ddf_fun_bind(hub_dev->hub_fun);
     180        if (opResult != EOK) {
     181                usb_log_error("Failed to bind hub function: %s.\n",
     182                   str_error(opResult));
     183                free(hub_dev);
     184                ddf_fun_destroy(hub_dev->hub_fun);
     185                return opResult;
     186        }
     187
     188        opResult = usb_device_auto_poll(hub_dev->usb_device, 0,
     189            hub_port_changes_callback, ((hub_dev->port_count + 1 + 8) / 8),
     190            usb_hub_polling_terminated_callback, hub_dev);
     191        if (opResult != EOK) {
     192                /* Function is already bound */
     193                ddf_fun_unbind(hub_dev->hub_fun);
     194                ddf_fun_destroy(hub_dev->hub_fun);
     195                free(hub_dev);
     196                usb_log_error("Failed to create polling fibril: %s.\n",
     197                    str_error(opResult));
     198                return opResult;
     199        }
     200        hub_dev->running = true;
     201        usb_log_info("Controlling hub '%s' (%zu ports).\n",
     202            hub_dev->usb_device->ddf_dev->name, hub_dev->port_count);
     203
     204        return EOK;
     205}
     206/*----------------------------------------------------------------------------*/
    246207/** Callback for polling hub for changes.
    247208 *
     
    282243/*----------------------------------------------------------------------------*/
    283244/**
     245 * create usb_hub_dev_t structure
     246 *
     247 * Does only basic copying of known information into new structure.
     248 * @param usb_dev usb device structure
     249 * @return basic usb_hub_dev_t structure
     250 */
     251static usb_hub_dev_t * usb_hub_dev_create(usb_device_t *usb_dev)
     252{
     253        assert(usb_dev);
     254        usb_hub_dev_t *hub_dev =
     255            usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t));
     256        if (!hub_dev)
     257            return NULL;
     258
     259        hub_dev->usb_device = usb_dev;
     260        hub_dev->ports = NULL;
     261        hub_dev->port_count = 0;
     262        hub_dev->pending_ops_count = 0;
     263        hub_dev->running = false;
     264        fibril_mutex_initialize(&hub_dev->pending_ops_mutex);
     265        fibril_condvar_initialize(&hub_dev->pending_ops_cv);
     266
     267        return hub_dev;
     268}
     269/*----------------------------------------------------------------------------*/
     270/**
    284271 * Load hub-specific information into hub_dev structure and process if needed
    285272 *
     
    324311        }
    325312
    326         hub_dev->power_switched =
     313        const bool is_power_switched =
    327314            !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG);
    328         hub_dev->per_port_power =
    329             descriptor.characteristics & HUB_CHAR_POWER_PER_PORT_FLAG;
    330 
    331         if (!hub_dev->power_switched) {
    332                 usb_log_info(
    333                    "Power switching not supported, ports always powered.\n");
    334                 return EOK;
    335         }
    336 
    337         usb_log_info("Hub port power switching enabled.\n");
    338 
    339         for (size_t port = 0; port < hub_dev->port_count; ++port) {
    340                 usb_log_debug("Powering port %zu.\n", port);
    341                 const int ret = usb_hub_port_set_feature(
    342                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    343 
    344                 if (ret != EOK) {
    345                         usb_log_error("Cannot power on port %zu: %s.\n",
    346                             hub_dev->ports[port].port_number, str_error(ret));
    347                 } else {
    348                         if (!hub_dev->per_port_power) {
    349                                 usb_log_debug("Ganged power switching, "
    350                                     "one port is enough.\n");
    351                                 break;
     315        if (is_power_switched) {
     316                usb_log_debug("Hub power switched\n");
     317                const bool per_port_power = descriptor.characteristics
     318                    & HUB_CHAR_POWER_PER_PORT_FLAG;
     319
     320                for (size_t port = 0; port < hub_dev->port_count; ++port) {
     321                        usb_log_debug("Powering port %zu.\n", port);
     322                        opResult = usb_hub_port_set_feature(
     323                            &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
     324                        if (opResult != EOK) {
     325                                usb_log_error("Cannot power on port %zu: %s.\n",
     326                                    port, str_error(opResult));
     327                        } else {
     328                                if (!per_port_power) {
     329                                        usb_log_debug(
     330                                            "Ganged power switching mode, "
     331                                            "one port is enough.\n");
     332                                        break;
     333                                }
    352334                        }
    353335                }
     336        } else {
     337                usb_log_debug("Power not switched, ports always powered\n");
    354338        }
    355339        return EOK;
     
    418402                usb_log_warning("Detected hub over-current condition, "
    419403                    "all ports should be powered off.");
    420                 return;
    421         }
    422 
    423         /* Ports are always powered. */
    424         if (!hub_dev->power_switched)
    425                 return;
    426 
    427         /* Over-current condition is gone, it is safe to turn the ports on. */
    428         for (size_t port = 0; port < hub_dev->port_count; ++port) {
    429                 const int ret = usb_hub_port_set_feature(
    430                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    431                 if (ret != EOK) {
    432                         usb_log_warning("HUB OVER-CURRENT GONE: Cannot power on"
    433                             " port %zu: %s\n", hub_dev->ports[port].port_number,
    434                             str_error(ret));
    435                 } else {
    436                         if (!hub_dev->per_port_power)
    437                                 return;
    438                 }
    439         }
    440 
     404        } else {
     405                /* Over-current condition is gone, it is safe to turn the
     406                 * ports on. */
     407                for (size_t port = 0; port < hub_dev->port_count; ++port) {
     408                        const int opResult = usb_hub_port_set_feature(
     409                            &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
     410                        // TODO: consider power policy here
     411                        if (opResult != EOK) {
     412                                usb_log_warning(
     413                                    "HUB OVER-CURRENT GONE: Cannot power on "
     414                                    "port %zu;  %s\n",
     415                                    port, str_error(opResult));
     416                        }
     417                }
     418        }
     419        const int opResult = usb_request_clear_feature(
     420            &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS,
     421            USB_REQUEST_RECIPIENT_DEVICE,
     422            USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
     423        if (opResult != EOK) {
     424                usb_log_error(
     425                    "Failed to clear hub over-current change flag: %s.\n",
     426                    str_error(opResult));
     427        }
    441428}
    442429/*----------------------------------------------------------------------------*/
     
    474461        if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
    475462                usb_hub_over_current(hub_dev, status);
    476                 /* Ack change in hub OC flag */
    477                 const int ret = usb_request_clear_feature(
    478                     &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS,
    479                     USB_REQUEST_RECIPIENT_DEVICE,
    480                     USB_HUB_FEATURE_C_HUB_OVER_CURRENT, 0);
    481                 if (ret != EOK) {
    482                         usb_log_error("Failed to clear hub over-current "
    483                             "change flag: %s.\n", str_error(opResult));
    484                 }
    485463        }
    486464
     
    499477                 * Just ACK the change.
    500478                 */
    501                 const int ret = usb_request_clear_feature(
     479                const int opResult = usb_request_clear_feature(
    502480                    control_pipe, USB_REQUEST_TYPE_CLASS,
    503481                    USB_REQUEST_RECIPIENT_DEVICE,
    504482                    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    505483                if (opResult != EOK) {
    506                         usb_log_error("Failed to clear hub power change "
    507                             "flag: %s.\n", str_error(ret));
     484                        usb_log_error(
     485                            "Failed to clear hub power change flag: %s.\n",
     486                            str_error(opResult));
    508487                }
    509488        }
Note: See TracChangeset for help on using the changeset viewer.