Ignore:
File:
1 edited

Legend:

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

    r9d58539 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 
    102         int opResult = usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe);
    103         if (opResult != EOK) {
    104                 usb_log_error("Failed to start long ctrl pipe transfer: %s\n",
    105                     str_error(opResult));
    106                 return opResult;
    107         }
    108 
    109         /* Set hub's first configuration. (There should be only one) */
    110         opResult = usb_set_first_configuration(usb_dev);
    111         if (opResult != EOK) {
    112                 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    113                 usb_log_error("Could not set hub configuration: %s\n",
    114                     str_error(opResult));
    115                 return opResult;
    116         }
    117 
    118         /* Get port count and create attached_devices. */
    119         opResult = usb_hub_process_hub_specific_info(hub_dev);
    120         if (opResult != EOK) {
    121                 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    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_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    133                 usb_log_error("Failed to create hub function.\n");
    134                 return ENOMEM;
    135         }
    136 
    137         /* Bind hub control function. */
    138         opResult = ddf_fun_bind(hub_dev->hub_fun);
    139         if (opResult != EOK) {
    140                 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    141                 usb_log_error("Failed to bind hub function: %s.\n",
    142                    str_error(opResult));
    143                 ddf_fun_destroy(hub_dev->hub_fun);
    144                 return opResult;
    145         }
    146 
    147         /* Start hub operation. */
    148         opResult = usb_device_auto_poll(hub_dev->usb_device, 0,
    149             hub_port_changes_callback, ((hub_dev->port_count + 1 + 8) / 8),
    150             usb_hub_polling_terminated_callback, hub_dev);
    151         if (opResult != EOK) {
    152                 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    153                 /* Function is already bound */
    154                 ddf_fun_unbind(hub_dev->hub_fun);
    155                 ddf_fun_destroy(hub_dev->hub_fun);
    156                 usb_log_error("Failed to create polling fibril: %s.\n",
    157                     str_error(opResult));
    158                 return opResult;
    159         }
    160         hub_dev->running = true;
    161         usb_log_info("Controlling hub '%s' (%zu ports).\n",
    162             hub_dev->usb_device->ddf_dev->name, hub_dev->port_count);
    163 
    164         usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
    165         return EOK;
    166 }
    167 /*----------------------------------------------------------------------------*/
    168 /**
    169  * Turn off power to all ports.
    170  *
    171  * @param usb_dev generic usb device information
    172  * @return error code
    173  */
    174 int usb_hub_device_remove(usb_device_t *usb_dev)
    175 {
    176         return ENOTSUP;
    177 }
    178 /*----------------------------------------------------------------------------*/
    179 /**
    180  * Remove all attached devices
    18182 * @param usb_dev generic usb device information
    18283 * @return error code
     
    220121}
    221122/*----------------------------------------------------------------------------*/
     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/*----------------------------------------------------------------------------*/
    222207/** Callback for polling hub for changes.
    223208 *
     
    258243/*----------------------------------------------------------------------------*/
    259244/**
     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/**
    260271 * Load hub-specific information into hub_dev structure and process if needed
    261272 *
     
    300311        }
    301312
    302         hub_dev->power_switched =
     313        const bool is_power_switched =
    303314            !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG);
    304         hub_dev->per_port_power =
    305             descriptor.characteristics & HUB_CHAR_POWER_PER_PORT_FLAG;
    306 
    307         if (!hub_dev->power_switched) {
    308                 usb_log_info(
    309                    "Power switching not supported, ports always powered.\n");
    310                 return EOK;
    311         }
    312 
    313         usb_log_info("Hub port power switching enabled.\n");
    314 
    315         for (size_t port = 0; port < hub_dev->port_count; ++port) {
    316                 usb_log_debug("Powering port %zu.\n", port);
    317                 const int ret = usb_hub_port_set_feature(
    318                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    319 
    320                 if (ret != EOK) {
    321                         usb_log_error("Cannot power on port %zu: %s.\n",
    322                             hub_dev->ports[port].port_number, str_error(ret));
    323                 } else {
    324                         if (!hub_dev->per_port_power) {
    325                                 usb_log_debug("Ganged power switching, "
    326                                     "one port is enough.\n");
    327                                 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                                }
    328334                        }
    329335                }
     336        } else {
     337                usb_log_debug("Power not switched, ports always powered\n");
    330338        }
    331339        return EOK;
     
    394402                usb_log_warning("Detected hub over-current condition, "
    395403                    "all ports should be powered off.");
    396                 return;
    397         }
    398 
    399         /* Ports are always powered. */
    400         if (!hub_dev->power_switched)
    401                 return;
    402 
    403         /* Over-current condition is gone, it is safe to turn the ports on. */
    404         for (size_t port = 0; port < hub_dev->port_count; ++port) {
    405                 const int ret = usb_hub_port_set_feature(
    406                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    407                 if (ret != EOK) {
    408                         usb_log_warning("HUB OVER-CURRENT GONE: Cannot power on"
    409                             " port %zu: %s\n", hub_dev->ports[port].port_number,
    410                             str_error(ret));
    411                 } else {
    412                         if (!hub_dev->per_port_power)
    413                                 return;
    414                 }
    415         }
    416 
     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        }
    417428}
    418429/*----------------------------------------------------------------------------*/
     
    450461        if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
    451462                usb_hub_over_current(hub_dev, status);
    452                 /* Ack change in hub OC flag */
    453                 const int ret = usb_request_clear_feature(
    454                     &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS,
    455                     USB_REQUEST_RECIPIENT_DEVICE,
    456                     USB_HUB_FEATURE_C_HUB_OVER_CURRENT, 0);
    457                 if (ret != EOK) {
    458                         usb_log_error("Failed to clear hub over-current "
    459                             "change flag: %s.\n", str_error(opResult));
    460                 }
    461463        }
    462464
     
    475477                 * Just ACK the change.
    476478                 */
    477                 const int ret = usb_request_clear_feature(
     479                const int opResult = usb_request_clear_feature(
    478480                    control_pipe, USB_REQUEST_TYPE_CLASS,
    479481                    USB_REQUEST_RECIPIENT_DEVICE,
    480482                    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    481483                if (opResult != EOK) {
    482                         usb_log_error("Failed to clear hub power change "
    483                             "flag: %s.\n", str_error(ret));
     484                        usb_log_error(
     485                            "Failed to clear hub power change flag: %s.\n",
     486                            str_error(opResult));
    484487                }
    485488        }
Note: See TracChangeset for help on using the changeset viewer.