Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/usbhub/ports.c

    r2ad98fd r69df9373  
    3333 * Hub ports functions.
    3434 */
    35 
    36 #include <bool.h>
     35#include "port_status.h"
     36#include <inttypes.h>
    3737#include <errno.h>
    3838#include <str_error.h>
    39 #include <inttypes.h>
    40 #include <fibril_synch.h>
    41 
     39#include <usb/request.h>
    4240#include <usb/debug.h>
    4341
    44 #include "ports.h"
    45 #include "usbhub.h"
    46 #include "usbhub_private.h"
    47 #include "port_status.h"
    48 
     42/** Retrieve port status.
     43 *
     44 * @param[in] ctrl_pipe Control pipe to use.
     45 * @param[in] port Port number (starting at 1).
     46 * @param[out] status Where to store the port status.
     47 * @return Error code.
     48 */
     49static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
     50    usb_port_status_t *status)
     51{
     52        size_t recv_size;
     53        usb_device_request_setup_packet_t request;
     54        usb_port_status_t status_tmp;
     55
     56        usb_hub_set_port_status_request(&request, port);
     57        int rc = usb_pipe_control_read(ctrl_pipe,
     58            &request, sizeof(usb_device_request_setup_packet_t),
     59            &status_tmp, sizeof(status_tmp), &recv_size);
     60        if (rc != EOK) {
     61                return rc;
     62        }
     63
     64        if (recv_size != sizeof (status_tmp)) {
     65                return ELIMIT;
     66        }
     67
     68        if (status != NULL) {
     69                *status = status_tmp;
     70        }
     71
     72        return EOK;
     73}
    4974
    5075/** Information for fibril for device discovery. */
     
    5580};
    5681
    57 static void usb_hub_removed_device(
    58         usb_hub_info_t * hub, uint16_t port);
    59 
    60 static void usb_hub_port_reset_completed(usb_hub_info_t * hub,
    61         uint16_t port, uint32_t status);
    62 
    63 static void usb_hub_port_over_current(usb_hub_info_t * hub,
    64         uint16_t port, uint32_t status);
    65 
    66 static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
    67     usb_port_status_t *status);
    68 
    69 static int enable_port_callback(int port_no, void *arg);
    70 
    71 static int add_device_phase1_worker_fibril(void *arg);
    72 
    73 static int create_add_device_fibril(usb_hub_info_t *hub, size_t port,
    74     usb_speed_t speed);
    75 
    76 /**
    77  * Process interrupts on given hub port
    78  *
    79  * Accepts connection, over current and port reset change.
    80  * @param hub hub representation
    81  * @param port port number, starting from 1
    82  */
    83 void usb_hub_process_interrupt(usb_hub_info_t * hub,
    84         uint16_t port) {
    85         usb_log_debug("interrupt at port %d\n", port);
    86         //determine type of change
    87         //usb_pipe_t *pipe = hub->control_pipe;
    88 
    89         int opResult;
    90 
    91         usb_port_status_t status;
    92         opResult = get_port_status(&hub->usb_device->ctrl_pipe, port, &status);
    93         if (opResult != EOK) {
    94                 usb_log_error("Failed to get port %zu status: %s.\n",
    95                     port, str_error(opResult));
    96                 return;
    97         }
    98         //connection change
    99         if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_CONNECTION)) {
    100                 bool device_connected = usb_port_is_status(status,
    101                     USB_HUB_FEATURE_PORT_CONNECTION);
    102                 usb_log_debug("Connection change on port %zu: %s.\n", port,
    103                     device_connected ? "device attached" : "device removed");
    104 
    105                 if (device_connected) {
    106                         opResult = create_add_device_fibril(hub, port,
    107                             usb_port_speed(status));
    108                         if (opResult != EOK) {
    109                                 usb_log_error(
    110                                     "Cannot handle change on port %zu: %s.\n",
    111                                     str_error(opResult));
    112                         }
    113                 } else {
    114                         usb_hub_removed_device(hub, port);
    115                 }
    116         }
    117         //over current
    118         if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_OVER_CURRENT)) {
    119                 //check if it was not auto-resolved
    120                 usb_log_debug("overcurrent change on port\n");
    121                 usb_hub_port_over_current(hub, port, status);
    122         }
    123         //port reset
    124         if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_RESET)) {
    125                 usb_hub_port_reset_completed(hub, port, status);
    126         }
    127         usb_log_debug("status x%x : %d\n ", status, status);
    128 
    129         usb_port_status_set_bit(
    130             &status, USB_HUB_FEATURE_C_PORT_CONNECTION,false);
    131         usb_port_status_set_bit(
    132             &status, USB_HUB_FEATURE_PORT_RESET,false);
    133         usb_port_status_set_bit(
    134             &status, USB_HUB_FEATURE_C_PORT_RESET,false);
    135         usb_port_status_set_bit(
    136             &status, USB_HUB_FEATURE_C_PORT_OVER_CURRENT,false);
    137         /// \TODO what about port power change?
    138         if (status >> 16) {
    139                 usb_log_info("there was unsupported change on port %d: %X\n",
    140                         port, status);
    141 
    142         }
    143 }
    144 
    145 
    146 /**
    147  * routine called when a device on port has been removed
    148  *
    149  * If the device on port had default address, it releases default address.
    150  * Otherwise does not do anything, because DDF does not allow to remove device
    151  * from it`s device tree.
    152  * @param hub hub representation
    153  * @param port port number, starting from 1
    154  */
    155 static void usb_hub_removed_device(
    156         usb_hub_info_t * hub, uint16_t port) {
    157 
    158         int opResult = usb_hub_clear_port_feature(hub->control_pipe,
    159                 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
    160         if (opResult != EOK) {
    161                 usb_log_warning("could not clear port-change-connection flag\n");
    162         }
    163         /** \TODO remove device from device manager - not yet implemented in
    164          * devide manager
    165          */
    166 
    167         //close address
    168         //if (hub->attached_devs[port].address != 0) {
    169         if(hub->ports[port].attached_device.address >= 0){
    170                 /*uncomment this code to use it when DDF allows device removal
    171                 opResult = usb_hc_unregister_device(
    172                         &hub->connection,
    173                         hub->attached_devs[port].address);
    174                 if(opResult != EOK) {
    175                         dprintf(USB_LOG_LEVEL_WARNING, "could not release "
    176                                 "address of "
    177                             "removed device: %d", opResult);
    178                 }
    179                 hub->attached_devs[port].address = 0;
    180                 hub->attached_devs[port].handle = 0;
    181                  */
    182         } else {
    183                 usb_log_warning("this is strange, disconnected device had "
    184                         "no address\n");
    185                 //device was disconnected before it`s port was reset -
    186                 //return default address
    187                 usb_hub_release_default_address(hub);
    188         }
    189 }
    190 
    191 
    192 /**
    193  * Process port reset change
    194  *
    195  * After this change port should be enabled, unless some problem occured.
    196  * This functions triggers second phase of enabling new device.
    197  * @param hub
    198  * @param port
    199  * @param status
    200  */
    201 static void usb_hub_port_reset_completed(usb_hub_info_t * hub,
    202         uint16_t port, uint32_t status){
    203         usb_log_debug("Port %zu reset complete.\n", port);
    204         if (usb_port_is_status(status, USB_HUB_FEATURE_PORT_ENABLE)) {
    205                 /* Finalize device adding. */
    206                 usb_hub_port_t *the_port = hub->ports + port;
    207                 fibril_mutex_lock(&the_port->reset_mutex);
    208                 the_port->reset_completed = true;
    209                 fibril_condvar_broadcast(&the_port->reset_cv);
    210                 fibril_mutex_unlock(&the_port->reset_mutex);
    211         } else {
    212                 usb_log_warning(
    213                     "Port %zu reset complete but port not enabled.\n",
    214                     port);
    215         }
    216 }
    217 
    218 /**
    219  * Process over current condition on port.
    220  *
    221  * Turn off the power on the port.
    222  *
    223  * @param hub hub representation
    224  * @param port port number, starting from 1
    225  */
    226 static void usb_hub_port_over_current(usb_hub_info_t * hub,
    227         uint16_t port, uint32_t status) {
    228         int opResult;
    229         if(usb_port_is_status(status, USB_HUB_FEATURE_PORT_OVER_CURRENT)){
    230                 opResult = usb_hub_clear_port_feature(hub->control_pipe,
    231                         port, USB_HUB_FEATURE_PORT_POWER);
    232                 if (opResult != EOK) {
    233                         usb_log_error("cannot power off port %d;  %d\n",
    234                                 port, opResult);
    235                 }
    236         }else{
    237                 opResult = usb_hub_set_port_feature(hub->control_pipe,
    238                         port, USB_HUB_FEATURE_PORT_POWER);
    239                 if (opResult != EOK) {
    240                         usb_log_error("cannot power on port %d;  %d\n",
    241                                 port, opResult);
    242                 }
    243         }
    244 }
    245 
    246 /** Retrieve port status.
    247  *
    248  * @param[in] ctrl_pipe Control pipe to use.
    249  * @param[in] port Port number (starting at 1).
    250  * @param[out] status Where to store the port status.
    251  * @return Error code.
    252  */
    253 static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
    254     usb_port_status_t *status)
    255 {
    256         size_t recv_size;
    257         usb_device_request_setup_packet_t request;
    258         usb_port_status_t status_tmp;
    259 
    260         usb_hub_set_port_status_request(&request, port);
    261         int rc = usb_pipe_control_read(ctrl_pipe,
    262             &request, sizeof(usb_device_request_setup_packet_t),
    263             &status_tmp, sizeof(status_tmp), &recv_size);
    264         if (rc != EOK) {
    265                 return rc;
    266         }
    267 
    268         if (recv_size != sizeof (status_tmp)) {
    269                 return ELIMIT;
    270         }
    271 
    272         if (status != NULL) {
    273                 *status = status_tmp;
    274         }
    275 
    276         return EOK;
    277 }
    278 
    27982/** Callback for enabling a specific port.
    28083 *
     
    28891static int enable_port_callback(int port_no, void *arg)
    28992{
    290         usb_hub_info_t *hub = arg;
     93        usb_hub_info_t *hub = (usb_hub_info_t *) arg;
    29194        int rc;
    29295        usb_device_request_setup_packet_t request;
     
    353156        data->hub->ports[data->port].attached_device.address = new_address;
    354157
    355         usb_log_info("Detected new device on `%s' (port %zu), "
     158        usb_log_info("Detected new device on `%s' (port %zu), " \
    356159            "address %d (handle %" PRIun ").\n",
    357160            data->hub->usb_device->ddf_dev->name, data->port,
     
    363166        return EOK;
    364167}
    365 
    366168
    367169/** Start device adding when connection change is detected.
     
    374176 * @return Error code.
    375177 */
    376 static int create_add_device_fibril(usb_hub_info_t *hub, size_t port,
     178static int add_device_phase1_new_fibril(usb_hub_info_t *hub, size_t port,
    377179    usb_speed_t speed)
    378180{
     
    411213}
    412214
     215/** Process change on a single port.
     216 *
     217 * @param hub Hub to which the port belongs.
     218 * @param port Port index (starting at 1).
     219 */
     220static void process_port_change(usb_hub_info_t *hub, size_t port)
     221{
     222        int rc;
     223
     224        usb_port_status_t port_status;
     225
     226        rc = get_port_status(&hub->usb_device->ctrl_pipe, port, &port_status);
     227        if (rc != EOK) {
     228                usb_log_error("Failed to get port %zu status: %s.\n",
     229                    port, str_error(rc));
     230                return;
     231        }
     232
     233        /*
     234         * Check exact nature of the change.
     235         */
     236        usb_log_debug("Port %zu change status: %x.\n", port,
     237            (unsigned int) port_status);
     238
     239        if (usb_port_connect_change(&port_status)) {
     240                bool device_connected = usb_port_dev_connected(&port_status);
     241                usb_log_debug("Connection change on port %zu: %s.\n", port,
     242                    device_connected ? "device attached" : "device removed");
     243
     244                if (device_connected) {
     245                        rc = add_device_phase1_new_fibril(hub, port,
     246                            usb_port_speed(&port_status));
     247                        if (rc != EOK) {
     248                                usb_log_error(
     249                                    "Cannot handle change on port %zu: %s.\n",
     250                                    str_error(rc));
     251                        }
     252                } else {
     253                        usb_hub_removed_device(hub, port);
     254                }
     255        }
     256
     257        if (usb_port_overcurrent_change(&port_status)) {
     258                if (usb_port_over_current(&port_status)) {
     259                        usb_log_warning("Overcurrent on port %zu.\n", port);
     260                        usb_hub_over_current(hub, port);
     261                } else {
     262                        usb_log_debug("Overcurrent on port %zu autoresolved.\n",
     263                            port);
     264                }
     265        }
     266
     267        if (usb_port_reset_completed(&port_status)) {
     268                usb_log_debug("Port %zu reset complete.\n", port);
     269                if (usb_port_enabled(&port_status)) {
     270                        /* Finalize device adding. */
     271                        usb_hub_port_t *the_port = hub->ports + port;
     272                        fibril_mutex_lock(&the_port->reset_mutex);
     273                        the_port->reset_completed = true;
     274                        fibril_condvar_broadcast(&the_port->reset_cv);
     275                        fibril_mutex_unlock(&the_port->reset_mutex);
     276                } else {
     277                        usb_log_warning(
     278                            "Port %zu reset complete but port not enabled.\n",
     279                            port);
     280                }
     281        }
     282
     283        usb_port_set_connect_change(&port_status, false);
     284        usb_port_set_reset(&port_status, false);
     285        usb_port_set_reset_completed(&port_status, false);
     286        usb_port_set_dev_connected(&port_status, false);
     287        if (port_status >> 16) {
     288                usb_log_warning("Unsupported change on port %zu: %x.\n",
     289                    port, (unsigned int) port_status);
     290        }
     291}
     292
     293
     294/** Callback for polling hub for port changes.
     295 *
     296 * @param dev Device where the change occured.
     297 * @param change_bitmap Bitmap of changed ports.
     298 * @param change_bitmap_size Size of the bitmap in bytes.
     299 * @param arg Custom argument, points to @c usb_hub_info_t.
     300 * @return Whether to continue polling.
     301 */
     302bool hub_port_changes_callback(usb_device_t *dev,
     303    uint8_t *change_bitmap, size_t change_bitmap_size, void *arg)
     304{
     305        usb_hub_info_t *hub = (usb_hub_info_t *) arg;
     306
     307        /* FIXME: check that we received enough bytes. */
     308        if (change_bitmap_size == 0) {
     309                goto leave;
     310        }
     311
     312        size_t port;
     313        for (port = 1; port < hub->port_count + 1; port++) {
     314                bool change = (change_bitmap[port / 8] >> (port % 8)) % 2;
     315                if (change) {
     316                        process_port_change(hub, port);
     317                }
     318        }
     319
     320
     321leave:
     322        /* FIXME: proper interval. */
     323        async_usleep(1000 * 1000 * 10 );
     324
     325        return true;
     326}
     327
     328
    413329/**
    414330 * @}
Note: See TracChangeset for help on using the changeset viewer.