Changeset e50cd7f in mainline for uspace/drv/usbhub/ports.c


Ignore:
Timestamp:
2011-04-17T19:17:55Z (14 years ago)
Author:
Matej Klonfar <maklf@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
63517c2, cfbbe1d3
Parents:
ef354b6 (diff), 8595577b (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

new report structure fixes

File:
1 edited

Legend:

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

    ref354b6 re50cd7f  
    3333 * Hub ports functions.
    3434 */
    35 #include "port_status.h"
    36 #include <inttypes.h>
     35
     36#include <bool.h>
    3737#include <errno.h>
    3838#include <str_error.h>
    39 #include <usb/request.h>
     39#include <inttypes.h>
     40#include <fibril_synch.h>
     41
    4042#include <usb/debug.h>
     43
     44#include "ports.h"
     45#include "usbhub.h"
     46#include "usbhub_private.h"
     47#include "port_status.h"
     48
     49
     50/** Information for fibril for device discovery. */
     51struct add_device_phase1 {
     52        usb_hub_info_t *hub;
     53        size_t port;
     54        usb_speed_t speed;
     55};
     56
     57static void usb_hub_removed_device(
     58        usb_hub_info_t * hub, uint16_t port);
     59
     60static void usb_hub_port_reset_completed(usb_hub_info_t * hub,
     61        uint16_t port, uint32_t status);
     62
     63static void usb_hub_port_over_current(usb_hub_info_t * hub,
     64        uint16_t port, uint32_t status);
     65
     66static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
     67    usb_port_status_t *status);
     68
     69static int enable_port_callback(int port_no, void *arg);
     70
     71static int add_device_phase1_worker_fibril(void *arg);
     72
     73static 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 */
     83void 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 */
     155static 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                // TODO: is this really reason to print a warning?
     184                usb_log_warning("Device removed before being registered.\n");
     185
     186                /*
     187                 * Device was removed before port reset completed.
     188                 * We will announce a failed port reset to unblock the
     189                 * port reset callback from new device wrapper.
     190                 */
     191                usb_hub_port_t *the_port = hub->ports + port;
     192                fibril_mutex_lock(&the_port->reset_mutex);
     193                the_port->reset_completed = true;
     194                the_port->reset_okay = false;
     195                fibril_condvar_broadcast(&the_port->reset_cv);
     196                fibril_mutex_unlock(&the_port->reset_mutex);
     197        }
     198}
     199
     200
     201/**
     202 * Process port reset change
     203 *
     204 * After this change port should be enabled, unless some problem occured.
     205 * This functions triggers second phase of enabling new device.
     206 * @param hub
     207 * @param port
     208 * @param status
     209 */
     210static void usb_hub_port_reset_completed(usb_hub_info_t * hub,
     211        uint16_t port, uint32_t status){
     212        usb_log_debug("Port %zu reset complete.\n", port);
     213        if (usb_port_is_status(status, USB_HUB_FEATURE_PORT_ENABLE)) {
     214                /* Finalize device adding. */
     215                usb_hub_port_t *the_port = hub->ports + port;
     216                fibril_mutex_lock(&the_port->reset_mutex);
     217                the_port->reset_completed = true;
     218                the_port->reset_okay = true;
     219                fibril_condvar_broadcast(&the_port->reset_cv);
     220                fibril_mutex_unlock(&the_port->reset_mutex);
     221        } else {
     222                usb_log_warning(
     223                    "Port %zu reset complete but port not enabled.\n",
     224                    port);
     225        }
     226}
     227
     228/**
     229 * Process over current condition on port.
     230 *
     231 * Turn off the power on the port.
     232 *
     233 * @param hub hub representation
     234 * @param port port number, starting from 1
     235 */
     236static void usb_hub_port_over_current(usb_hub_info_t * hub,
     237        uint16_t port, uint32_t status) {
     238        int opResult;
     239        if(usb_port_is_status(status, USB_HUB_FEATURE_PORT_OVER_CURRENT)){
     240                opResult = usb_hub_clear_port_feature(hub->control_pipe,
     241                        port, USB_HUB_FEATURE_PORT_POWER);
     242                if (opResult != EOK) {
     243                        usb_log_error("cannot power off port %d;  %d\n",
     244                                port, opResult);
     245                }
     246        }else{
     247                opResult = usb_hub_set_port_feature(hub->control_pipe,
     248                        port, USB_HUB_FEATURE_PORT_POWER);
     249                if (opResult != EOK) {
     250                        usb_log_error("cannot power on port %d;  %d\n",
     251                                port, opResult);
     252                }
     253        }
     254}
    41255
    42256/** Retrieve port status.
     
    73287}
    74288
    75 /** Information for fibril for device discovery. */
    76 struct add_device_phase1 {
    77         usb_hub_info_t *hub;
    78         size_t port;
    79         usb_speed_t speed;
    80 };
    81 
    82289/** Callback for enabling a specific port.
    83290 *
     
    91298static int enable_port_callback(int port_no, void *arg)
    92299{
    93         usb_hub_info_t *hub = (usb_hub_info_t *) arg;
     300        usb_hub_info_t *hub = arg;
    94301        int rc;
    95302        usb_device_request_setup_packet_t request;
     
    122329        }
    123330
    124         return EOK;
     331        if (my_port->reset_okay) {
     332                return EOK;
     333        } else {
     334                return ESTALL;
     335        }
    125336}
    126337
     
    156367        data->hub->ports[data->port].attached_device.address = new_address;
    157368
    158         usb_log_info("Detected new device on `%s' (port %zu), " \
     369        usb_log_info("Detected new device on `%s' (port %zu), "
    159370            "address %d (handle %" PRIun ").\n",
    160371            data->hub->usb_device->ddf_dev->name, data->port,
     
    166377        return EOK;
    167378}
     379
    168380
    169381/** Start device adding when connection change is detected.
     
    176388 * @return Error code.
    177389 */
    178 static int add_device_phase1_new_fibril(usb_hub_info_t *hub, size_t port,
     390static int create_add_device_fibril(usb_hub_info_t *hub, size_t port,
    179391    usb_speed_t speed)
    180392{
     
    213425}
    214426
    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  */
    220 static 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  */
    302 bool 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 
    321 leave:
    322         /* FIXME: proper interval. */
    323         async_usleep(1000 * 1000 * 10 );
    324 
    325         return true;
    326 }
    327 
    328 
    329427/**
    330428 * @}
Note: See TracChangeset for help on using the changeset viewer.