Changeset 54d1ad9 in mainline


Ignore:
Timestamp:
2011-10-30T13:50:07Z (12 years ago)
Author:
Jan Vesely <jano.vesely@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
e978ada
Parents:
fbe148ee
Message:

usbhub: Fix over-current handling.

Make power-switching policies part of the hub structure.
Add incomplete device_remove handler.

Location:
uspace/drv/bus/usb/usbhub
Files:
4 edited

Legend:

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

    rfbe148ee r54d1ad9  
    4747 * For more information see section 11.15.1 of USB 1.1 specification.
    4848 */
    49 static usb_endpoint_description_t hub_status_change_endpoint_description = {
     49static const usb_endpoint_description_t hub_status_change_endpoint_description =
     50{
    5051        .transfer_type = USB_TRANSFER_INTERRUPT,
    5152        .direction = USB_DIRECTION_IN,
     
    5657};
    5758
    58 /**
    59  * USB hub driver operations
    60  *
    61  * The most important one is device_add, which is set to usb_hub_device_add.
    62  */
    63 static usb_driver_ops_t usb_hub_driver_ops = {
     59/** USB hub driver operations. */
     60static const usb_driver_ops_t usb_hub_driver_ops = {
    6461        .device_add = usb_hub_device_add,
     62//      .device_rem = usb_hub_device_remove,
    6563        .device_gone = usb_hub_device_gone,
    6664};
     
    7270};
    7371/** Static usb hub driver information. */
    74 static usb_driver_t usb_hub_driver = {
     72static const usb_driver_t usb_hub_driver = {
    7573        .name = NAME,
    7674        .ops = &usb_hub_driver_ops,
    7775        .endpoints = usb_hub_endpoints
    7876};
    79 
    8077
    8178int main(int argc, char *argv[])
  • uspace/drv/bus/usb/usbhub/port.h

    rfbe148ee r54d1ad9  
    4444/** Information about single port on a hub. */
    4545typedef struct {
     46        /* Port number as reporteed in descriptors. */
    4647        size_t port_number;
     48        /** Device communication pipe. */
    4749        usb_pipe_t *control_pipe;
    4850        /** Mutex needed not only by CV for checking port reset. */
  • uspace/drv/bus/usb/usbhub/usbhub.c

    rfbe148ee r54d1ad9  
    7474static void usb_hub_polling_terminated_callback(usb_device_t *device,
    7575    bool was_error, void *data);
    76 /**
    77  * Initialize hub device driver fibril
     76
     77/**
     78 * Initialize hub device driver structure.
    7879 *
    7980 * Creates hub representation and fibril that periodically checks hub's status.
    8081 * 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
    81205 * @param usb_dev generic usb device information
    82206 * @return error code
     
    120244}
    121245/*----------------------------------------------------------------------------*/
    122 /**
    123  * Initialize hub device driver fibril
    124  *
    125  * Creates hub representation and fibril that periodically checks hub's status.
    126  * Hub representation is passed to the fibril.
    127  * @param usb_dev generic usb device information
    128  * @return error code
    129  */
    130 int usb_hub_device_add(usb_device_t *usb_dev)
    131 {
    132         assert(usb_dev);
    133         /* Create driver soft-state structure */
    134         usb_hub_dev_t *hub_dev =
    135             usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t));
    136         if (hub_dev == NULL) {
    137                 usb_log_error("Failed to create hun driver structure.\n");
    138                 return ENOMEM;
    139         }
    140         hub_dev->usb_device = usb_dev;
    141         hub_dev->pending_ops_count = 0;
    142         hub_dev->running = false;
    143         fibril_mutex_initialize(&hub_dev->pending_ops_mutex);
    144         fibril_condvar_initialize(&hub_dev->pending_ops_cv);
    145 
    146         /* Create hc connection */
    147         usb_log_debug("Initializing USB wire abstraction.\n");
    148         int opResult = usb_hc_connection_initialize_from_device(
    149             &hub_dev->connection, hub_dev->usb_device->ddf_dev);
    150         if (opResult != EOK) {
    151                 usb_log_error("Could not initialize connection to device: %s\n",
    152                     str_error(opResult));
    153                 return opResult;
    154         }
    155 
    156         /* Set hub's first configuration. (There should be only one) */
    157         opResult = usb_set_first_configuration(usb_dev);
    158         if (opResult != EOK) {
    159                 usb_log_error("Could not set hub configuration: %s\n",
    160                     str_error(opResult));
    161                 return opResult;
    162         }
    163 
    164         /* Get port count and create attached_devices. */
    165         opResult = usb_hub_process_hub_specific_info(hub_dev);
    166         if (opResult != EOK) {
    167                 usb_log_error("Could process hub specific info, %s\n",
    168                     str_error(opResult));
    169                 return opResult;
    170         }
    171 
    172         usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.\n");
    173         hub_dev->hub_fun = ddf_fun_create(hub_dev->usb_device->ddf_dev,
    174             fun_exposed, HUB_FNC_NAME);
    175         if (hub_dev->hub_fun == NULL) {
    176                 usb_log_error("Failed to create hub function.\n");
    177                 return ENOMEM;
    178         }
    179 
    180         opResult = ddf_fun_bind(hub_dev->hub_fun);
    181         if (opResult != EOK) {
    182                 usb_log_error("Failed to bind hub function: %s.\n",
    183                    str_error(opResult));
    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                 usb_log_error("Failed to create polling fibril: %s.\n",
    196                     str_error(opResult));
    197                 return opResult;
    198         }
    199         hub_dev->running = true;
    200         usb_log_info("Controlling hub '%s' (%zu ports).\n",
    201             hub_dev->usb_device->ddf_dev->name, hub_dev->port_count);
    202 
    203         return EOK;
    204 }
    205 /*----------------------------------------------------------------------------*/
    206246/** Callback for polling hub for changes.
    207247 *
     
    284324        }
    285325
    286         const bool is_power_switched =
     326        hub_dev->power_switched =
    287327            !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG);
    288         if (is_power_switched) {
    289                 usb_log_debug("Hub power switched\n");
    290                 const bool per_port_power = descriptor.characteristics
    291                     & HUB_CHAR_POWER_PER_PORT_FLAG;
    292 
    293                 for (size_t port = 0; port < hub_dev->port_count; ++port) {
    294                         usb_log_debug("Powering port %zu.\n", port);
    295                         opResult = usb_hub_port_set_feature(
    296                             &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    297                         if (opResult != EOK) {
    298                                 usb_log_error("Cannot power on port %zu: %s.\n",
    299                                     port, str_error(opResult));
    300                         } else {
    301                                 if (!per_port_power) {
    302                                         usb_log_debug(
    303                                             "Ganged power switching mode, "
    304                                             "one port is enough.\n");
    305                                         break;
    306                                 }
     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;
    307352                        }
    308353                }
    309         } else {
    310                 usb_log_debug("Power not switched, ports always powered\n");
    311354        }
    312355        return EOK;
     
    375418                usb_log_warning("Detected hub over-current condition, "
    376419                    "all ports should be powered off.");
    377         } else {
    378                 /* Over-current condition is gone, it is safe to turn the
    379                  * ports on. */
    380                 for (size_t port = 0; port < hub_dev->port_count; ++port) {
    381                         const int opResult = usb_hub_port_set_feature(
    382                             &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
    383                         // TODO: consider power policy here
    384                         if (opResult != EOK) {
    385                                 usb_log_warning(
    386                                     "HUB OVER-CURRENT GONE: Cannot power on "
    387                                     "port %zu;  %s\n",
    388                                     port, str_error(opResult));
    389                         }
    390                 }
    391         }
    392         const int opResult = usb_request_clear_feature(
    393             &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS,
    394             USB_REQUEST_RECIPIENT_DEVICE,
    395             USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    396         if (opResult != EOK) {
    397                 usb_log_error(
    398                     "Failed to clear hub over-current change flag: %s.\n",
    399                     str_error(opResult));
    400         }
     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
    401441}
    402442/*----------------------------------------------------------------------------*/
     
    434474        if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
    435475                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                }
    436485        }
    437486
     
    450499                 * Just ACK the change.
    451500                 */
    452                 const int opResult = usb_request_clear_feature(
     501                const int ret = usb_request_clear_feature(
    453502                    control_pipe, USB_REQUEST_TYPE_CLASS,
    454503                    USB_REQUEST_RECIPIENT_DEVICE,
    455504                    USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
    456505                if (opResult != EOK) {
    457                         usb_log_error(
    458                             "Failed to clear hub power change flag: %s.\n",
    459                             str_error(opResult));
     506                        usb_log_error("Failed to clear hub power change "
     507                            "flag: %s.\n", str_error(ret));
    460508                }
    461509        }
  • uspace/drv/bus/usb/usbhub/usbhub.h

    rfbe148ee r54d1ad9  
    7777        /** Status indicator */
    7878        bool running;
     79        /** Hub supports port power switching. */
     80        bool power_switched;
     81        /** Each port is switched individually. */
     82        bool per_port_power;
    7983};
    8084
    8185int usb_hub_device_add(usb_device_t *usb_dev);
     86int usb_hub_device_remove(usb_device_t *usb_dev);
    8287int usb_hub_device_gone(usb_device_t *usb_dev);
    8388
Note: See TracChangeset for help on using the changeset viewer.