Ignore:
File:
1 edited

Legend:

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

    r065064e6 r54d1ad9  
    6868
    6969static int usb_set_first_configuration(usb_device_t *usb_device);
    70 static usb_hub_dev_t * usb_hub_dev_create(usb_device_t *usb_dev);
    7170static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev);
    7271static void usb_hub_over_current(const usb_hub_dev_t *hub_dev,
     
    7574static void usb_hub_polling_terminated_callback(usb_device_t *device,
    7675    bool was_error, void *data);
    77 /**
    78  * Initialize hub device driver fibril
     76
     77/**
     78 * Initialize hub device driver structure.
    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 */
     85int 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 */
     170int 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
    82205 * @param usb_dev generic usb device information
    83206 * @return error code
     
    121244}
    122245/*----------------------------------------------------------------------------*/
    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  */
    131 int 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 /*----------------------------------------------------------------------------*/
    207246/** Callback for polling hub for changes.
    208247 *
     
    243282/*----------------------------------------------------------------------------*/
    244283/**
    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  */
    251 static 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 /**
    271284 * Load hub-specific information into hub_dev structure and process if needed
    272285 *
     
    311324        }
    312325
    313         const bool is_power_switched =
     326        hub_dev->power_switched =
    314327            !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG);
    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                                 }
     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;
    334352                        }
    335353                }
    336         } else {
    337                 usb_log_debug("Power not switched, ports always powered\n");
    338354        }
    339355        return EOK;
     
    402418                usb_log_warning("Detected hub over-current condition, "
    403419                    "all ports should be powered off.");
    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         }
     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
    428441}
    429442/*----------------------------------------------------------------------------*/
     
    461474        if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
    462475                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                }
    463485        }
    464486
     
    477499                 * Just ACK the change.
    478500                 */
    479                 const int opResult = usb_request_clear_feature(
     501                const int ret = usb_request_clear_feature(
    480502                    control_pipe, USB_REQUEST_TYPE_CLASS,
    481503                    USB_REQUEST_RECIPIENT_DEVICE,
    482504                    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    483505                if (opResult != EOK) {
    484                         usb_log_error(
    485                             "Failed to clear hub power change flag: %s.\n",
    486                             str_error(opResult));
     506                        usb_log_error("Failed to clear hub power change "
     507                            "flag: %s.\n", str_error(ret));
    487508                }
    488509        }
Note: See TracChangeset for help on using the changeset viewer.