Changeset 94f8c363 in mainline
- Timestamp:
- 2018-01-18T00:48:27Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- a9fcd73
- Parents:
- 8ad2b0a
- Location:
- uspace
- Files:
-
- 2 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/usbhub/port.c
r8ad2b0a r94f8c363 58 58 assert(port); 59 59 memset(port, 0, sizeof(*port)); 60 fibril_mutex_initialize(&port->guard);61 fibril_condvar_initialize(&port->state_cv);62 60 port->hub = hub; 63 61 port->port_number = port_number; 64 } 65 66 /** 67 * Utility method to change current port state and notify waiters. 68 */ 69 static void port_change_state(usb_hub_port_t *port, port_state_t state) 70 { 71 assert(fibril_mutex_is_locked(&port->guard)); 72 #define S(STATE) [PORT_##STATE] = #STATE 73 static const char *state_names [] = { 74 S(DISABLED), S(CONNECTED), S(ERROR), S(IN_RESET), S(ENABLED), 75 }; 76 #undef S 77 port_log(debug, port, "%s ->%s", state_names[port->state], state_names[state]); 78 port->state = state; 79 fibril_condvar_broadcast(&port->state_cv); 80 } 81 82 /** 83 * Utility method to wait for a particular state. 84 * 85 * @warning Some states might not be reached because of an external error 86 * condition (PORT_ENABLED). 87 */ 88 static void port_wait_state(usb_hub_port_t *port, port_state_t state) 89 { 90 assert(fibril_mutex_is_locked(&port->guard)); 91 while (port->state != state) 92 fibril_condvar_wait(&port->state_cv, &port->guard); 62 usb_port_init(&port->base); 63 } 64 65 static inline usb_hub_port_t *get_hub_port(usb_port_t *port) 66 { 67 assert(port); 68 return (usb_hub_port_t *) port; 93 69 } 94 70 … … 96 72 * Inform the HC that the device on port is gone. 97 73 */ 98 static int usb_hub_port_device_gone(usb_hub_port_t *port) 99 { 100 assert(port); 101 assert(fibril_mutex_is_locked(&port->guard)); 102 assert(port->state == PORT_ENABLED); 74 static void remove_device(usb_port_t *port_base) 75 { 76 usb_hub_port_t *port = get_hub_port(port_base); 103 77 104 78 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device); 105 if (!exch) 79 if (!exch) { 80 port_log(error, port, "Cannot remove the device, failed creating exchange."); 81 return; 82 } 83 84 const int err = usbhc_device_remove(exch, port->port_number); 85 if (err) 86 port_log(error, port, "Failed to remove device: %s", str_error(err)); 87 88 usb_device_bus_exchange_end(exch); 89 } 90 91 /** 92 * Routine for adding a new device. 93 * 94 * Separate fibril is needed because the operation blocks on waiting for 95 * requesting default address and resetting port, and we must not block the 96 * control pipe. 97 */ 98 static int enumerate_device(usb_port_t *port_base) 99 { 100 int err; 101 usb_hub_port_t *port = get_hub_port(port_base); 102 103 port_log(debug, port, "Setting up new device."); 104 105 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device); 106 if (!exch) { 107 port_log(error, port, "Failed to create exchange."); 106 108 return ENOMEM; 107 const int rc = usbhc_device_remove(exch, port->port_number); 108 usb_device_bus_exchange_end(exch); 109 return rc; 110 } 111 112 /** 113 * Teardown a device on port, no matter which state it was in. 114 */ 115 static void port_make_disabled(usb_hub_port_t *port) 116 { 117 assert(fibril_mutex_is_locked(&port->guard)); 118 119 port_log(debug, port, "Making device offline."); 120 121 switch (port->state) { 122 case PORT_ENABLED: 123 port_log(debug, port, "Enabled ->"); 124 if (usb_hub_port_device_gone(port)) 125 port_log(error, port, "Failed to remove the device node from HC. Continuing anyway."); 126 port_change_state(port, PORT_DISABLED); 127 break; 128 129 case PORT_CONNECTED: 130 port_log(debug, port, "Connected ->"); 131 /* fallthrough */ 132 case PORT_IN_RESET: 133 port_log(debug, port, "In reset ->"); 134 port_change_state(port, PORT_ERROR); 135 /* fallthrough */ 136 case PORT_ERROR: 137 port_log(debug, port, "Error ->"); 138 port_wait_state(port, PORT_DISABLED); 139 /* fallthrough */ 140 case PORT_DISABLED: 141 port_log(debug, port, "Disabled."); 142 break; 143 } 144 145 assert(port->state == PORT_DISABLED); 146 } 147 148 /** 149 * Finalize a port. Make sure no fibril is managing its state, 150 * and that the HC is aware the device is no longer there. 151 */ 152 void usb_hub_port_fini(usb_hub_port_t *port) 153 { 154 assert(port); 155 156 fibril_mutex_lock(&port->guard); 157 switch (port->state) { 158 case PORT_ENABLED: 159 /* 160 * We shall inform the HC that the device is gone. 161 * However, we can't wait for it, because if the device is hub, 162 * it would have to use the same IPC handling fibril as we do. 163 * But we cannot even defer it to another fibril, because then 164 * the HC would assume our driver didn't cleanup properly, and 165 * will remove those devices by himself. 166 * 167 * So the solutions seems to behave like a bad driver and leave 168 * the work for HC. 169 */ 170 port_change_state(port, PORT_DISABLED); 171 break; 172 173 case PORT_CONNECTED: 174 case PORT_IN_RESET: 175 port_change_state(port, PORT_ERROR); 176 /* fallthrough */ 177 case PORT_ERROR: 178 port_wait_state(port, PORT_DISABLED); 179 /* fallthrough */ 180 case PORT_DISABLED: 181 break; 182 } 183 port_log(debug, port, "Finalized."); 184 fibril_mutex_unlock(&port->guard); 185 } 186 187 static int port_reset_sync(usb_hub_port_t *port) 188 { 189 assert(fibril_mutex_is_locked(&port->guard)); 190 assert(port->state == PORT_CONNECTED); 191 192 port_change_state(port, PORT_IN_RESET); 193 port_log(debug2, port, "Issuing reset."); 109 } 110 111 /* Reserve default address */ 112 err = usb_hub_reserve_default_address(port->hub, exch, &port->base); 113 if (err != EOK) { 114 port_log(error, port, "Failed to reserve default address: %s", str_error(err)); 115 goto out_exch; 116 } 117 118 /* Reservation of default address could have blocked */ 119 if (port->base.state != PORT_CONNECTING) 120 goto out_address; 121 122 port_log(debug, port, "Got default address. Resetting port."); 194 123 int rc = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET); 195 124 if (rc != EOK) { 196 125 port_log(warning, port, "Port reset request failed: %s", str_error(rc)); 197 return rc; 198 } 199 200 fibril_condvar_wait_timeout(&port->state_cv, &port->guard, 2000000); 201 return port->state == PORT_ENABLED ? EOK : ESTALL; 202 } 203 204 /** 205 * Routine for adding a new device. 206 * 207 * Separate fibril is needed because the operation blocks on waiting for 208 * requesting default address and resetting port, and we must not block the 209 * control pipe. 210 */ 211 static void setup_device(usb_hub_port_t *port) 212 { 213 int err; 214 215 fibril_mutex_lock(&port->guard); 216 217 if (port->state == PORT_ERROR) { 218 /* 219 * The device was removed faster than this fibril acquired the 220 * mutex. 221 */ 222 port_change_state(port, PORT_DISABLED); 223 goto out; 224 } 225 226 if (port->state != PORT_CONNECTED) { 227 /* 228 * Another fibril already took care of the device. 229 * This may happen for example when the connection is unstable 230 * and a sequence of connect, disconnect and connect come 231 * faster the first fibril manages to request the default 232 * address. 233 */ 234 goto out; 235 } 236 237 port_log(debug, port, "Setting up new device."); 238 239 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device); 240 if (!exch) { 241 port_log(error, port, "Failed to create exchange."); 242 goto out; 243 } 244 245 /* Reserve default address */ 246 err = usb_hub_reserve_default_address(port->hub, exch, &port->guard); 247 if (err != EOK) { 248 port_log(error, port, "Failed to reserve default address: %s", str_error(err)); 249 port_change_state(port, PORT_DISABLED); 250 goto out_exch; 251 } 252 253 /* Reservation of default address could have blocked */ 254 if (port->state != PORT_CONNECTED) { 255 assert(port->state == PORT_ERROR); 256 port_change_state(port, PORT_DISABLED); 257 goto out_exch; 258 } 259 260 port_log(debug, port, "Got default address. Resetting port."); 261 262 if ((err = port_reset_sync(port))) { 263 port_log(error, port, "Failed to reset port."); 264 port_change_state(port, PORT_DISABLED); 265 goto out_address; 266 } 267 268 assert(port->state == PORT_ENABLED); 126 goto out_address; 127 } 128 129 if ((err = usb_port_wait_for_enabled(&port->base))) { 130 port_log(error, port, "Failed to reset port: %s", str_error(err)); 131 goto out_address; 132 } 269 133 270 134 port_log(debug, port, "Port reset, enumerating device."); 271 135 272 if ((err = usbhc_device_enumerate(exch, port->port_number, port-> speed))) {136 if ((err = usbhc_device_enumerate(exch, port->port_number, port->base.speed))) { 273 137 port_log(error, port, "Failed to enumerate device: %s", str_error(err)); 274 port_change_state(port, PORT_DISABLED); 275 goto out_port; 138 /* Disable the port */ 139 usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_ENABLE); 140 goto out_address; 276 141 } 277 142 278 143 port_log(debug, port, "Device enumerated"); 279 144 280 out_port:281 if (port->state != PORT_ENABLED)282 usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_C_PORT_ENABLE);283 145 out_address: 284 146 usb_hub_release_default_address(port->hub, exch); 285 147 out_exch: 286 148 usb_device_bus_exchange_end(exch); 287 out: 288 assert(port->state == PORT_ENABLED || port->state == PORT_DISABLED); 289 fibril_mutex_unlock(&port->guard); 290 } 291 292 static int setup_device_worker(void *arg) 293 { 294 setup_device(arg); 295 return EOK; 296 } 297 298 /** Start device adding when connection change is detected. 299 * 300 * This fires a new fibril to complete the device addition. 301 * 302 * @param hub Hub where the change occured. 303 * @param port Port index (starting at 1). 304 * @param speed Speed of the device. 305 * @return Error code. 306 */ 307 static int create_setup_device_fibril(usb_hub_port_t *port) 308 { 309 assert(port); 310 311 fid_t fibril = fibril_create(setup_device_worker, port); 312 if (!fibril) 313 return ENOMEM; 314 315 fibril_add_ready(fibril); 316 return EOK; 149 return err; 317 150 } 318 151 … … 323 156 324 157 if (connected) { 325 if (port->state == PORT_ENABLED) 326 port_log(warning, port, "Connection detected on port that is currently enabled. Resetting."); 327 328 port_make_disabled(port); 329 port_change_state(port, PORT_CONNECTED); 330 port->speed = usb_port_speed(status); 331 create_setup_device_fibril(port); 158 usb_port_connected(&port->base, &enumerate_device); 332 159 } else { 333 port_make_disabled(port);160 usb_port_disabled(&port->base, &remove_device); 334 161 } 335 162 } … … 341 168 port_log(warning, port, "Port unexpectedly changed to enabled."); 342 169 } else { 343 port_make_disabled(port);170 usb_port_disabled(&port->base, &remove_device); 344 171 } 345 172 } … … 360 187 * back on when the over-current condition is gone */ 361 188 362 port_make_disabled(port);189 usb_port_disabled(&port->base, &remove_device); 363 190 364 191 if (!overcurrent) { … … 373 200 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED); 374 201 375 /* Check if someone is waiting for the result */ 376 if (port->state != PORT_IN_RESET) 377 return; 378 379 port_change_state(port, enabled ? PORT_ENABLED : PORT_ERROR); 202 if (enabled) 203 usb_port_enabled(&port->base, usb_port_speed(status)); 204 else 205 usb_port_disabled(&port->base, &remove_device); 380 206 } 381 207 … … 398 224 * @param hub hub representation 399 225 */ 400 void usb_hub_port_process_interrupt(usb_hub_port_t *port , usb_hub_dev_t *hub)226 void usb_hub_port_process_interrupt(usb_hub_port_t *port) 401 227 { 402 228 assert(port); 403 assert(hub);404 229 port_log(debug2, port, "Interrupt."); 405 230 … … 411 236 } 412 237 413 fibril_mutex_lock(&port->guard);414 415 238 for (uint32_t feature = 0; feature < sizeof(usb_port_status_t) * 8; ++feature) { 416 239 uint32_t mask = 1 << feature; … … 429 252 } 430 253 431 fibril_mutex_unlock(&port->guard);432 433 254 port_log(debug2, port, "Port status after handling: %#08" PRIx32, status); 434 255 } -
uspace/drv/bus/usb/usbhub/port.h
r8ad2b0a r94f8c363 33 33 */ 34 34 /** @file 35 * Hub port state machine.35 * Hub port handling. 36 36 */ 37 37 … … 41 41 #include <usb/dev/driver.h> 42 42 #include <usb/classes/hub.h> 43 #include <usb/port.h> 43 44 44 45 typedef struct usb_hub_dev usb_hub_dev_t; 45 46 46 typedef enum {47 PORT_DISABLED, /* No device connected. */48 PORT_CONNECTED, /* A device connected, not yet initialized. */49 PORT_IN_RESET, /* An initial port reset in progress. */50 PORT_ENABLED, /* Port reset complete, port enabled. Device announced to the HC. */51 PORT_ERROR, /* An error occured. There is still a fibril that needs to know it. */52 } port_state_t;53 54 47 /** Information about single port on a hub. */ 55 48 typedef struct { 49 usb_port_t base; 56 50 /* Parenting hub */ 57 51 usb_hub_dev_t *hub; 58 /** Guarding all fields */59 fibril_mutex_t guard;60 /** Current state of the port */61 port_state_t state;62 /** A speed of the device connected (if any). Valid unless state == PORT_DISABLED. */63 usb_speed_t speed;64 52 /** Port number as reported in descriptors. */ 65 53 unsigned int port_number; 66 /** CV for waiting to port reset completion. */67 fibril_condvar_t state_cv;68 54 } usb_hub_port_t; 69 55 70 56 void usb_hub_port_init(usb_hub_port_t *, usb_hub_dev_t *, unsigned int); 71 void usb_hub_port_fini(usb_hub_port_t *);72 57 73 void usb_hub_port_process_interrupt(usb_hub_port_t *port , usb_hub_dev_t *hub);58 void usb_hub_port_process_interrupt(usb_hub_port_t *port); 74 59 75 60 #endif -
uspace/drv/bus/usb/usbhub/usbhub.c
r8ad2b0a r94f8c363 200 200 201 201 for (size_t port = 0; port < hub->port_count; ++port) { 202 usb_ hub_port_fini(&hub->ports[port]);202 usb_port_fini(&hub->ports[port].base); 203 203 } 204 204 free(hub->ports); … … 291 291 const bool change = (change_bitmap[bit / 8] >> (bit % 8)) & 1; 292 292 if (change) { 293 usb_hub_port_process_interrupt(&hub->ports[port] , hub);293 usb_hub_port_process_interrupt(&hub->ports[port]); 294 294 } 295 295 } … … 605 605 } 606 606 607 static FIBRIL_CONDVAR_INITIALIZE(global_hub_default_address_cv); 608 607 609 /** 608 610 * Reserve a default address for a port across all other devices connected to … … 611 613 * is connected with already attached devices. 612 614 */ 613 int usb_hub_reserve_default_address(usb_hub_dev_t *hub, async_exch_t *exch, fibril_mutex_t *guard)615 int usb_hub_reserve_default_address(usb_hub_dev_t *hub, async_exch_t *exch, usb_port_t *port) 614 616 { 615 617 assert(hub); 616 618 assert(exch); 617 assert( guard);618 assert(fibril_mutex_is_locked( guard));619 assert(port); 620 assert(fibril_mutex_is_locked(&port->guard)); 619 621 620 622 fibril_mutex_lock(&hub->default_address_guard); … … 624 626 int err; 625 627 while ((err = usbhc_reserve_default_address(exch)) == EAGAIN) { 626 fibril_mutex_unlock(guard); 627 async_usleep(500000); 628 fibril_mutex_lock(guard); 628 // We ignore the return value here, as we cannot give up now. 629 usb_port_condvar_wait_timeout(port, &global_hub_default_address_cv, 500000); 629 630 } 630 631 return err; 631 632 } else { 632 633 /* Drop the port guard, we're going to wait */ 633 fibril_mutex_unlock( guard);634 fibril_mutex_unlock(&port->guard); 634 635 635 636 /* Wait for a signal */ … … 638 639 /* Remember ABBA, first drop the hub guard */ 639 640 fibril_mutex_unlock(&hub->default_address_guard); 640 fibril_mutex_lock( guard);641 fibril_mutex_lock(&port->guard); 641 642 return EOK; 642 643 } … … 655 656 // from requesting the address before we release 656 657 ret = usbhc_release_default_address(exch); 658 // This is optimistic optimization - it may wake one hub from polling sleep 659 fibril_condvar_signal(&global_hub_default_address_cv); 657 660 } else { 658 661 fibril_condvar_signal(&hub->default_address_cv); -
uspace/drv/bus/usb/usbhub/usbhub.h
r8ad2b0a r94f8c363 89 89 bool hub_port_changes_callback(usb_device_t *, uint8_t *, size_t, void *); 90 90 91 int usb_hub_reserve_default_address(usb_hub_dev_t *, async_exch_t *, fibril_mutex_t *);91 int usb_hub_reserve_default_address(usb_hub_dev_t *, async_exch_t *, usb_port_t *); 92 92 int usb_hub_release_default_address(usb_hub_dev_t *, async_exch_t *); 93 93 -
uspace/lib/usb/Makefile
r8ad2b0a r94f8c363 36 36 src/debug.c \ 37 37 src/dump.c \ 38 src/port.c \ 38 39 src/usb.c 39 40
Note:
See TracChangeset
for help on using the changeset viewer.