Ignore:
Timestamp:
2012-06-29T13:02:14Z (13 years ago)
Author:
Jan Vesely <jano.vesely@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
722912e
Parents:
ba72f2b (diff), 0bbd13e (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:

Merge mainline changes

Trivial conflicts.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/src/usb_endpoint_manager.c

    rba72f2b r6843a9c  
    2626 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2727 */
     28/**  @addtogroup libusbhost
     29 * @{
     30 */
     31/** @file
     32 * HC Endpoint management.
     33 */
    2834
    2935#include <bool.h>
     
    3440#include <usb/host/usb_endpoint_manager.h>
    3541
    36 #define BUCKET_COUNT 7
    37 
    38 #define MAX_KEYS (3)
    39 typedef struct {
    40         link_t link;
    41         size_t bw;
    42         endpoint_t *ep;
    43 } node_t;
    44 /*----------------------------------------------------------------------------*/
    45 static hash_index_t node_hash(unsigned long key[])
    46 {
    47         /* USB endpoints use 4 bits, thus ((key[0] << 4) | key[1])
    48          * produces unique value for every address.endpoint pair */
    49         return ((key[0] << 4) | key[1]) % BUCKET_COUNT;
    50 }
    51 /*----------------------------------------------------------------------------*/
    52 static int node_compare(unsigned long key[], hash_count_t keys, link_t *item)
    53 {
    54         assert(item);
    55         node_t *node = hash_table_get_instance(item, node_t, link);
    56         assert(node);
    57         assert(node->ep);
    58         bool match = true;
    59         switch (keys) {
    60         case 3:
    61                 match = match &&
    62                     ((key[2] == node->ep->direction)
    63                     || (node->ep->direction == USB_DIRECTION_BOTH));
    64         case 2:
    65                 match = match && (key[1] == (unsigned long)node->ep->endpoint);
    66         case 1:
    67                 match = match && (key[0] == (unsigned long)node->ep->address);
    68                 break;
    69         default:
    70                 match = false;
    71         }
    72         return match;
    73 }
    74 /*----------------------------------------------------------------------------*/
    75 static void node_remove(link_t *item)
    76 {
    77         assert(item);
    78         node_t *node = hash_table_get_instance(item, node_t, link);
    79         endpoint_destroy(node->ep);
    80         free(node);
    81 }
    82 /*----------------------------------------------------------------------------*/
    83 static void node_toggle_reset_filtered(link_t *item, void *arg)
    84 {
    85         assert(item);
    86         node_t *node = hash_table_get_instance(item, node_t, link);
    87         usb_target_t *target = arg;
    88         endpoint_toggle_reset_filtered(node->ep, *target);
    89 }
    90 /*----------------------------------------------------------------------------*/
    91 static hash_table_operations_t op = {
    92         .hash = node_hash,
    93         .compare = node_compare,
    94         .remove_callback = node_remove,
    95 };
    96 /*----------------------------------------------------------------------------*/
     42/** Endpoint compare helper function.
     43 *
     44 * USB_DIRECTION_BOTH matches both IN and OUT.
     45 * @param ep Endpoint to compare, non-null.
     46 * @param address Tested address.
     47 * @param endpoint Tested endpoint number.
     48 * @param direction Tested direction.
     49 * @return True if ep can be used to communicate with given device,
     50 * false otherwise.
     51 */
     52static inline bool ep_match(const endpoint_t *ep,
     53    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     54{
     55        assert(ep);
     56        return
     57            ((direction == ep->direction)
     58                || (ep->direction == USB_DIRECTION_BOTH)
     59                || (direction == USB_DIRECTION_BOTH))
     60            && (endpoint == ep->endpoint)
     61            && (address == ep->address);
     62}
     63/*----------------------------------------------------------------------------*/
     64/** Get list that holds endpoints for given address.
     65 * @param instance usb_endpoint_manager structure, non-null.
     66 * @param addr USB address, must be >= 0.
     67 * @return Pointer to the appropriate list.
     68 */
     69static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
     70{
     71        assert(instance);
     72        assert(addr >= 0);
     73        return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
     74}
     75/*----------------------------------------------------------------------------*/
     76/** Internal search function, works on locked structure.
     77 * @param instance usb_endpoint_manager structure, non-null.
     78 * @param address USB address, must be valid.
     79 * @param endpoint USB endpoint number.
     80 * @param direction Communication direction.
     81 * @return Pointer to endpoint_t structure representing given communication
     82 * target, NULL if there is no such endpoint registered.
     83 * @note Assumes that the internal mutex is locked.
     84 */
     85static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
     86    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     87{
     88        assert(instance);
     89        assert(fibril_mutex_is_locked(&instance->guard));
     90        if (address < 0)
     91                return NULL;
     92        list_foreach(*get_list(instance, address), iterator) {
     93                endpoint_t *ep = endpoint_get_instance(iterator);
     94                if (ep_match(ep, address, endpoint, direction))
     95                        return ep;
     96        }
     97        return NULL;
     98}
     99/*----------------------------------------------------------------------------*/
     100/** Calculate bandwidth that needs to be reserved for communication with EP.
     101 * Calculation follows USB 1.1 specification.
     102 * @param speed Device's speed.
     103 * @param type Type of the transfer.
     104 * @param size Number of byte to transfer.
     105 * @param max_packet_size Maximum bytes in one packet.
     106 */
    97107size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
    98108    size_t size, size_t max_packet_size)
     
    106116        const unsigned packet_count =
    107117            (size + max_packet_size - 1) / max_packet_size;
    108         /* TODO: It may be that ISO and INT transfers use only one data packet
    109          * per transaction, but I did not find text in UB spec that confirms
    110          * this */
     118        /* TODO: It may be that ISO and INT transfers use only one packet per
     119         * transaction, but I did not find text in USB spec to confirm this */
    111120        /* NOTE: All data packets will be considered to be max_packet_size */
    112121        switch (speed)
     
    137146}
    138147/*----------------------------------------------------------------------------*/
     148/** Initialize to default state.
     149 * You need to provide valid bw_count function if you plan to use
     150 * add_endpoint/remove_endpoint pair.
     151 *
     152 * @param instance usb_endpoint_manager structure, non-null.
     153 * @param available_bandwidth Size of the bandwidth pool.
     154 * @param bw_count function to use to calculate endpoint bw requirements.
     155 * @return Error code.
     156 */
    139157int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
    140158    size_t available_bandwidth,
     
    145163        instance->free_bw = available_bandwidth;
    146164        instance->bw_count = bw_count;
    147         const bool ht =
    148             hash_table_create(&instance->ep_table, BUCKET_COUNT, MAX_KEYS, &op);
    149         return ht ? EOK : ENOMEM;
    150 }
    151 /*----------------------------------------------------------------------------*/
    152 void usb_endpoint_manager_destroy(usb_endpoint_manager_t *instance)
    153 {
    154         hash_table_destroy(&instance->ep_table);
    155 }
    156 /*----------------------------------------------------------------------------*/
    157 int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
    158     endpoint_t *ep, size_t data_size)
    159 {
    160         assert(instance);
    161         assert(instance->bw_count);
    162         assert(ep);
    163         const size_t bw = instance->bw_count(ep->speed, ep->transfer_type,
    164             data_size, ep->max_packet_size);
    165 
    166         fibril_mutex_lock(&instance->guard);
    167 
    168         if (bw > instance->free_bw) {
    169                 fibril_mutex_unlock(&instance->guard);
    170                 return ENOSPC;
    171         }
    172 
    173         unsigned long key[MAX_KEYS] =
    174             {ep->address, ep->endpoint, ep->direction};
    175 
    176         const link_t *item =
    177             hash_table_find(&instance->ep_table, key);
    178         if (item != NULL) {
    179                 fibril_mutex_unlock(&instance->guard);
    180                 return EEXISTS;
    181         }
    182 
    183         node_t *node = malloc(sizeof(node_t));
    184         if (node == NULL) {
    185                 fibril_mutex_unlock(&instance->guard);
    186                 return ENOMEM;
    187         }
    188 
    189         node->bw = bw;
    190         node->ep = ep;
    191         link_initialize(&node->link);
    192 
    193         hash_table_insert(&instance->ep_table, key, &node->link);
    194         instance->free_bw -= bw;
    195         fibril_mutex_unlock(&instance->guard);
     165        for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
     166                list_initialize(&instance->endpoint_lists[i]);
     167        }
    196168        return EOK;
    197169}
    198170/*----------------------------------------------------------------------------*/
    199 int usb_endpoint_manager_unregister_ep(usb_endpoint_manager_t *instance,
    200     usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
    201 {
    202         assert(instance);
    203         unsigned long key[MAX_KEYS] = {address, endpoint, direction};
    204 
    205         fibril_mutex_lock(&instance->guard);
    206         link_t *item = hash_table_find(&instance->ep_table, key);
    207         if (item == NULL) {
    208                 fibril_mutex_unlock(&instance->guard);
    209                 return EINVAL;
    210         }
    211 
    212         node_t *node = hash_table_get_instance(item, node_t, link);
    213         if (node->ep->active) {
    214                 fibril_mutex_unlock(&instance->guard);
    215                 return EBUSY;
    216         }
    217 
    218         instance->free_bw += node->bw;
    219         hash_table_remove(&instance->ep_table, key, MAX_KEYS);
    220 
    221         fibril_mutex_unlock(&instance->guard);
    222         return EOK;
    223 }
    224 /*----------------------------------------------------------------------------*/
    225 endpoint_t * usb_endpoint_manager_get_ep(usb_endpoint_manager_t *instance,
    226     usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
    227     size_t *bw)
    228 {
    229         assert(instance);
    230         unsigned long key[MAX_KEYS] = {address, endpoint, direction};
    231 
    232         fibril_mutex_lock(&instance->guard);
    233         const link_t *item = hash_table_find(&instance->ep_table, key);
    234         if (item == NULL) {
    235                 fibril_mutex_unlock(&instance->guard);
    236                 return NULL;
    237         }
    238         const node_t *node = hash_table_get_instance(item, node_t, link);
    239         if (bw)
    240                 *bw = node->bw;
    241 
    242         fibril_mutex_unlock(&instance->guard);
    243         return node->ep;
    244 }
    245 /*----------------------------------------------------------------------------*/
    246171/** Check setup packet data for signs of toggle reset.
    247172 *
    248  * @param[in] instance Device keeper structure to use.
     173 * @param[in] instance usb_endpoint_manager structure, non-null.
    249174 * @param[in] target Device to receive setup packet.
    250175 * @param[in] data Setup packet data.
    251176 *
    252  * Really ugly one.
    253  */
    254 void usb_endpoint_manager_reset_if_need(
    255     usb_endpoint_manager_t *instance, usb_target_t target, const uint8_t *data)
     177 * Really ugly one. Resets toggle bit on all endpoints that need it.
     178 * @TODO Use tools from libusbdev requests.h
     179 */
     180void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance,
     181    usb_target_t target, const uint8_t data[8])
    256182{
    257183        assert(instance);
     
    266192        case 0x01: /* Clear Feature -- resets only cleared ep */
    267193                /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
     194                // TODO Use macros in libusbdev requests.h
    268195                if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
     196                        fibril_mutex_lock(&instance->guard);
    269197                        /* endpoint number is < 16, thus first byte is enough */
    270                         usb_target_t reset_target =
    271                             { .address = target.address, data[4] };
    272                         fibril_mutex_lock(&instance->guard);
    273                         hash_table_apply(&instance->ep_table,
    274                             node_toggle_reset_filtered, &reset_target);
     198                        list_foreach(*get_list(instance, target.address), it) {
     199                                endpoint_t *ep = endpoint_get_instance(it);
     200                                if ((ep->address == target.address)
     201                                    && (ep->endpoint = data[4])) {
     202                                        endpoint_toggle_set(ep,0);
     203                                }
     204                        }
    275205                        fibril_mutex_unlock(&instance->guard);
    276206                }
     
    279209        case 0x9: /* Set Configuration */
    280210        case 0x11: /* Set Interface */
    281                 /* Recipient must be device */
     211                /* Recipient must be device, this resets all endpoints,
     212                 * In fact there should be no endpoints but EP 0 registered
     213                 * as different interfaces use different endpoints,
     214                 * unless you're changing configuration or alternative
     215                 * interface of an already setup device. */
    282216                if ((data[0] & 0xf) == 0) {
    283                         usb_target_t reset_target =
    284                             { .address = target.address, 0 };
    285217                        fibril_mutex_lock(&instance->guard);
    286                         hash_table_apply(&instance->ep_table,
    287                             node_toggle_reset_filtered, &reset_target);
     218                        list_foreach(*get_list(instance, target.address), it) {
     219                                endpoint_t *ep = endpoint_get_instance(it);
     220                                if (ep->address == target.address) {
     221                                        endpoint_toggle_set(ep,0);
     222                                }
     223                        }
    288224                        fibril_mutex_unlock(&instance->guard);
    289225                }
     
    291227        }
    292228}
     229/*----------------------------------------------------------------------------*/
     230/** Register endpoint structure.
     231 * Checks for duplicates.
     232 * @param instance usb_endpoint_manager, non-null.
     233 * @param ep endpoint_t to register.
     234 * @param data_size Size of data to transfer.
     235 * @return Error code.
     236 */
     237int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
     238    endpoint_t *ep, size_t data_size)
     239{
     240        assert(instance);
     241        if (ep == NULL || ep->address < 0)
     242                return EINVAL;
     243
     244        fibril_mutex_lock(&instance->guard);
     245        /* Check for available bandwidth */
     246        if (ep->bandwidth > instance->free_bw) {
     247                fibril_mutex_unlock(&instance->guard);
     248                return ENOSPC;
     249        }
     250
     251        /* Check for existence */
     252        const endpoint_t *endpoint =
     253            find_locked(instance, ep->address, ep->endpoint, ep->direction);
     254        if (endpoint != NULL) {
     255                fibril_mutex_unlock(&instance->guard);
     256                return EEXISTS;
     257        }
     258        list_append(&ep->link, get_list(instance, ep->address));
     259
     260        instance->free_bw -= ep->bandwidth;
     261        fibril_mutex_unlock(&instance->guard);
     262        return EOK;
     263}
     264/*----------------------------------------------------------------------------*/
     265/** Unregister endpoint structure.
     266 * Checks for duplicates.
     267 * @param instance usb_endpoint_manager, non-null.
     268 * @param ep endpoint_t to unregister.
     269 * @return Error code.
     270 */
     271int usb_endpoint_manager_unregister_ep(
     272    usb_endpoint_manager_t *instance, endpoint_t *ep)
     273{
     274        assert(instance);
     275        if (ep == NULL || ep->address < 0)
     276                return EINVAL;
     277
     278        fibril_mutex_lock(&instance->guard);
     279        if (!list_member(&ep->link, get_list(instance, ep->address))) {
     280                fibril_mutex_unlock(&instance->guard);
     281                return ENOENT;
     282        }
     283        list_remove(&ep->link);
     284        instance->free_bw += ep->bandwidth;
     285        fibril_mutex_unlock(&instance->guard);
     286        return EOK;
     287}
     288/*----------------------------------------------------------------------------*/
     289/** Find endpoint_t representing the given communication route.
     290 * @param instance usb_endpoint_manager, non-null.
     291 * @param address
     292 */
     293endpoint_t * usb_endpoint_manager_find_ep(usb_endpoint_manager_t *instance,
     294    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     295{
     296        assert(instance);
     297
     298        fibril_mutex_lock(&instance->guard);
     299        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     300        fibril_mutex_unlock(&instance->guard);
     301        return ep;
     302}
     303/*----------------------------------------------------------------------------*/
     304/** Create and register new endpoint_t structure.
     305 * @param instance usb_endpoint_manager structure, non-null.
     306 * @param address USB address.
     307 * @param endpoint USB endpoint number.
     308 * @param direction Communication direction.
     309 * @param type USB transfer type.
     310 * @param speed USB Communication speed.
     311 * @param max_packet_size Maximum size of data packets.
     312 * @param data_size Expected communication size.
     313 * @param callback function to call just after registering.
     314 * @param arg Argument to pass to the callback function.
     315 * @return Error code.
     316 */
     317int usb_endpoint_manager_add_ep(usb_endpoint_manager_t *instance,
     318    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
     319    usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size,
     320    size_t data_size, int (*callback)(endpoint_t *, void *), void *arg)
     321{
     322        assert(instance);
     323        if (instance->bw_count == NULL)
     324                return ENOTSUP;
     325        if (address < 0)
     326                return EINVAL;
     327
     328        const size_t bw =
     329            instance->bw_count(speed, type, data_size, max_packet_size);
     330
     331        fibril_mutex_lock(&instance->guard);
     332        /* Check for available bandwidth */
     333        if (bw > instance->free_bw) {
     334                fibril_mutex_unlock(&instance->guard);
     335                return ENOSPC;
     336        }
     337
     338        /* Check for existence */
     339        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     340        if (ep != NULL) {
     341                fibril_mutex_unlock(&instance->guard);
     342                return EEXISTS;
     343        }
     344
     345        ep = endpoint_create(
     346            address, endpoint, direction, type, speed, max_packet_size, bw);
     347        if (!ep) {
     348                fibril_mutex_unlock(&instance->guard);
     349                return ENOMEM;
     350        }
     351
     352        if (callback) {
     353                const int ret = callback(ep, arg);
     354                if (ret != EOK) {
     355                        fibril_mutex_unlock(&instance->guard);
     356                        endpoint_destroy(ep);
     357                        return ret;
     358                }
     359        }
     360        list_append(&ep->link, get_list(instance, ep->address));
     361
     362        instance->free_bw -= ep->bandwidth;
     363        fibril_mutex_unlock(&instance->guard);
     364        return EOK;
     365}
     366/*----------------------------------------------------------------------------*/
     367/** Unregister and destroy endpoint_t structure representing given route.
     368 * @param instance usb_endpoint_manager structure, non-null.
     369 * @param address USB address.
     370 * @param endpoint USB endpoint number.
     371 * @param direction Communication direction.
     372 * @param callback Function to call after unregister, before destruction.
     373 * @arg Argument to pass to the callback function.
     374 * @return Error code.
     375 */
     376int usb_endpoint_manager_remove_ep(usb_endpoint_manager_t *instance,
     377    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
     378    void (*callback)(endpoint_t *, void *), void *arg)
     379{
     380        assert(instance);
     381        fibril_mutex_lock(&instance->guard);
     382        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     383        if (ep != NULL) {
     384                list_remove(&ep->link);
     385                instance->free_bw += ep->bandwidth;
     386        }
     387        fibril_mutex_unlock(&instance->guard);
     388        if (ep == NULL)
     389                return ENOENT;
     390
     391        if (callback) {
     392                callback(ep, arg);
     393        }
     394        endpoint_destroy(ep);
     395        return EOK;
     396}
     397/*----------------------------------------------------------------------------*/
     398/** Unregister and destroy all endpoints using given address.
     399 * @param instance usb_endpoint_manager structure, non-null.
     400 * @param address USB address.
     401 * @param endpoint USB endpoint number.
     402 * @param direction Communication direction.
     403 * @param callback Function to call after unregister, before destruction.
     404 * @arg Argument to pass to the callback function.
     405 * @return Error code.
     406 */
     407void usb_endpoint_manager_remove_address(usb_endpoint_manager_t *instance,
     408    usb_address_t address, void (*callback)(endpoint_t *, void *), void *arg)
     409{
     410        assert(address >= 0);
     411        assert(instance);
     412        fibril_mutex_lock(&instance->guard);
     413        list_foreach(*get_list(instance, address), iterator) {
     414                endpoint_t *ep = endpoint_get_instance(iterator);
     415                if (ep->address == address) {
     416                        iterator = iterator->next;
     417                        list_remove(&ep->link);
     418                        if (callback)
     419                                callback(ep, arg);
     420                        endpoint_destroy(ep);
     421                }
     422        }
     423        fibril_mutex_unlock(&instance->guard);
     424}
     425/**
     426 * @}
     427 */
Note: See TracChangeset for help on using the changeset viewer.