Ignore:
Timestamp:
2011-10-30T19:50:54Z (13 years ago)
Author:
Maurizio Lombardi <m.lombardi85@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
3ce78580, 48902fa
Parents:
4c3ad56 (diff), 45bf63c (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

File:
1 edited

Legend:

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

    r4c3ad56 r20a3465  
    3434#include <usb/host/usb_endpoint_manager.h>
    3535
    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 /*----------------------------------------------------------------------------*/
     36/** Endpoint compare helper function.
     37 *
     38 * USB_DIRECTION_BOTH matches both IN and OUT.
     39 * @param ep Endpoint to compare, non-null.
     40 * @param address Tested address.
     41 * @param endpoint Tested endpoint number.
     42 * @param direction Tested direction.
     43 * @return True if ep can be used to communicate with given device,
     44 * false otherwise.
     45 */
     46static inline bool ep_match(const endpoint_t *ep,
     47    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     48{
     49        assert(ep);
     50        return
     51            ((direction == ep->direction)
     52                || (ep->direction == USB_DIRECTION_BOTH)
     53                || (direction == USB_DIRECTION_BOTH))
     54            && (endpoint == ep->endpoint)
     55            && (address == ep->address);
     56}
     57/*----------------------------------------------------------------------------*/
     58/** Get list that holds endpints for given address.
     59 * @param instance usb_endpoint_manager structure, non-null.
     60 * @param addr USB address, must be >= 0.
     61 * @return Pointer to the appropriate list.
     62 */
     63static list_t * get_list(usb_endpoint_manager_t *instance, usb_address_t addr)
     64{
     65        assert(instance);
     66        assert(addr >= 0);
     67        return &instance->endpoint_lists[addr % ENDPOINT_LIST_COUNT];
     68}
     69/*----------------------------------------------------------------------------*/
     70/** Internal search function, works on locked structure.
     71 * @param instance usb_endpoint_manager structure, non-null.
     72 * @param address USB address, must be valid.
     73 * @param endpoint USB endpoint number.
     74 * @param direction Communication direction.
     75 * @return Pointer to endpoint_t structure representing given communication
     76 * target, NULL if there is no such endpoint registered.
     77 */
     78static endpoint_t * find_locked(usb_endpoint_manager_t *instance,
     79    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     80{
     81        assert(instance);
     82        assert(fibril_mutex_is_locked(&instance->guard));
     83        if (address < 0)
     84                return NULL;
     85        list_foreach(*get_list(instance, address), iterator) {
     86                endpoint_t *ep = endpoint_get_instance(iterator);
     87                if (ep_match(ep, address, endpoint, direction))
     88                        return ep;
     89        }
     90        return NULL;
     91}
     92/*----------------------------------------------------------------------------*/
     93/** Calculate bandwidth that needs to be reserved for communication with EP.
     94 * Calculation follows USB 1.1 specification.
     95 * @param speed Device's speed.
     96 * @param type Type of the transfer.
     97 * @param size Number of byte to transfer.
     98 * @param max_packet_size Maximum bytes in one packet.
     99 */
    97100size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
    98101    size_t size, size_t max_packet_size)
     
    106109        const unsigned packet_count =
    107110            (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 */
     111        /* TODO: It may be that ISO and INT transfers use only one packet per
     112         * transaction, but I did not find text in USB spec to confirm this */
    111113        /* NOTE: All data packets will be considered to be max_packet_size */
    112114        switch (speed)
     
    137139}
    138140/*----------------------------------------------------------------------------*/
     141/** Initialize to default state.
     142 * You need to provide valid bw_count function if you plan to use
     143 * add_endpoint/remove_endpoint pair.
     144 *
     145 * @param instance usb_endpoint_manager structure, non-null.
     146 * @param available_bandwidth Size of the bandwidth pool.
     147 * @param bw_count function to use to calculate endpoint bw requirements.
     148 * @return Error code.
     149 */
    139150int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
    140151    size_t available_bandwidth,
     
    145156        instance->free_bw = available_bandwidth;
    146157        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);
    196         return EOK;
    197 }
    198 /*----------------------------------------------------------------------------*/
    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;
     158        for (unsigned i = 0; i < ENDPOINT_LIST_COUNT; ++i) {
     159                list_initialize(&instance->endpoint_lists[i]);
     160        }
     161        return EOK;
    244162}
    245163/*----------------------------------------------------------------------------*/
    246164/** Check setup packet data for signs of toggle reset.
    247165 *
    248  * @param[in] instance Device keeper structure to use.
     166 * @param[in] instance usb_endpoint_manager structure, non-null.
    249167 * @param[in] target Device to receive setup packet.
    250168 * @param[in] data Setup packet data.
    251169 *
    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)
     170 * Really ugly one. Resets toggle bit on all endpoints that need it.
     171 */
     172void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance,
     173    usb_target_t target, const uint8_t data[8])
    256174{
    257175        assert(instance);
     
    267185                /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */
    268186                if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
     187                        fibril_mutex_lock(&instance->guard);
    269188                        /* 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);
     189                        list_foreach(*get_list(instance, target.address), it) {
     190                                endpoint_t *ep = endpoint_get_instance(it);
     191                                if ((ep->address == target.address)
     192                                    && (ep->endpoint = data[4])) {
     193                                        endpoint_toggle_set(ep,0);
     194                                }
     195                        }
    275196                        fibril_mutex_unlock(&instance->guard);
    276197                }
     
    279200        case 0x9: /* Set Configuration */
    280201        case 0x11: /* Set Interface */
    281                 /* Recipient must be device */
     202                /* Recipient must be device, this resets all endpoints,
     203                 * In fact there should be no endpoints but EP 0 registered
     204                 * as different interfaces use different endpoints. */
    282205                if ((data[0] & 0xf) == 0) {
    283                         usb_target_t reset_target =
    284                             { .address = target.address, 0 };
    285206                        fibril_mutex_lock(&instance->guard);
    286                         hash_table_apply(&instance->ep_table,
    287                             node_toggle_reset_filtered, &reset_target);
     207                        list_foreach(*get_list(instance, target.address), it) {
     208                                endpoint_t *ep = endpoint_get_instance(it);
     209                                if (ep->address == target.address) {
     210                                        endpoint_toggle_set(ep,0);
     211                                }
     212                        }
    288213                        fibril_mutex_unlock(&instance->guard);
    289214                }
     
    291216        }
    292217}
     218/*----------------------------------------------------------------------------*/
     219/** Register endpoint structure.
     220 * Checks for duplicates.
     221 * @param instance usb_endpoint_manager, non-null.
     222 * @param ep endpoint_t to register.
     223 * @param data_size Size of data to transfer.
     224 * @return Error code.
     225 */
     226int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
     227    endpoint_t *ep, size_t data_size)
     228{
     229        assert(instance);
     230        if (ep == NULL || ep->address < 0)
     231                return EINVAL;
     232
     233        fibril_mutex_lock(&instance->guard);
     234        /* Check for available bandwidth */
     235        if (ep->bandwidth > instance->free_bw) {
     236                fibril_mutex_unlock(&instance->guard);
     237                return ENOSPC;
     238        }
     239
     240        /* Check for existence */
     241        const endpoint_t *endpoint =
     242            find_locked(instance, ep->address, ep->endpoint, ep->direction);
     243        if (endpoint != NULL) {
     244                fibril_mutex_unlock(&instance->guard);
     245                return EEXISTS;
     246        }
     247        list_append(&ep->link, get_list(instance, ep->address));
     248
     249        instance->free_bw -= ep->bandwidth;
     250        fibril_mutex_unlock(&instance->guard);
     251        return EOK;
     252}
     253/*----------------------------------------------------------------------------*/
     254/** Unregister endpoint structure.
     255 * Checks for duplicates.
     256 * @param instance usb_endpoint_manager, non-null.
     257 * @param ep endpoint_t to unregister.
     258 * @return Error code.
     259 */
     260int usb_endpoint_manager_unregister_ep(
     261    usb_endpoint_manager_t *instance, endpoint_t *ep)
     262{
     263        assert(instance);
     264        if (ep == NULL || ep->address < 0)
     265                return EINVAL;
     266
     267        fibril_mutex_lock(&instance->guard);
     268        if (!list_member(&ep->link, get_list(instance, ep->address))) {
     269                fibril_mutex_unlock(&instance->guard);
     270                return ENOENT;
     271        }
     272        list_remove(&ep->link);
     273        instance->free_bw += ep->bandwidth;
     274        fibril_mutex_unlock(&instance->guard);
     275        return EOK;
     276}
     277/*----------------------------------------------------------------------------*/
     278/** Find endpoint_t representing the given communication route.
     279 * @param instance usb_endpoint_manager, non-null.
     280 * @param address
     281 */
     282endpoint_t * usb_endpoint_manager_find_ep(usb_endpoint_manager_t *instance,
     283    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
     284{
     285        assert(instance);
     286
     287        fibril_mutex_lock(&instance->guard);
     288        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     289        fibril_mutex_unlock(&instance->guard);
     290        return ep;
     291}
     292/*----------------------------------------------------------------------------*/
     293/** Create and register new endpoint_t structure.
     294 * @param instance usb_endpoint_manager structure, non-null.
     295 * @param address USB address.
     296 * @param endpoint USB endpoint number.
     297 * @param direction Communication direction.
     298 * @param type USB transfer type.
     299 * @param speed USB Communication speed.
     300 * @param max_packet_size Maximum size of data packets.
     301 * @param data_size Expected communication size.
     302 * @param callback function to call just after registering.
     303 * @param arg Argument to pass to the callback function.
     304 * @return Error code.
     305 */
     306int usb_endpoint_manager_add_ep(usb_endpoint_manager_t *instance,
     307    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
     308    usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size,
     309    size_t data_size, int (*callback)(endpoint_t *, void *), void *arg)
     310{
     311        assert(instance);
     312        if (instance->bw_count == NULL)
     313                return ENOTSUP;
     314        if (address < 0)
     315                return EINVAL;
     316
     317        const size_t bw =
     318            instance->bw_count(speed, type, data_size, max_packet_size);
     319
     320        fibril_mutex_lock(&instance->guard);
     321        /* Check for available bandwidth */
     322        if (bw > instance->free_bw) {
     323                fibril_mutex_unlock(&instance->guard);
     324                return ENOSPC;
     325        }
     326
     327        /* Check for existence */
     328        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     329        if (ep != NULL) {
     330                fibril_mutex_unlock(&instance->guard);
     331                return EEXISTS;
     332        }
     333
     334        ep = endpoint_create(
     335            address, endpoint, direction, type, speed, max_packet_size, bw);
     336        if (!ep) {
     337                fibril_mutex_unlock(&instance->guard);
     338                return ENOMEM;
     339        }
     340
     341        if (callback) {
     342                const int ret = callback(ep, arg);
     343                if (ret != EOK) {
     344                        fibril_mutex_unlock(&instance->guard);
     345                        endpoint_destroy(ep);
     346                        return ret;
     347                }
     348        }
     349        list_append(&ep->link, get_list(instance, ep->address));
     350
     351        instance->free_bw -= ep->bandwidth;
     352        fibril_mutex_unlock(&instance->guard);
     353        return EOK;
     354}
     355/*----------------------------------------------------------------------------*/
     356/** Unregister and destroy endpoint_t structure representing given route.
     357 * @param instance usb_endpoint_manager structure, non-null.
     358 * @param address USB address.
     359 * @param endpoint USB endpoint number.
     360 * @param direction Communication direction.
     361 * @param callback Function to call after unregister, before destruction.
     362 * @arg Argument to pass to the callback function.
     363 * @return Error code.
     364 */
     365int usb_endpoint_manager_remove_ep(usb_endpoint_manager_t *instance,
     366    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
     367    void (*callback)(endpoint_t *, void *), void *arg)
     368{
     369        assert(instance);
     370        fibril_mutex_lock(&instance->guard);
     371        endpoint_t *ep = find_locked(instance, address, endpoint, direction);
     372        if (ep != NULL) {
     373                list_remove(&ep->link);
     374                instance->free_bw += ep->bandwidth;
     375        }
     376        fibril_mutex_unlock(&instance->guard);
     377        if (ep == NULL)
     378                return ENOENT;
     379
     380        if (callback) {
     381                callback(ep, arg);
     382        }
     383        endpoint_destroy(ep);
     384        return EOK;
     385}
Note: See TracChangeset for help on using the changeset viewer.