Changeset 34d750c in mainline
- Timestamp:
- 2018-01-22T21:44:56Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 3ac86a4
- Parents:
- 9f685aa
- Location:
- uspace/drv/bus/usb/usbhub
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/usbhub/usbhub.c
r9f685aa r34d750c 67 67 } 68 68 69 /** Hub status-change endpoint description. 70 * 71 * For more information see section 11.15.1 of USB 1.1 specification. 69 /** 70 * Hub status-change endpoint description. 71 * 72 * According to USB 2.0 specification, there are two possible arrangements of 73 * endpoints, depending on whether the hub has a MTT or not. 74 * 75 * Under any circumstances, there shall be exactly one endpoint descriptor. 76 * Though to be sure, let's map the protocol precisely. The possible 77 * combinations are: 78 * | bDeviceProtocol | bInterfaceProtocol 79 * Only single TT | 0 | 0 80 * MTT in Single-TT mode | 2 | 1 81 * MTT in MTT mode | 2 | 2 (iface alt. 1) 72 82 */ 73 83 static const usb_endpoint_description_t … … 79 89 &status_change_mtt_available, 80 90 }; 81 82 91 83 92 /** Standard get hub global status request */ … … 90 99 }; 91 100 92 static int usb_set_first_configuration(usb_device_t *usb_device); 93 static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev); 94 static void usb_hub_over_current(const usb_hub_dev_t *hub_dev, 95 usb_hub_status_t status); 96 static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev); 97 98 static bool usb_hub_polling_error_callback(usb_device_t *dev, int err_code, void *arg) 101 static int usb_set_first_configuration(usb_device_t *); 102 static int usb_hub_process_hub_specific_info(usb_hub_dev_t *); 103 static void usb_hub_over_current(const usb_hub_dev_t *, usb_hub_status_t); 104 static int usb_hub_polling_init(usb_hub_dev_t *, usb_endpoint_mapping_t *); 105 static void usb_hub_global_interrupt(const usb_hub_dev_t *); 106 107 static bool usb_hub_polling_error_callback(usb_device_t *dev, 108 int err_code, void *arg) 99 109 { 100 110 assert(dev); 101 111 assert(arg); 102 112 103 usb_log_error("Device %s polling error: %s", usb_device_get_name(dev), str_error(err_code)); 113 usb_log_error("Device %s polling error: %s", 114 usb_device_get_name(dev), str_error(err_code)); 104 115 105 116 return true; … … 116 127 int usb_hub_device_add(usb_device_t *usb_dev) 117 128 { 129 int err; 118 130 assert(usb_dev); 131 119 132 /* Create driver soft-state structure */ 120 133 usb_hub_dev_t *hub_dev = … … 127 140 hub_dev->speed = usb_device_get_speed(usb_dev); 128 141 129 fibril_mutex_initialize(&hub_dev->default_address_guard);130 fibril_condvar_initialize(&hub_dev->default_address_cv);131 132 142 /* Set hub's first configuration. (There should be only one) */ 133 int opResult = usb_set_first_configuration(usb_dev); 134 if (opResult != EOK) { 135 usb_log_error("Could not set hub configuration: %s", 136 str_error(opResult)); 137 return opResult; 143 if ((err = usb_set_first_configuration(usb_dev))) { 144 usb_log_error("Could not set hub configuration: %s", str_error(err)); 145 return err; 138 146 } 139 147 140 148 /* Get port count and create attached_devices. */ 141 opResult = usb_hub_process_hub_specific_info(hub_dev); 142 if (opResult != EOK) { 143 usb_log_error("Could process hub specific info, %s", 144 str_error(opResult)); 145 return opResult; 149 if ((err = usb_hub_process_hub_specific_info(hub_dev))) { 150 usb_log_error("Could process hub specific info, %s", str_error(err)); 151 return err; 146 152 } 147 153 … … 167 173 168 174 /* Bind hub control function. */ 169 opResult = ddf_fun_bind(hub_dev->hub_fun); 170 if (opResult != EOK) { 171 usb_log_error("Failed to bind hub function: %s.", 172 str_error(opResult)); 173 ddf_fun_destroy(hub_dev->hub_fun); 174 return opResult; 175 if ((err = ddf_fun_bind(hub_dev->hub_fun))) { 176 usb_log_error("Failed to bind hub function: %s.", str_error(err)); 177 goto err_ddf_fun; 175 178 } 176 179 177 180 /* Start hub operation. */ 181 if ((err = usb_hub_polling_init(hub_dev, status_change_mapping))) { 182 usb_log_error("Failed to start polling: %s.", str_error(err)); 183 goto err_bound; 184 } 185 186 usb_log_info("Controlling %s-speed hub '%s' (%p: %zu ports).", 187 usb_str_speed(hub_dev->speed), 188 usb_device_get_name(hub_dev->usb_device), hub_dev, 189 hub_dev->port_count); 190 191 return EOK; 192 193 err_bound: 194 ddf_fun_unbind(hub_dev->hub_fun); 195 err_ddf_fun: 196 ddf_fun_destroy(hub_dev->hub_fun); 197 return err; 198 } 199 200 static int usb_hub_cleanup(usb_hub_dev_t *hub) 201 { 202 free(hub->polling.buffer); 203 usb_polling_fini(&hub->polling); 204 205 for (size_t port = 0; port < hub->port_count; ++port) { 206 usb_port_fini(&hub->ports[port].base); 207 } 208 free(hub->ports); 209 210 const int ret = ddf_fun_unbind(hub->hub_fun); 211 if (ret != EOK) { 212 usb_log_error("(%p) Failed to unbind '%s' function: %s.", 213 hub, HUB_FNC_NAME, str_error(ret)); 214 return ret; 215 } 216 ddf_fun_destroy(hub->hub_fun); 217 218 usb_log_info("(%p) USB hub driver stopped and cleaned.", hub); 219 220 /* Device data (usb_hub_dev_t) will be freed by usbdev. */ 221 return EOK; 222 } 223 224 /** 225 * Turn off power to all ports. 226 * 227 * @param usb_dev generic usb device information 228 * @return error code 229 */ 230 int usb_hub_device_remove(usb_device_t *usb_dev) 231 { 232 assert(usb_dev); 233 usb_hub_dev_t *hub = usb_device_data_get(usb_dev); 234 assert(hub); 235 236 usb_log_info("(%p) USB hub removed, joining polling fibril.", hub); 237 238 /* Join polling fibril (ignoring error code). */ 239 usb_polling_join(&hub->polling); 240 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub); 241 242 /* Destroy hub. */ 243 return usb_hub_cleanup(hub); 244 } 245 246 /** 247 * Remove all attached devices 248 * @param usb_dev generic usb device information 249 * @return error code 250 */ 251 int usb_hub_device_gone(usb_device_t *usb_dev) 252 { 253 assert(usb_dev); 254 usb_hub_dev_t *hub = usb_device_data_get(usb_dev); 255 assert(hub); 256 257 usb_log_info("(%p) USB hub gone, joining polling fibril.", hub); 258 259 /* Join polling fibril (ignoring error code). */ 260 usb_polling_join(&hub->polling); 261 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub); 262 263 /* Destroy hub. */ 264 return usb_hub_cleanup(hub); 265 } 266 267 /** 268 * Initialize and start the polling of the Status Change Endpoint. 269 * 270 * @param mapping The mapping of Status Change Endpoint 271 */ 272 static int usb_hub_polling_init(usb_hub_dev_t *hub_dev, 273 usb_endpoint_mapping_t *mapping) 274 { 275 int err; 178 276 usb_polling_t *polling = &hub_dev->polling; 179 opResult = usb_polling_init(polling); 180 if (opResult != EOK) { 181 /* Function is already bound */ 182 ddf_fun_unbind(hub_dev->hub_fun); 183 ddf_fun_destroy(hub_dev->hub_fun); 184 usb_log_error("Failed to initialize polling fibril: %s.", 185 str_error(opResult)); 186 return opResult; 187 } 277 278 if ((err = usb_polling_init(polling))) 279 return err; 188 280 189 281 polling->device = hub_dev->usb_device; 190 polling->ep_mapping = status_change_mapping;282 polling->ep_mapping = mapping; 191 283 polling->request_size = ((hub_dev->port_count + 1 + 7) / 8); 192 284 polling->buffer = malloc(polling->request_size); … … 195 287 polling->arg = hub_dev; 196 288 197 opResult = usb_polling_start(polling); 198 if (opResult != EOK) { 289 if ((err = usb_polling_start(polling))) { 199 290 /* Polling is already initialized. */ 200 291 free(polling->buffer); 201 292 usb_polling_fini(polling); 202 ddf_fun_unbind(hub_dev->hub_fun); 203 ddf_fun_destroy(hub_dev->hub_fun); 204 usb_log_error("Failed to create polling fibril: %s.", 205 str_error(opResult)); 206 return opResult; 207 } 208 209 usb_log_info("Controlling %s-speed hub '%s' (%p: %zu ports).", 210 usb_str_speed(hub_dev->speed), 211 usb_device_get_name(hub_dev->usb_device), hub_dev, 212 hub_dev->port_count); 293 return err; 294 } 213 295 214 296 return EOK; 215 297 } 216 298 217 static int usb_hub_cleanup(usb_hub_dev_t *hub) 218 { 219 free(hub->polling.buffer); 220 usb_polling_fini(&hub->polling); 221 222 for (size_t port = 0; port < hub->port_count; ++port) { 223 usb_port_fini(&hub->ports[port].base); 224 } 225 free(hub->ports); 226 227 const int ret = ddf_fun_unbind(hub->hub_fun); 228 if (ret != EOK) { 229 usb_log_error("(%p) Failed to unbind '%s' function: %s.", 230 hub, HUB_FNC_NAME, str_error(ret)); 231 return ret; 232 } 233 ddf_fun_destroy(hub->hub_fun); 234 235 usb_log_info("(%p) USB hub driver stopped and cleaned.", hub); 236 237 /* Device data (usb_hub_dev_t) will be freed by usbdev. */ 238 return EOK; 239 } 240 241 /** 242 * Turn off power to all ports. 243 * 244 * @param usb_dev generic usb device information 245 * @return error code 246 */ 247 int usb_hub_device_remove(usb_device_t *usb_dev) 248 { 249 assert(usb_dev); 250 usb_hub_dev_t *hub = usb_device_data_get(usb_dev); 251 assert(hub); 252 253 usb_log_info("(%p) USB hub removed, joining polling fibril.", hub); 254 255 /* Join polling fibril (ignoring error code). */ 256 usb_polling_join(&hub->polling); 257 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub); 258 259 /* Destroy hub. */ 260 return usb_hub_cleanup(hub); 261 } 262 263 /** 264 * Remove all attached devices 265 * @param usb_dev generic usb device information 266 * @return error code 267 */ 268 int usb_hub_device_gone(usb_device_t *usb_dev) 269 { 270 assert(usb_dev); 271 usb_hub_dev_t *hub = usb_device_data_get(usb_dev); 272 assert(hub); 273 274 usb_log_info("(%p) USB hub gone, joining polling fibril.", hub); 275 276 /* Join polling fibril (ignoring error code). */ 277 usb_polling_join(&hub->polling); 278 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub); 279 280 /* Destroy hub. */ 281 return usb_hub_cleanup(hub); 282 } 283 284 /** Callback for polling hub for changes. 299 /** 300 * Callback for polling hub for changes. 285 301 * 286 302 * @param dev Device where the change occured. … … 307 323 } 308 324 309 /* N + 1bit indicates change on port N */325 /* Nth bit indicates change on port N */ 310 326 for (size_t port = 0; port < hub->port_count; ++port) { 311 327 const size_t bit = port + 1; … … 331 347 for (unsigned int port = 0; port < hub_dev->port_count; ++port) { 332 348 usb_log_debug("(%p): Powering port %u.", hub_dev, port + 1); 333 const int ret = usb_hub_set_port_feature(hub_dev, port + 1, USB_HUB_FEATURE_PORT_POWER); 349 const int ret = usb_hub_set_port_feature(hub_dev, port + 1, 350 USB_HUB_FEATURE_PORT_POWER); 334 351 335 352 if (ret != EOK) { … … 356 373 assert(hub_dev); 357 374 358 /* Get hub descriptor. */359 375 usb_log_debug("(%p): Retrieving descriptor.", hub_dev); 360 376 usb_pipe_t *control_pipe = usb_device_get_default_pipe(hub_dev->usb_device); … … 363 379 ? USB_DESCTYPE_SSPEED_HUB : USB_DESCTYPE_HUB; 364 380 381 /* Get hub descriptor. */ 365 382 usb_hub_descriptor_header_t descriptor; 366 383 size_t received_size; … … 483 500 /* Over-current condition is gone, it is safe to turn the ports on. */ 484 501 for (size_t port = 0; port < hub_dev->port_count; ++port) { 485 const int ret = usb_hub_set_port_feature(hub_dev, port, USB_HUB_FEATURE_PORT_POWER); 502 const int ret = usb_hub_set_port_feature(hub_dev, port, 503 USB_HUB_FEATURE_PORT_POWER); 486 504 if (ret != EOK) { 487 505 usb_log_warning("(%p-%u): HUB OVER-CURRENT GONE: Cannot" … … 526 544 * @param feature Feature selector. 527 545 */ 528 int usb_hub_set_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature) 546 int usb_hub_set_port_feature(const usb_hub_dev_t *hub, size_t port_number, 547 usb_hub_class_feature_t feature) 529 548 { 530 549 assert(hub); … … 546 565 * @param feature Feature selector. 547 566 */ 548 int usb_hub_clear_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature) 567 int usb_hub_clear_port_feature(const usb_hub_dev_t *hub, size_t port_number, 568 usb_hub_class_feature_t feature) 549 569 { 550 570 assert(hub); … … 567 587 * @return Error code. 568 588 */ 569 int usb_hub_get_port_status(const usb_hub_dev_t *hub, size_t port_number, usb_port_status_t *status) 589 int usb_hub_get_port_status(const usb_hub_dev_t *hub, size_t port_number, 590 usb_port_status_t *status) 570 591 { 571 592 assert(hub); … … 669 690 } 670 691 692 /** 693 * Instead of just sleeping, we may as well sleep on a condition variable. 694 * This has the advantage that we may instantly wait other hub from the polling 695 * sleep, mitigating the delay of polling while still being synchronized with 696 * other devices in need of the default address (there shall not be any). 697 */ 671 698 static FIBRIL_CONDVAR_INITIALIZE(global_hub_default_address_cv); 699 static FIBRIL_MUTEX_INITIALIZE(global_hub_default_address_guard); 672 700 673 701 /** … … 677 705 * is connected with already attached devices. 678 706 */ 679 int usb_hub_reserve_default_address(usb_hub_dev_t *hub, async_exch_t *exch, usb_port_t *port) 707 int usb_hub_reserve_default_address(usb_hub_dev_t *hub, async_exch_t *exch, 708 usb_port_t *port) 680 709 { 681 710 assert(hub); … … 684 713 assert(fibril_mutex_is_locked(&port->guard)); 685 714 686 fibril_mutex_lock(&hub->default_address_guard); 687 if (hub->default_address_requests++ == 0) { 688 /* We're the first to request the address, we can just do it */ 689 fibril_mutex_unlock(&hub->default_address_guard); 690 int err; 691 while ((err = usbhc_reserve_default_address(exch)) == EAGAIN) { 692 // We ignore the return value here, as we cannot give up now. 693 usb_port_condvar_wait_timeout(port, &global_hub_default_address_cv, 500000); 694 } 695 return err; 696 } else { 715 int err = usbhc_reserve_default_address(exch); 716 /* 717 * EINVAL signalls that its our hub (hopefully different port) that has 718 * this address reserved 719 */ 720 while (err == EAGAIN || err == EINVAL) { 697 721 /* Drop the port guard, we're going to wait */ 698 722 fibril_mutex_unlock(&port->guard); 699 723 700 /* Wait for a signal */ 701 fibril_condvar_wait(&hub->default_address_cv, &hub->default_address_guard); 702 703 /* Remember ABBA, first drop the hub guard */ 704 fibril_mutex_unlock(&hub->default_address_guard); 724 /* This sleeping might be disturbed by other hub */ 725 fibril_mutex_lock(&global_hub_default_address_guard); 726 fibril_condvar_wait_timeout(&global_hub_default_address_cv, 727 &global_hub_default_address_guard, 2000000); 728 fibril_mutex_unlock(&global_hub_default_address_guard); 729 705 730 fibril_mutex_lock(&port->guard); 706 return EOK; 707 } 731 err = usbhc_reserve_default_address(exch); 732 } 733 734 if (err) 735 return err; 736 737 /* 738 * As we dropped the port guard, we need to check whether the device is 739 * still connected. If the release fails, we still hold the default 740 * address -- but then there is probably a bigger problem with the HC 741 * anyway. 742 */ 743 if (port->state != PORT_CONNECTING) { 744 err = usb_hub_release_default_address(hub, exch); 745 return err ? err : EINTR; 746 } 747 748 return EOK; 708 749 } 709 750 … … 713 754 int usb_hub_release_default_address(usb_hub_dev_t *hub, async_exch_t *exch) 714 755 { 715 int ret = EOK; 716 717 fibril_mutex_lock(&hub->default_address_guard); 718 if (--hub->default_address_requests == 0) { 719 // We must do it in critical section to prevent other fibril 720 // from requesting the address before we release 721 ret = usbhc_release_default_address(exch); 722 // This is optimistic optimization - it may wake one hub from polling sleep 723 fibril_condvar_signal(&global_hub_default_address_cv); 724 } else { 725 fibril_condvar_signal(&hub->default_address_cv); 726 } 727 fibril_mutex_unlock(&hub->default_address_guard); 756 const int ret = usbhc_release_default_address(exch); 757 758 /* 759 * This is an optimistic optimization - it may wake 760 * one hub from polling sleep instantly. 761 */ 762 fibril_condvar_signal(&global_hub_default_address_cv); 728 763 729 764 return ret; -
uspace/drv/bus/usb/usbhub/usbhub.h
r9f685aa r34d750c 74 74 /** Whether MTT is available */ 75 75 bool mtt_available; 76 77 /** Default address management */78 unsigned default_address_requests;79 fibril_mutex_t default_address_guard;80 fibril_condvar_t default_address_cv;81 76 }; 82 77
Note:
See TracChangeset
for help on using the changeset viewer.