Changeset 6843a9c in mainline for uspace/lib/usbhost/src/usb_endpoint_manager.c
- Timestamp:
- 2012-06-29T13:02:14Z (13 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/usbhost/src/usb_endpoint_manager.c
rba72f2b r6843a9c 26 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 27 */ 28 /** @addtogroup libusbhost 29 * @{ 30 */ 31 /** @file 32 * HC Endpoint management. 33 */ 28 34 29 35 #include <bool.h> … … 34 40 #include <usb/host/usb_endpoint_manager.h> 35 41 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 */ 52 static 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 */ 69 static 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 */ 85 static 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 */ 97 107 size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type, 98 108 size_t size, size_t max_packet_size) … … 106 116 const unsigned packet_count = 107 117 (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 */ 111 120 /* NOTE: All data packets will be considered to be max_packet_size */ 112 121 switch (speed) … … 137 146 } 138 147 /*----------------------------------------------------------------------------*/ 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 */ 139 157 int usb_endpoint_manager_init(usb_endpoint_manager_t *instance, 140 158 size_t available_bandwidth, … … 145 163 instance->free_bw = available_bandwidth; 146 164 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 } 196 168 return EOK; 197 169 } 198 170 /*----------------------------------------------------------------------------*/ 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 /*----------------------------------------------------------------------------*/246 171 /** Check setup packet data for signs of toggle reset. 247 172 * 248 * @param[in] instance Device keeper structure to use.173 * @param[in] instance usb_endpoint_manager structure, non-null. 249 174 * @param[in] target Device to receive setup packet. 250 175 * @param[in] data Setup packet data. 251 176 * 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 */ 180 void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance, 181 usb_target_t target, const uint8_t data[8]) 256 182 { 257 183 assert(instance); … … 266 192 case 0x01: /* Clear Feature -- resets only cleared ep */ 267 193 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */ 194 // TODO Use macros in libusbdev requests.h 268 195 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) { 196 fibril_mutex_lock(&instance->guard); 269 197 /* 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 } 275 205 fibril_mutex_unlock(&instance->guard); 276 206 } … … 279 209 case 0x9: /* Set Configuration */ 280 210 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. */ 282 216 if ((data[0] & 0xf) == 0) { 283 usb_target_t reset_target =284 { .address = target.address, 0 };285 217 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 } 288 224 fibril_mutex_unlock(&instance->guard); 289 225 } … … 291 227 } 292 228 } 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 */ 237 int 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 */ 271 int 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 */ 293 endpoint_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 */ 317 int 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 */ 376 int 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 */ 407 void 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.