Changeset 20a3465 in mainline for uspace/lib/usbhost/src/usb_endpoint_manager.c
- Timestamp:
- 2011-10-30T19:50:54Z (13 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/usbhost/src/usb_endpoint_manager.c
r4c3ad56 r20a3465 34 34 #include <usb/host/usb_endpoint_manager.h> 35 35 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 */ 46 static 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 */ 63 static 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 */ 78 static 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 */ 97 100 size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type, 98 101 size_t size, size_t max_packet_size) … … 106 109 const unsigned packet_count = 107 110 (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 */ 111 113 /* NOTE: All data packets will be considered to be max_packet_size */ 112 114 switch (speed) … … 137 139 } 138 140 /*----------------------------------------------------------------------------*/ 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 */ 139 150 int usb_endpoint_manager_init(usb_endpoint_manager_t *instance, 140 151 size_t available_bandwidth, … … 145 156 instance->free_bw = available_bandwidth; 146 157 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; 244 162 } 245 163 /*----------------------------------------------------------------------------*/ 246 164 /** Check setup packet data for signs of toggle reset. 247 165 * 248 * @param[in] instance Device keeper structure to use.166 * @param[in] instance usb_endpoint_manager structure, non-null. 249 167 * @param[in] target Device to receive setup packet. 250 168 * @param[in] data Setup packet data. 251 169 * 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 */ 172 void usb_endpoint_manager_reset_eps_if_need(usb_endpoint_manager_t *instance, 173 usb_target_t target, const uint8_t data[8]) 256 174 { 257 175 assert(instance); … … 267 185 /* Recipient is endpoint, value is zero (ENDPOINT_STALL) */ 268 186 if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) { 187 fibril_mutex_lock(&instance->guard); 269 188 /* 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 } 275 196 fibril_mutex_unlock(&instance->guard); 276 197 } … … 279 200 case 0x9: /* Set Configuration */ 280 201 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. */ 282 205 if ((data[0] & 0xf) == 0) { 283 usb_target_t reset_target =284 { .address = target.address, 0 };285 206 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 } 288 213 fibril_mutex_unlock(&instance->guard); 289 214 } … … 291 216 } 292 217 } 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 */ 226 int 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 */ 260 int 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 */ 282 endpoint_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 */ 306 int 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 */ 365 int 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.