Ignore:
File:
1 edited

Legend:

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

    r065064e6 r9d58539  
    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
     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 */
     174int usb_hub_device_remove(usb_device_t *usb_dev)
     175{
     176        return ENOTSUP;
     177}
     178/*----------------------------------------------------------------------------*/
     179/**
     180 * Remove all attached devices
    82181 * @param usb_dev generic usb device information
    83182 * @return error code
     
    121220}
    122221/*----------------------------------------------------------------------------*/
    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 /*----------------------------------------------------------------------------*/
    207222/** Callback for polling hub for changes.
    208223 *
     
    243258/*----------------------------------------------------------------------------*/
    244259/**
    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 /**
    271260 * Load hub-specific information into hub_dev structure and process if needed
    272261 *
     
    311300        }
    312301
    313         const bool is_power_switched =
     302        hub_dev->power_switched =
    314303            !(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                                 }
     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;
    334328                        }
    335329                }
    336         } else {
    337                 usb_log_debug("Power not switched, ports always powered\n");
    338330        }
    339331        return EOK;
     
    402394                usb_log_warning("Detected hub over-current condition, "
    403395                    "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         }
     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
    428417}
    429418/*----------------------------------------------------------------------------*/
     
    461450        if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
    462451                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                }
    463461        }
    464462
     
    477475                 * Just ACK the change.
    478476                 */
    479                 const int opResult = usb_request_clear_feature(
     477                const int ret = usb_request_clear_feature(
    480478                    control_pipe, USB_REQUEST_TYPE_CLASS,
    481479                    USB_REQUEST_RECIPIENT_DEVICE,
    482480                    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    483481                if (opResult != EOK) {
    484                         usb_log_error(
    485                             "Failed to clear hub power change flag: %s.\n",
    486                             str_error(opResult));
     482                        usb_log_error("Failed to clear hub power change "
     483                            "flag: %s.\n", str_error(ret));
    487484                }
    488485        }
Note: See TracChangeset for help on using the changeset viewer.