Changeset c4e84ed6 in mainline
- Timestamp:
- 2018-01-16T03:45:38Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- c952abc4
- Parents:
- d2c3dcd
- git-author:
- Ondřej Hlavatý <aearsis@…> (2018-01-15 20:49:15)
- git-committer:
- Ondřej Hlavatý <aearsis@…> (2018-01-16 03:45:38)
- Location:
- uspace/drv/bus/usb/usbhub
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/usbhub/port.c
rd2c3dcd rc4e84ed6 48 48 #include "status.h" 49 49 50 /** Information for fibril for device discovery. */ 51 struct add_device_phase1 { 52 usb_hub_dev_t *hub; 53 usb_hub_port_t *port; 54 usb_speed_t speed; 55 }; 56 57 static int usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub); 58 static void usb_hub_port_reset_completed(usb_hub_port_t *port, 59 usb_hub_dev_t *hub, usb_port_status_t status); 60 static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status); 61 static int add_device_phase1_worker_fibril(void *arg); 62 static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub, 63 usb_speed_t speed); 64 65 int usb_hub_port_fini(usb_hub_port_t *port, usb_hub_dev_t *hub) 50 #define port_log(lvl, port, fmt, ...) do { usb_log_##lvl("(%p-%u): " fmt, (port->hub), (port->port_number), ##__VA_ARGS__); } while (0) 51 52 /** Initialize hub port information. 53 * 54 * @param port Port to be initialized. 55 */ 56 void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub, unsigned int port_number) 66 57 { 67 58 assert(port); 68 if (port->device_attached) 69 return usb_hub_port_device_gone(port, hub); 70 return EOK; 71 } 72 73 /** 74 * Clear feature on hub port. 75 * 76 * @param port Port structure. 77 * @param feature Feature selector. 78 * @return Operation result 79 */ 80 int usb_hub_port_clear_feature( 81 usb_hub_port_t *port, usb_hub_class_feature_t feature) 59 memset(port, 0, sizeof(*port)); 60 fibril_mutex_initialize(&port->guard); 61 fibril_condvar_initialize(&port->state_cv); 62 port->hub = hub; 63 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); 93 } 94 95 /** 96 * Inform the HC that the device on port is gone. 97 */ 98 static int usb_hub_port_device_gone(usb_hub_port_t *port) 82 99 { 83 100 assert(port); 84 const usb_device_request_setup_packet_t clear_request = { 85 .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE, 86 .request = USB_DEVREQ_CLEAR_FEATURE, 87 .value = feature, 88 .index = port->port_number, 89 .length = 0, 90 }; 91 return usb_pipe_control_write(port->control_pipe, &clear_request, 92 sizeof(clear_request), NULL, 0); 93 } 94 95 /** 96 * Set feature on hub port. 97 * 98 * @param port Port structure. 99 * @param feature Feature selector. 100 * @return Operation result 101 */ 102 int usb_hub_port_set_feature( 103 usb_hub_port_t *port, usb_hub_class_feature_t feature) 104 { 105 assert(port); 106 const usb_device_request_setup_packet_t clear_request = { 107 .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE, 108 .request = USB_DEVREQ_SET_FEATURE, 109 .index = port->port_number, 110 .value = feature, 111 .length = 0, 112 }; 113 return usb_pipe_control_write(port->control_pipe, &clear_request, 114 sizeof(clear_request), NULL, 0); 115 } 116 117 /** 118 * Mark reset process as failed due to external reasons 119 * 120 * @param port Port structure 121 */ 122 void usb_hub_port_reset_fail(usb_hub_port_t *port) 123 { 124 assert(port); 125 fibril_mutex_lock(&port->mutex); 126 if (port->reset_status == IN_RESET) 127 port->reset_status = RESET_FAIL; 128 fibril_condvar_broadcast(&port->reset_cv); 129 fibril_mutex_unlock(&port->mutex); 130 } 131 132 /** 133 * Process interrupts on given port 134 * 135 * Accepts connection, over current and port reset change. 136 * @param port port structure 137 * @param hub hub representation 138 */ 139 void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub) 140 { 141 assert(port); 142 assert(hub); 143 usb_log_debug2("(%p-%u): Interrupt.", hub, port->port_number); 144 145 usb_port_status_t status = 0; 146 const int opResult = get_port_status(port, &status); 147 if (opResult != EOK) { 148 usb_log_error("(%p-%u): Failed to get port status: %s.", hub, 149 port->port_number, str_error(opResult)); 150 return; 151 } 152 153 /* Connection change */ 154 if (status & USB_HUB_PORT_C_STATUS_CONNECTION) { 155 const bool connected = 156 (status & USB_HUB_PORT_STATUS_CONNECTION) != 0; 157 usb_log_debug("(%p-%u): Connection change: device %s.", hub, 158 port->port_number, connected ? "attached" : "removed"); 159 160 /* ACK the change */ 161 const int opResult = usb_hub_port_clear_feature(port, 162 USB_HUB_FEATURE_C_PORT_CONNECTION); 163 if (opResult != EOK) { 164 usb_log_warning("(%p-%u): Failed to clear " 165 "port-change-connection flag: %s.\n", hub, 166 port->port_number, str_error(opResult)); 167 } 168 169 if (connected) { 170 const int opResult = create_add_device_fibril(port, hub, 171 usb_port_speed(status)); 172 if (opResult != EOK) { 173 usb_log_error("(%p-%u): Cannot handle change on" 174 " port: %s.\n", hub, port->port_number, 175 str_error(opResult)); 176 } 177 } else { 178 /* Handle the case we were in reset */ 179 // FIXME: usb_hub_port_reset_fail(port); 180 /* If enabled change was reported leave the removal 181 * to that handler, it shall ACK the change too. */ 182 if (!(status & USB_HUB_PORT_C_STATUS_ENABLED)) { 183 usb_hub_port_device_gone(port, hub); 184 } 185 } 186 } 187 188 /* Enable change, ports are automatically disabled on errors. */ 189 if (status & USB_HUB_PORT_C_STATUS_ENABLED) { 190 // TODO: maybe HS reset failed? 191 usb_log_info("(%p-%u): Port disabled because of errors.", hub, 192 port->port_number); 193 usb_hub_port_device_gone(port, hub); 194 const int rc = usb_hub_port_clear_feature(port, 195 USB_HUB_FEATURE_C_PORT_ENABLE); 196 if (rc != EOK) { 197 usb_log_error("(%p-%u): Failed to clear port enable " 198 "change feature: %s.", hub, port->port_number, 199 str_error(rc)); 200 } 201 202 } 203 204 /* Suspend change */ 205 if (status & USB_HUB_PORT_C_STATUS_SUSPEND) { 206 usb_log_error("(%p-%u): Port went to suspend state, this should" 207 " NOT happen as we do not support suspend state!", hub, 208 port->port_number); 209 const int rc = usb_hub_port_clear_feature(port, 210 USB_HUB_FEATURE_C_PORT_SUSPEND); 211 if (rc != EOK) { 212 usb_log_error("(%p-%u): Failed to clear port suspend " 213 "change feature: %s.", hub, port->port_number, 214 str_error(rc)); 215 } 216 } 217 218 /* Over current */ 219 if (status & USB_HUB_PORT_C_STATUS_OC) { 220 usb_log_debug("(%p-%u): Port OC reported!.", hub, 221 port->port_number); 222 /* According to the USB specs: 223 * 11.13.5 Over-current Reporting and Recovery 224 * Hub device is responsible for putting port in power off 225 * mode. USB system software is responsible for powering port 226 * back on when the over-current condition is gone */ 227 const int rc = usb_hub_port_clear_feature(port, 228 USB_HUB_FEATURE_C_PORT_OVER_CURRENT); 229 if (rc != EOK) { 230 usb_log_error("(%p-%u): Failed to clear port OC change " 231 "feature: %s.\n", hub, port->port_number, 232 str_error(rc)); 233 } 234 if (!(status & ~USB_HUB_PORT_STATUS_OC)) { 235 const int rc = usb_hub_port_set_feature( 236 port, USB_HUB_FEATURE_PORT_POWER); 237 if (rc != EOK) { 238 usb_log_error("(%p-%u): Failed to set port " 239 "power after OC: %s.", hub, 240 port->port_number, str_error(rc)); 241 } 242 } 243 } 244 245 /* Port reset, set on port reset complete. */ 246 if (status & USB_HUB_PORT_C_STATUS_RESET) { 247 usb_hub_port_reset_completed(port, hub, status); 248 } 249 250 usb_log_debug2("(%p-%u): Port status %#08" PRIx32, hub, 251 port->port_number, status); 252 } 253 254 /** 255 * routine called when a device on port has been removed 256 * 257 * If the device on port had default address, it releases default address. 258 * Otherwise does not do anything, because DDF does not allow to remove device 259 * from it`s device tree. 260 * @param port port structure 261 * @param hub hub representation 262 */ 263 int usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub) 264 { 265 assert(port); 266 assert(hub); 267 async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device); 101 assert(fibril_mutex_is_locked(&port->guard)); 102 assert(port->state == PORT_ENABLED); 103 104 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device); 268 105 if (!exch) 269 106 return ENOMEM; 270 107 const int rc = usbhc_device_remove(exch, port->port_number); 271 108 usb_device_bus_exchange_end(exch); 272 if (rc == EOK)273 port->device_attached = false;274 109 return rc; 275 276 } 277 278 /** 279 * Process port reset change 280 * 281 * After this change port should be enabled, unless some problem occurred. 282 * This functions triggers second phase of enabling new device. 283 * @param port Port structure 284 * @param status Port status mask 285 */ 286 void usb_hub_port_reset_completed(usb_hub_port_t *port, usb_hub_dev_t *hub, 287 usb_port_status_t status) 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) 288 153 { 289 154 assert(port); 290 fibril_mutex_lock(&port->mutex); 291 const bool enabled = (status & USB_HUB_PORT_STATUS_ENABLED) != 0; 292 /* Finalize device adding. */ 293 294 if (enabled) { 295 port->reset_status = RESET_OK; 296 usb_log_debug("(%p-%u): Port reset complete.", hub, 297 port->port_number); 298 } else { 299 port->reset_status = RESET_FAIL; 300 usb_log_warning("(%p-%u): Port reset complete but port not " 301 "enabled.", hub, port->port_number); 302 } 303 fibril_condvar_broadcast(&port->reset_cv); 304 fibril_mutex_unlock(&port->mutex); 305 306 /* Clear the port reset change. */ 307 int rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET); 155 fibril_mutex_lock(&port->guard); 156 port_make_disabled(port); 157 port_log(debug, port, "Finalized."); 158 fibril_mutex_unlock(&port->guard); 159 } 160 161 static int port_reset_sync(usb_hub_port_t *port) 162 { 163 assert(fibril_mutex_is_locked(&port->guard)); 164 assert(port->state == PORT_CONNECTED); 165 166 port_change_state(port, PORT_IN_RESET); 167 port_log(debug2, port, "Issuing reset."); 168 int rc = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET); 308 169 if (rc != EOK) { 309 usb_log_error("(%p-%u): Failed to clear port reset change: %s.", 310 hub, port->port_number, str_error(rc)); 311 } 312 } 313 314 /** Retrieve port status. 315 * 316 * @param[in] port Port structure 317 * @param[out] status Where to store the port status. 318 * @return Error code. 319 */ 320 static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status) 321 { 322 assert(port); 323 /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6 324 * Generic GET_STATUS request cannot be used because of the difference 325 * in status data size (2B vs. 4B)*/ 326 const usb_device_request_setup_packet_t request = { 327 .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS, 328 .request = USB_HUB_REQUEST_GET_STATUS, 329 .value = 0, 330 .index = uint16_host2usb(port->port_number), 331 .length = sizeof(usb_port_status_t), 332 }; 333 size_t recv_size; 334 usb_port_status_t status_tmp; 335 336 const int rc = usb_pipe_control_read(port->control_pipe, 337 &request, sizeof(usb_device_request_setup_packet_t), 338 &status_tmp, sizeof(status_tmp), &recv_size); 339 if (rc != EOK) { 170 port_log(warning, port, "Port reset request failed: %s", str_error(rc)); 340 171 return rc; 341 172 } 342 173 343 if (recv_size != sizeof (status_tmp)) { 344 return ELIMIT; 345 } 346 347 if (status != NULL) { 348 *status = status_tmp; 349 } 350 174 fibril_condvar_wait_timeout(&port->state_cv, &port->guard, 2000000); 175 return port->state == PORT_ENABLED ? EOK : ESTALL; 176 } 177 178 /** 179 * Routine for adding a new device. 180 * 181 * Separate fibril is needed because the operation blocks on waiting for 182 * requesting default address and resetting port, and we must not block the 183 * control pipe. 184 */ 185 static void setup_device(usb_hub_port_t *port) 186 { 187 int err; 188 189 fibril_mutex_lock(&port->guard); 190 191 if (port->state == PORT_ERROR) { 192 /* 193 * The device was removed faster than this fibril acquired the 194 * mutex. 195 */ 196 port_change_state(port, PORT_DISABLED); 197 goto out; 198 } 199 200 if (port->state != PORT_CONNECTED) { 201 /* 202 * Another fibril already took care of the device. 203 * This may happen for example when the connection is unstable 204 * and a sequence of connect, disconnect and connect come 205 * faster the first fibril manages to request the default 206 * address. 207 */ 208 goto out; 209 } 210 211 port_log(debug, port, "Setting up new device."); 212 213 async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device); 214 if (!exch) { 215 port_log(error, port, "Failed to create exchange."); 216 goto out; 217 } 218 219 /* Reserve default address 220 * TODO: Make the request synchronous. 221 */ 222 while ((err = usbhc_reserve_default_address(exch, port->speed)) == EAGAIN) { 223 fibril_condvar_wait_timeout(&port->state_cv, &port->guard, 500000); 224 if (port->state != PORT_CONNECTED) { 225 assert(port->state == PORT_ERROR); 226 port_change_state(port, PORT_DISABLED); 227 goto out_exch; 228 } 229 } 230 if (err != EOK) { 231 port_log(error, port, "Failed to reserve default address: %s", str_error(err)); 232 goto out_exch; 233 } 234 235 port_log(debug, port, "Got default address. Resetting port."); 236 237 if ((err = port_reset_sync(port))) { 238 port_log(error, port, "Failed to reset port."); 239 port_change_state(port, PORT_DISABLED); 240 goto out_address; 241 } 242 243 assert(port->state == PORT_ENABLED); 244 245 port_log(debug, port, "Port reset, enumerating device."); 246 247 if ((err = usbhc_device_enumerate(exch, port->port_number))) { 248 port_log(error, port, "Failed to enumerate device: %s", str_error(err)); 249 port_change_state(port, PORT_DISABLED); 250 goto out_port; 251 } 252 253 port_log(debug, port, "Device enumerated"); 254 255 out_port: 256 if (port->state != PORT_ENABLED) 257 usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_C_PORT_ENABLE); 258 259 out_address: 260 if ((err = usbhc_release_default_address(exch))) 261 port_log(error, port, "Failed to release default address: %s", str_error(err)); 262 263 out_exch: 264 usb_device_bus_exchange_end(exch); 265 266 out: 267 assert(port->state == PORT_ENABLED || port->state == PORT_DISABLED); 268 fibril_mutex_unlock(&port->guard); 269 } 270 271 static int setup_device_worker(void *arg) 272 { 273 setup_device(arg); 351 274 return EOK; 352 }353 354 static int port_enable(usb_hub_port_t *port, usb_hub_dev_t *hub, bool enable)355 {356 if (enable) {357 int rc =358 usb_hub_port_set_feature(port, USB_HUB_FEATURE_PORT_RESET);359 if (rc != EOK) {360 usb_log_error("(%p-%u): Port reset request failed: %s.",361 hub, port->port_number, str_error(rc));362 return rc;363 }364 /* Wait until reset completes. */365 fibril_mutex_lock(&port->mutex);366 port->reset_status = IN_RESET;367 while (port->reset_status == IN_RESET)368 fibril_condvar_wait(&port->reset_cv, &port->mutex);369 rc = port->reset_status == RESET_OK ? EOK : ESTALL;370 fibril_mutex_unlock(&port->mutex);371 return rc;372 } else {373 return usb_hub_port_clear_feature(port,374 USB_HUB_FEATURE_PORT_ENABLE);375 }376 }377 378 /** Fibril for adding a new device.379 *380 * Separate fibril is needed because the port reset completion is announced381 * via interrupt pipe and thus we cannot block here.382 *383 * @param arg Pointer to struct add_device_phase1.384 * @return 0 Always.385 */386 int add_device_phase1_worker_fibril(void *arg)387 {388 struct add_device_phase1 *params = arg;389 assert(params);390 391 bool release_default_address = false;392 393 int ret = EOK;394 usb_hub_dev_t *hub = params->hub;395 usb_hub_port_t *port = params->port;396 const usb_speed_t speed = params->speed;397 free(arg);398 399 usb_log_debug("(%p-%u): New device sequence.", hub, port->port_number);400 401 async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);402 if (!exch) {403 usb_log_error("(%p-%u): Failed to begin bus exchange.", hub,404 port->port_number);405 ret = ENOMEM;406 goto out;407 }408 409 /* Reserve default address */410 while ((ret = usbhc_reserve_default_address(exch, speed)) == EAGAIN) {411 async_usleep(1000000);412 }413 if (ret != EOK) {414 usb_log_error("(%p-%u): Failed to reserve default address: %s", hub, port->port_number, str_error(ret));415 goto out;416 }417 release_default_address = true;418 419 usb_log_debug("(%p-%u): Got default address. Reseting port.", hub, port->port_number);420 421 /* Reset port */422 if ((ret = port_enable(port, hub, true))) {423 usb_log_error("(%p-%u): Failed to reset port.", hub, port->port_number);424 ret = EIO;425 goto out_address;426 }427 428 usb_log_debug("(%p-%u): Port reset, enumerating device", hub, port->port_number);429 430 if ((ret = usbhc_device_enumerate(exch, port->port_number))) {431 usb_log_error("(%p-%u): Failed to enumerate device: %s", hub, port->port_number, str_error(ret));432 goto out_port;433 }434 435 usb_log_debug("(%p-%u): Device enumerated", hub, port->port_number);436 port->device_attached = true;437 438 out_port:439 if (!port->device_attached && (ret = port_enable(port, hub, false))) {440 usb_log_warning("(%p-%u)Failed to disable port (%s), NOT releasing default address.", hub, port->port_number, str_error(ret));441 release_default_address = false;442 }443 444 out_address:445 if (release_default_address && (ret = usbhc_release_default_address(exch)))446 usb_log_warning("(%p-%u): Failed to release default address: %s", hub, port->port_number, str_error(ret));447 448 out:449 usb_device_bus_exchange_end(exch);450 451 fibril_mutex_lock(&hub->pending_ops_mutex);452 assert(hub->pending_ops_count > 0);453 --hub->pending_ops_count;454 fibril_condvar_signal(&hub->pending_ops_cv);455 fibril_mutex_unlock(&hub->pending_ops_mutex);456 457 return ret;458 275 } 459 276 … … 467 284 * @return Error code. 468 285 */ 469 static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub, 470 usb_speed_t speed) 471 { 286 static int create_setup_device_fibril(usb_hub_port_t *port) 287 { 288 assert(port); 289 290 fid_t fibril = fibril_create(setup_device_worker, port); 291 if (!fibril) 292 return ENOMEM; 293 294 fibril_add_ready(fibril); 295 return EOK; 296 } 297 298 static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status) 299 { 300 const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION); 301 port_log(debug, port, "Connection change: device %s.", connected ? "attached" : "removed"); 302 303 if (connected) { 304 if (port->state == PORT_ENABLED) 305 port_log(warning, port, "Connection detected on port that is currently enabled. Resetting."); 306 307 port_make_disabled(port); 308 port_change_state(port, PORT_CONNECTED); 309 port->speed = usb_port_speed(status); 310 create_setup_device_fibril(port); 311 } else { 312 port_make_disabled(port); 313 } 314 } 315 316 static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status) 317 { 318 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED); 319 if (enabled) { 320 port_log(warning, port, "Port unexpectedly changed to enabled."); 321 } else { 322 port_make_disabled(port); 323 } 324 } 325 326 static void port_changed_suspend(usb_hub_port_t *port, usb_port_status_t status) 327 { 328 port_log(error, port, "Port unexpectedly suspend. Weird, we do not support suspending!"); 329 } 330 331 static void port_changed_overcurrent(usb_hub_port_t *port, usb_port_status_t status) 332 { 333 const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC); 334 335 /* According to the USB specs: 336 * 11.13.5 Over-current Reporting and Recovery 337 * Hub device is responsible for putting port in power off 338 * mode. USB system software is responsible for powering port 339 * back on when the over-current condition is gone */ 340 341 port_make_disabled(port); 342 343 if (!overcurrent) { 344 const int err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_POWER); 345 if (err) 346 port_log(error, port, "Failed to set port power after OC: %s.", str_error(err)); 347 } 348 } 349 350 static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status) 351 { 352 const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED); 353 354 /* Check if someone is waiting for the result */ 355 if (port->state != PORT_IN_RESET) 356 return; 357 358 port_change_state(port, enabled ? PORT_ENABLED : PORT_ERROR); 359 } 360 361 typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t); 362 363 static const change_handler_t port_change_handlers [] = { 364 [USB_HUB_FEATURE_C_PORT_CONNECTION] = &port_changed_connection, 365 [USB_HUB_FEATURE_C_PORT_ENABLE] = &port_changed_enabled, 366 [USB_HUB_FEATURE_C_PORT_SUSPEND] = &port_changed_suspend, 367 [USB_HUB_FEATURE_C_PORT_OVER_CURRENT] = &port_changed_overcurrent, 368 [USB_HUB_FEATURE_C_PORT_RESET] = &port_changed_reset, 369 [sizeof(usb_port_status_t) * 8] = NULL, 370 }; 371 372 /** 373 * Process interrupts on given port 374 * 375 * Accepts connection, over current and port reset change. 376 * @param port port structure 377 * @param hub hub representation 378 */ 379 void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub) 380 { 381 assert(port); 472 382 assert(hub); 473 assert(port); 474 struct add_device_phase1 *data 475 = malloc(sizeof(struct add_device_phase1)); 476 if (data == NULL) { 477 return ENOMEM; 478 } 479 data->hub = hub; 480 data->port = port; 481 data->speed = speed; 482 483 fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data); 484 if (fibril == 0) { 485 free(data); 486 return ENOMEM; 487 } 488 fibril_mutex_lock(&hub->pending_ops_mutex); 489 ++hub->pending_ops_count; 490 fibril_mutex_unlock(&hub->pending_ops_mutex); 491 fibril_add_ready(fibril); 492 493 return EOK; 494 } 383 port_log(debug2, port, "Interrupt."); 384 385 usb_port_status_t status = 0; 386 const int err = usb_hub_get_port_status(port->hub, port->port_number, &status); 387 if (err != EOK) { 388 port_log(error, port, "Failed to get port status: %s.", str_error(err)); 389 return; 390 } 391 392 fibril_mutex_lock(&port->guard); 393 394 for (uint32_t feature = 0; feature < sizeof(usb_port_status_t) * 8; ++feature) { 395 uint32_t mask = 1 << feature; 396 397 if ((status & mask) == 0) 398 continue; 399 400 if (!port_change_handlers[feature]) 401 continue; 402 403 /* ACK this change */ 404 status &= ~mask; 405 usb_hub_clear_port_feature(port->hub, port->port_number, feature); 406 407 port_change_handlers[feature](port, status); 408 } 409 410 fibril_mutex_unlock(&port->guard); 411 412 port_log(debug2, port, "Port status after handling: %#08" PRIx32, status); 413 } 414 495 415 496 416 /** -
uspace/drv/bus/usb/usbhub/port.h
rd2c3dcd rc4e84ed6 2 2 * Copyright (c) 2011 Vojtech Horky 3 3 * Copyright (c) 2011 Jan Vesely 4 * Copyright (c) 2017 Ondra Hlavaty 4 5 * All rights reserved. 5 6 * … … 32 33 */ 33 34 /** @file 34 * Hub port s related functions.35 * Hub port state machine. 35 36 */ 36 37 … … 43 44 typedef struct usb_hub_dev usb_hub_dev_t; 44 45 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 45 54 /** Information about single port on a hub. */ 46 55 typedef struct { 56 /* Parenting hub */ 57 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; 47 64 /** Port number as reported in descriptors. */ 48 65 unsigned int port_number; 49 /** Device communication pipe. */50 usb_pipe_t *control_pipe;51 /** Mutex needed not only by CV for checking port reset. */52 fibril_mutex_t mutex;53 66 /** CV for waiting to port reset completion. */ 54 fibril_condvar_t reset_cv; 55 /** Port reset status. 56 * Guarded by @c reset_mutex. 57 */ 58 enum { 59 NO_RESET, 60 IN_RESET, 61 RESET_OK, 62 RESET_FAIL, 63 } reset_status; 64 /** Device reported to USB bus driver */ 65 bool device_attached; 67 fibril_condvar_t state_cv; 66 68 } usb_hub_port_t; 67 69 68 /** Initialize hub port information. 69 * 70 * @param port Port to be initialized. 71 */ 72 static inline void usb_hub_port_init(usb_hub_port_t *port, 73 unsigned int port_number, usb_pipe_t *control_pipe) 74 { 75 assert(port); 76 port->port_number = port_number; 77 port->control_pipe = control_pipe; 78 port->reset_status = NO_RESET; 79 port->device_attached = false; 80 fibril_mutex_initialize(&port->mutex); 81 fibril_condvar_initialize(&port->reset_cv); 82 } 70 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 *); 83 72 84 int usb_hub_port_fini(usb_hub_port_t *port, usb_hub_dev_t *hub);85 int usb_hub_port_clear_feature(86 usb_hub_port_t *port, usb_hub_class_feature_t feature);87 int usb_hub_port_set_feature(88 usb_hub_port_t *port, usb_hub_class_feature_t feature);89 void usb_hub_port_reset_fail(usb_hub_port_t *port);90 73 void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub); 91 74 -
uspace/drv/bus/usb/usbhub/usbhub.c
rd2c3dcd rc4e84ed6 86 86 usb_hub_status_t status); 87 87 static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev); 88 static void usb_hub_polling_terminated_callback(usb_device_t *device,89 bool was_error, void *data);90 88 91 89 static bool usb_hub_polling_error_callback(usb_device_t *dev, int err_code, void *arg) … … 93 91 assert(dev); 94 92 assert(arg); 95 usb_hub_dev_t *hub = arg; 96 97 usb_log_error("Device %s polling error: %s", usb_device_get_name(dev), 98 str_error(err_code)); 99 100 /* Continue polling until the device is about to be removed. */ 101 return hub->running; 93 94 usb_log_error("Device %s polling error: %s", usb_device_get_name(dev), str_error(err_code)); 95 96 return true; 102 97 } 103 98 … … 121 116 } 122 117 hub_dev->usb_device = usb_dev; 123 hub_dev->pending_ops_count = 0;124 hub_dev->running = false;125 fibril_mutex_initialize(&hub_dev->pending_ops_mutex);126 fibril_condvar_initialize(&hub_dev->pending_ops_cv);127 118 128 119 /* Set hub's first configuration. (There should be only one) */ … … 178 169 polling->buffer = malloc(polling->request_size); 179 170 polling->on_data = hub_port_changes_callback; 180 polling->on_polling_end = usb_hub_polling_terminated_callback;181 171 polling->on_error = usb_hub_polling_error_callback; 182 172 polling->arg = hub_dev; … … 194 184 } 195 185 196 hub_dev->running = true;197 186 usb_log_info("Controlling hub '%s' (%p: %zu ports).", 198 187 usb_device_get_name(hub_dev->usb_device), hub_dev, … … 204 193 static int usb_hub_cleanup(usb_hub_dev_t *hub) 205 194 { 206 assert(!hub->running);207 208 195 free(hub->polling.buffer); 209 196 usb_polling_fini(&hub->polling); 210 197 211 198 for (size_t port = 0; port < hub->port_count; ++port) { 212 const int ret = usb_hub_port_fini(&hub->ports[port], hub); 213 if (ret != EOK) 214 return ret; 199 usb_hub_port_fini(&hub->ports[port]); 215 200 } 216 201 free(hub->ports); … … 343 328 descriptor.port_count); 344 329 hub_dev->port_count = descriptor.port_count; 330 hub_dev->control_pipe = control_pipe; 345 331 346 332 hub_dev->ports = calloc(hub_dev->port_count, sizeof(usb_hub_port_t)); … … 350 336 351 337 for (size_t port = 0; port < hub_dev->port_count; ++port) { 352 usb_hub_port_init( 353 &hub_dev->ports[port], port + 1, control_pipe); 338 usb_hub_port_init(&hub_dev->ports[port], hub_dev, port + 1); 354 339 } 355 340 … … 370 355 for (unsigned int port = 0; port < hub_dev->port_count; ++port) { 371 356 usb_log_debug("(%p): Powering port %u.", hub_dev, port); 372 const int ret = usb_hub_port_set_feature( 373 &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER); 357 const int ret = usb_hub_set_port_feature(hub_dev, port, USB_HUB_FEATURE_PORT_POWER); 374 358 375 359 if (ret != EOK) { … … 459 443 /* Over-current condition is gone, it is safe to turn the ports on. */ 460 444 for (size_t port = 0; port < hub_dev->port_count; ++port) { 461 const int ret = usb_hub_port_set_feature( 462 &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER); 445 const int ret = usb_hub_set_port_feature(hub_dev, port, USB_HUB_FEATURE_PORT_POWER); 463 446 if (ret != EOK) { 464 447 usb_log_warning("(%p-%u): HUB OVER-CURRENT GONE: Cannot" … … 470 453 } 471 454 } 472 455 } 456 457 /** 458 * Set feature on the real hub port. 459 * 460 * @param port Port structure. 461 * @param feature Feature selector. 462 */ 463 int usb_hub_set_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature) 464 { 465 assert(hub); 466 const usb_device_request_setup_packet_t clear_request = { 467 .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE, 468 .request = USB_DEVREQ_SET_FEATURE, 469 .index = uint16_host2usb(port_number), 470 .value = feature, 471 .length = 0, 472 }; 473 return usb_pipe_control_write(hub->control_pipe, &clear_request, 474 sizeof(clear_request), NULL, 0); 475 } 476 477 /** 478 * Clear feature on the real hub port. 479 * 480 * @param port Port structure. 481 * @param feature Feature selector. 482 */ 483 int usb_hub_clear_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature) 484 { 485 assert(hub); 486 const usb_device_request_setup_packet_t clear_request = { 487 .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE, 488 .request = USB_DEVREQ_CLEAR_FEATURE, 489 .value = feature, 490 .index = uint16_host2usb(port_number), 491 .length = 0, 492 }; 493 return usb_pipe_control_write(hub->control_pipe, 494 &clear_request, sizeof(clear_request), NULL, 0); 495 } 496 497 /** 498 * Retrieve port status. 499 * 500 * @param[in] port Port structure 501 * @param[out] status Where to store the port status. 502 * @return Error code. 503 */ 504 int usb_hub_get_port_status(const usb_hub_dev_t *hub, size_t port_number, usb_port_status_t *status) 505 { 506 assert(hub); 507 assert(status); 508 509 /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6 510 * Generic GET_STATUS request cannot be used because of the difference 511 * in status data size (2B vs. 4B)*/ 512 const usb_device_request_setup_packet_t request = { 513 .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS, 514 .request = USB_HUB_REQUEST_GET_STATUS, 515 .value = 0, 516 .index = uint16_host2usb(port_number), 517 .length = sizeof(usb_port_status_t), 518 }; 519 size_t recv_size; 520 521 const int rc = usb_pipe_control_read(hub->control_pipe, 522 &request, sizeof(usb_device_request_setup_packet_t), 523 status, sizeof(*status), &recv_size); 524 if (rc != EOK) 525 return rc; 526 527 if (recv_size != sizeof(*status)) 528 return ELIMIT; 529 530 return EOK; 473 531 } 474 532 … … 545 603 546 604 /** 547 * callback called from hub polling fibril when the fibril terminates548 *549 * Does not perform cleanup, just marks the hub as not running.550 * @param device usb device afected551 * @param was_error indicates that the fibril is stoped due to an error552 * @param data pointer to usb_hub_dev_t structure553 */554 static void usb_hub_polling_terminated_callback(usb_device_t *device,555 bool was_error, void *data)556 {557 usb_hub_dev_t *hub = data;558 assert(hub);559 560 fibril_mutex_lock(&hub->pending_ops_mutex);561 562 /* The device is dead. However there might be some pending operations563 * that we need to wait for.564 * One of them is device adding in progress.565 * The respective fibril is probably waiting for status change566 * in port reset (port enable) callback.567 * Such change would never come (otherwise we would not be here).568 * Thus, we would flush all pending port resets.569 */570 if (hub->pending_ops_count > 0) {571 for (size_t port = 0; port < hub->port_count; ++port) {572 usb_hub_port_reset_fail(&hub->ports[port]);573 }574 }575 /* And now wait for them. */576 while (hub->pending_ops_count > 0) {577 fibril_condvar_wait(&hub->pending_ops_cv,578 &hub->pending_ops_mutex);579 }580 fibril_mutex_unlock(&hub->pending_ops_mutex);581 hub->running = false;582 }583 584 /**585 605 * @} 586 606 */ -
uspace/drv/bus/usb/usbhub/usbhub.h
rd2c3dcd rc4e84ed6 50 50 51 51 #include "port.h" 52 #include "status.h" 52 53 53 54 /** Information about attached hub. */ … … 61 62 /** Data polling handle. */ 62 63 usb_polling_t polling; 63 /** Number of pending operations on the mutex to prevent shooting64 * ourselves in the foot.65 * When the hub is disconnected but we are in the middle of some66 * operation, we cannot destroy this structure right away because67 * the pending operation might use it.68 */69 size_t pending_ops_count;70 /** Guard for pending_ops_count. */71 fibril_mutex_t pending_ops_mutex;72 /** Condition variable for pending_ops_count. */73 fibril_condvar_t pending_ops_cv;74 64 /** Pointer to usbhub function. */ 75 65 ddf_fun_t *hub_fun; 76 /** Status indicator*/77 volatile bool running;66 /** Device communication pipe. */ 67 usb_pipe_t *control_pipe; 78 68 /** Hub supports port power switching. */ 79 69 bool power_switched; … … 84 74 extern const usb_endpoint_description_t hub_status_change_endpoint_description; 85 75 86 externint usb_hub_device_add(usb_device_t *);87 externint usb_hub_device_remove(usb_device_t *);88 externint usb_hub_device_gone(usb_device_t *);76 int usb_hub_device_add(usb_device_t *); 77 int usb_hub_device_remove(usb_device_t *); 78 int usb_hub_device_gone(usb_device_t *); 89 79 90 extern bool hub_port_changes_callback(usb_device_t *, uint8_t *, size_t, 91 void *); 80 int usb_hub_get_port_status(const usb_hub_dev_t *, size_t, usb_port_status_t *); 81 int usb_hub_set_port_feature(const usb_hub_dev_t *, size_t, usb_hub_class_feature_t); 82 int usb_hub_clear_port_feature(const usb_hub_dev_t *, size_t, usb_hub_class_feature_t); 83 84 bool hub_port_changes_callback(usb_device_t *, uint8_t *, size_t, void *); 92 85 93 86 #endif
Note:
See TracChangeset
for help on using the changeset viewer.