Changes in uspace/drv/bus/usb/usbhub/usbhub.c [8895d05:fb2ef35] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/usbhub/usbhub.c
r8895d05 rfb2ef35 1 1 /* 2 2 * Copyright (c) 2010 Matus Dekanek 3 * Copyright (c) 2011 Jan Vesely 3 4 * All rights reserved. 4 5 * … … 38 39 #include <str_error.h> 39 40 #include <inttypes.h> 40 41 #include <usb_iface.h> 41 #include <stdio.h> 42 43 #include <usb/usb.h> 44 #include <usb/debug.h> 45 #include <usb/dev/pipes.h> 46 #include <usb/classes/classes.h> 42 47 #include <usb/ddfiface.h> 43 48 #include <usb/descriptor.h> … … 46 51 #include <usb/classes/hub.h> 47 52 #include <usb/dev/poll.h> 48 #include < stdio.h>53 #include <usb_iface.h> 49 54 50 55 #include "usbhub.h" 51 #include " usbhub_private.h"52 #include "port_status.h" 53 # include <usb/usb.h>54 #include <usb/dev/pipes.h> 55 #include <usb/classes/classes.h> 56 57 58 static usb_hub_info_t * usb_hub_info_create(usb_device_t *usb_dev); 59 60 static int usb_hub_process_hub_specific_info(usb_hub_info_t *hub_info); 61 62 static int usb_hub_set_configuration(usb_hub_info_t *hub_info);63 64 static int usb_ hub_start_hub_fibril(usb_hub_info_t *hub_info);65 66 static int usb_process_hub_over_current(usb_hub_info_t *hub_info,56 #include "status.h" 57 58 #define HUB_FNC_NAME "hub" 59 60 /** Standard get hub global status request */ 61 static const usb_device_request_setup_packet_t get_hub_status_request = { 62 .request_type = USB_HUB_REQ_TYPE_GET_HUB_STATUS, 63 .request = USB_HUB_REQUEST_GET_STATUS, 64 .index = 0, 65 .value = 0, 66 .length = sizeof(usb_hub_status_t), 67 }; 68 69 static int usb_set_first_configuration(usb_device_t *usb_device); 70 static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev); 71 static void usb_hub_over_current(const usb_hub_dev_t *hub_dev, 67 72 usb_hub_status_t status); 68 69 static int usb_process_hub_local_power_change(usb_hub_info_t *hub_info, 70 usb_hub_status_t status); 71 72 static void usb_hub_process_global_interrupt(usb_hub_info_t *hub_info); 73 73 static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev); 74 74 static void usb_hub_polling_terminated_callback(usb_device_t *device, 75 75 bool was_error, void *data); 76 76 77 78 //********************************************* 79 // 80 // hub driver code, initialization 81 // 82 //********************************************* 83 84 /** 85 * Initialize hub device driver fibril 86 * 87 * Creates hub representation and fibril that periodically checks hub`s status. 77 /** 78 * Initialize hub device driver structure. 79 * 80 * Creates hub representation and fibril that periodically checks hub's status. 88 81 * Hub representation is passed to the fibril. 89 82 * @param usb_dev generic usb device information 90 83 * @return error code 91 84 */ 92 int usb_hub_add_device(usb_device_t *usb_dev) { 93 if (!usb_dev) return EINVAL; 94 usb_hub_info_t *hub_info = usb_hub_info_create(usb_dev); 95 //create hc connection 96 usb_log_debug("Initializing USB wire abstraction.\n"); 97 int opResult = usb_hc_connection_initialize_from_device( 98 &hub_info->connection, 99 hub_info->usb_device->ddf_dev); 100 if (opResult != EOK) { 101 usb_log_error("Could not initialize connection to device, " 102 " %s\n", 103 str_error(opResult)); 104 free(hub_info); 85 int usb_hub_device_add(usb_device_t *usb_dev) 86 { 87 assert(usb_dev); 88 /* Create driver soft-state structure */ 89 usb_hub_dev_t *hub_dev = 90 usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t)); 91 if (hub_dev == NULL) { 92 usb_log_error("Failed to create hub driver structure.\n"); 93 return ENOMEM; 94 } 95 hub_dev->usb_device = usb_dev; 96 hub_dev->pending_ops_count = 0; 97 hub_dev->running = false; 98 fibril_mutex_initialize(&hub_dev->pending_ops_mutex); 99 fibril_condvar_initialize(&hub_dev->pending_ops_cv); 100 101 102 int opResult = usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe); 103 if (opResult != EOK) { 104 usb_log_error("Failed to start long ctrl pipe transfer: %s\n", 105 str_error(opResult)); 105 106 return opResult; 106 107 } 107 108 108 / /set hub configuration109 opResult = usb_ hub_set_configuration(hub_info);110 if (opResult != EOK) { 111 usb_ log_error("Could not set hub configuration, %s\n",112 str_error(opResult));113 free(hub_info);109 /* Set hub's first configuration. (There should be only one) */ 110 opResult = usb_set_first_configuration(usb_dev); 111 if (opResult != EOK) { 112 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 113 usb_log_error("Could not set hub configuration: %s\n", 114 str_error(opResult)); 114 115 return opResult; 115 116 } 116 //get port count and create attached_devs 117 opResult = usb_hub_process_hub_specific_info(hub_info); 118 if (opResult != EOK) { 117 118 /* Get port count and create attached_devices. */ 119 opResult = usb_hub_process_hub_specific_info(hub_dev); 120 if (opResult != EOK) { 121 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 119 122 usb_log_error("Could process hub specific info, %s\n", 120 123 str_error(opResult)); 121 free(hub_info);122 124 return opResult; 123 125 } 124 126 125 usb_log_debug("Creating 'hub' function in DDF.\n"); 126 ddf_fun_t *hub_fun = ddf_fun_create(hub_info->usb_device->ddf_dev, 127 fun_exposed, "hub"); 128 assert(hub_fun != NULL); 129 hub_fun->ops = NULL; 130 131 opResult = ddf_fun_bind(hub_fun); 132 assert(opResult == EOK); 133 134 opResult = usb_hub_start_hub_fibril(hub_info); 135 if (opResult != EOK) 136 free(hub_info); 137 return opResult; 138 } 139 127 /* Create hub control function. */ 128 usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.\n"); 129 hub_dev->hub_fun = ddf_fun_create(hub_dev->usb_device->ddf_dev, 130 fun_exposed, HUB_FNC_NAME); 131 if (hub_dev->hub_fun == NULL) { 132 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 133 usb_log_error("Failed to create hub function.\n"); 134 return ENOMEM; 135 } 136 137 /* Bind hub control function. */ 138 opResult = ddf_fun_bind(hub_dev->hub_fun); 139 if (opResult != EOK) { 140 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 141 usb_log_error("Failed to bind hub function: %s.\n", 142 str_error(opResult)); 143 ddf_fun_destroy(hub_dev->hub_fun); 144 return opResult; 145 } 146 147 /* Start hub operation. */ 148 opResult = usb_device_auto_poll(hub_dev->usb_device, 0, 149 hub_port_changes_callback, ((hub_dev->port_count + 1 + 8) / 8), 150 usb_hub_polling_terminated_callback, hub_dev); 151 if (opResult != EOK) { 152 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 153 /* Function is already bound */ 154 ddf_fun_unbind(hub_dev->hub_fun); 155 ddf_fun_destroy(hub_dev->hub_fun); 156 usb_log_error("Failed to create polling fibril: %s.\n", 157 str_error(opResult)); 158 return opResult; 159 } 160 hub_dev->running = true; 161 usb_log_info("Controlling hub '%s' (%zu ports).\n", 162 hub_dev->usb_device->ddf_dev->name, hub_dev->port_count); 163 164 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe); 165 return EOK; 166 } 167 /*----------------------------------------------------------------------------*/ 168 /** 169 * Turn off power to all ports. 170 * 171 * @param usb_dev generic usb device information 172 * @return error code 173 */ 174 int usb_hub_device_remove(usb_device_t *usb_dev) 175 { 176 return ENOTSUP; 177 } 178 /*----------------------------------------------------------------------------*/ 179 /** 180 * Remove all attached devices 181 * @param usb_dev generic usb device information 182 * @return error code 183 */ 184 int usb_hub_device_gone(usb_device_t *usb_dev) 185 { 186 assert(usb_dev); 187 usb_hub_dev_t *hub = usb_dev->driver_data; 188 assert(hub); 189 unsigned tries = 10; 190 while (hub->running) { 191 async_usleep(100000); 192 if (!tries--) { 193 usb_log_error("Can't remove hub, still running.\n"); 194 return EINPROGRESS; 195 } 196 } 197 198 assert(!hub->running); 199 200 for (size_t port = 0; port < hub->port_count; ++port) { 201 if (hub->ports[port].attached_device.fun) { 202 const int ret = 203 usb_hub_port_fini(&hub->ports[port], hub); 204 if (ret != EOK) 205 return ret; 206 } 207 } 208 free(hub->ports); 209 210 const int ret = ddf_fun_unbind(hub->hub_fun); 211 if (ret != EOK) { 212 usb_log_error("Failed to unbind '%s' function: %s.\n", 213 HUB_FNC_NAME, str_error(ret)); 214 return ret; 215 } 216 ddf_fun_destroy(hub->hub_fun); 217 218 usb_log_info("USB hub driver, stopped and cleaned.\n"); 219 return EOK; 220 } 221 /*----------------------------------------------------------------------------*/ 140 222 /** Callback for polling hub for changes. 141 223 * … … 143 225 * @param change_bitmap Bitmap of changed ports. 144 226 * @param change_bitmap_size Size of the bitmap in bytes. 145 * @param arg Custom argument, points to @c usb_hub_ info_t.227 * @param arg Custom argument, points to @c usb_hub_dev_t. 146 228 * @return Whether to continue polling. 147 229 */ 148 230 bool hub_port_changes_callback(usb_device_t *dev, 149 uint8_t *change_bitmap, size_t change_bitmap_size, void *arg) { 231 uint8_t *change_bitmap, size_t change_bitmap_size, void *arg) 232 { 150 233 usb_log_debug("hub_port_changes_callback\n"); 151 usb_hub_info_t *hub = (usb_hub_info_t *) arg; 152 153 /* FIXME: check that we received enough bytes. */ 234 usb_hub_dev_t *hub = arg; 235 assert(hub); 236 237 /* It is an error condition if we didn't receive enough data */ 154 238 if (change_bitmap_size == 0) { 155 goto leave;156 } 157 158 bool change;159 c hange = ((uint8_t*) change_bitmap)[0] & 1;239 return false; 240 } 241 242 /* Lowest bit indicates global change */ 243 const bool change = change_bitmap[0] & 1; 160 244 if (change) { 161 usb_hub_process_global_interrupt(hub); 162 } 163 164 size_t port; 165 for (port = 1; port < hub->port_count + 1; port++) { 166 bool change = (change_bitmap[port / 8] >> (port % 8)) % 2; 245 usb_hub_global_interrupt(hub); 246 } 247 248 /* N + 1 bit indicates change on port N */ 249 for (size_t port = 0; port < hub->port_count + 1; port++) { 250 const size_t bit = port + 1; 251 const bool change = (change_bitmap[bit / 8] >> (bit % 8)) & 1; 167 252 if (change) { 168 usb_hub_process_port_interrupt(hub, port); 169 } 170 } 171 leave: 172 /* FIXME: proper interval. */ 173 async_usleep(1000 * 250); 174 253 usb_hub_port_process_interrupt(&hub->ports[port], hub); 254 } 255 } 175 256 return true; 176 257 } 177 178 179 //********************************************* 180 // 181 // support functions 182 // 183 //********************************************* 184 185 /** 186 * create usb_hub_info_t structure 187 * 188 * Does only basic copying of known information into new structure. 189 * @param usb_dev usb device structure 190 * @return basic usb_hub_info_t structure 191 */ 192 static usb_hub_info_t * usb_hub_info_create(usb_device_t *usb_dev) { 193 usb_hub_info_t * result = malloc(sizeof (usb_hub_info_t)); 194 if (!result) return NULL; 195 result->usb_device = usb_dev; 196 result->status_change_pipe = usb_dev->pipes[0].pipe; 197 result->control_pipe = &usb_dev->ctrl_pipe; 198 result->is_default_address_used = false; 199 200 result->ports = NULL; 201 result->port_count = (size_t) - 1; 202 fibril_mutex_initialize(&result->port_mutex); 203 204 fibril_mutex_initialize(&result->pending_ops_mutex); 205 fibril_condvar_initialize(&result->pending_ops_cv); 206 result->pending_ops_count = 0; 207 return result; 208 } 209 210 /** 211 * Load hub-specific information into hub_info structure and process if needed 212 * 213 * Particularly read port count and initialize structure holding port 214 * information. If there are non-removable devices, start initializing them. 258 /*----------------------------------------------------------------------------*/ 259 /** 260 * Load hub-specific information into hub_dev structure and process if needed 261 * 262 * Read port count and initialize structures holding per port information. 263 * If there are any non-removable devices, start initializing them. 215 264 * This function is hub-specific and should be run only after the hub is 216 * configured using usb_ hub_set_configuration function.217 * @param hub_ infohub representation265 * configured using usb_set_first_configuration function. 266 * @param hub_dev hub representation 218 267 * @return error code 219 268 */ 220 int usb_hub_process_hub_specific_info(usb_hub_info_t *hub_info) 221 { 222 // get hub descriptor 269 static int usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev) 270 { 271 assert(hub_dev); 272 273 /* Get hub descriptor. */ 223 274 usb_log_debug("Retrieving descriptor\n"); 224 u int8_t serialized_descriptor[USB_HUB_MAX_DESCRIPTOR_SIZE];225 int opResult; 226 275 usb_pipe_t *control_pipe = &hub_dev->usb_device->ctrl_pipe; 276 277 usb_hub_descriptor_header_t descriptor; 227 278 size_t received_size; 228 opResult = usb_request_get_descriptor(hub_info->control_pipe,279 int opResult = usb_request_get_descriptor(control_pipe, 229 280 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE, 230 USB_DESCTYPE_HUB, 0, 0, serialized_descriptor, 231 USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size); 232 281 USB_DESCTYPE_HUB, 0, 0, &descriptor, 282 sizeof(usb_hub_descriptor_header_t), &received_size); 233 283 if (opResult != EOK) { 234 284 usb_log_error("Failed to receive hub descriptor: %s.\n", … … 236 286 return opResult; 237 287 } 238 usb_log_debug2("Parsing descriptor\n"); 239 usb_hub_descriptor_t descriptor; 240 opResult = usb_deserialize_hub_desriptor( 241 serialized_descriptor, received_size, &descriptor); 242 if (opResult != EOK) { 243 usb_log_error("Could not parse descriptor: %s\n", 244 str_error(opResult)); 245 return opResult; 246 } 247 usb_log_debug("Setting port count to %d.\n", descriptor.ports_count); 248 hub_info->port_count = descriptor.ports_count; 249 250 hub_info->ports = 251 malloc(sizeof(usb_hub_port_t) * (hub_info->port_count + 1)); 252 if (!hub_info->ports) { 288 289 usb_log_debug("Setting port count to %d.\n", descriptor.port_count); 290 hub_dev->port_count = descriptor.port_count; 291 292 hub_dev->ports = calloc(hub_dev->port_count, sizeof(usb_hub_port_t)); 293 if (!hub_dev->ports) { 253 294 return ENOMEM; 254 295 } 255 296 256 size_t port; 257 for (port = 0; port < hub_info->port_count + 1; ++port) { 258 usb_hub_port_init(&hub_info->ports[port]); 259 } 260 261 const bool is_power_switched = 262 !(descriptor.hub_characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG); 263 if (is_power_switched) { 264 usb_log_debug("Hub power switched\n"); 265 const bool per_port_power = descriptor.hub_characteristics 266 & HUB_CHAR_POWER_PER_PORT_FLAG; 267 268 for (port = 1; port <= hub_info->port_count; ++port) { 269 usb_log_debug("Powering port %zu.\n", port); 270 opResult = usb_hub_set_port_feature( 271 hub_info->control_pipe, 272 port, USB_HUB_FEATURE_PORT_POWER); 273 if (opResult != EOK) { 274 usb_log_error("Cannot power on port %zu: %s.\n", 275 port, str_error(opResult)); 276 } else { 277 if (!per_port_power) { 278 usb_log_debug( 279 "Ganged power switching mode, " 280 "one port is enough.\n"); 281 break; 282 } 297 for (size_t port = 0; port < hub_dev->port_count; ++port) { 298 usb_hub_port_init( 299 &hub_dev->ports[port], port + 1, control_pipe); 300 } 301 302 hub_dev->power_switched = 303 !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG); 304 hub_dev->per_port_power = 305 descriptor.characteristics & HUB_CHAR_POWER_PER_PORT_FLAG; 306 307 if (!hub_dev->power_switched) { 308 usb_log_info( 309 "Power switching not supported, ports always powered.\n"); 310 return EOK; 311 } 312 313 usb_log_info("Hub port power switching enabled.\n"); 314 315 for (size_t port = 0; port < hub_dev->port_count; ++port) { 316 usb_log_debug("Powering port %zu.\n", port); 317 const int ret = usb_hub_port_set_feature( 318 &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER); 319 320 if (ret != EOK) { 321 usb_log_error("Cannot power on port %zu: %s.\n", 322 hub_dev->ports[port].port_number, str_error(ret)); 323 } else { 324 if (!hub_dev->per_port_power) { 325 usb_log_debug("Ganged power switching, " 326 "one port is enough.\n"); 327 break; 283 328 } 284 329 } 285 286 } else {287 usb_log_debug("Power not switched, not going to be powered\n");288 330 } 289 331 return EOK; 290 332 } 291 292 /** 293 * Set configuration of hub333 /*----------------------------------------------------------------------------*/ 334 /** 335 * Set configuration of and USB device 294 336 * 295 337 * Check whether there is at least one configuration and sets the first one. 296 338 * This function should be run prior to running any hub-specific action. 297 * @param hub_info hubrepresentation339 * @param usb_device usb device representation 298 340 * @return error code 299 341 */ 300 static int usb_hub_set_configuration(usb_hub_info_t *hub_info) { 301 //device descriptor 302 usb_standard_device_descriptor_t *std_descriptor 303 = &hub_info->usb_device->descriptors.device; 304 usb_log_debug("Hub has %d configurations\n", 305 std_descriptor->configuration_count); 306 if (std_descriptor->configuration_count < 1) { 342 static int usb_set_first_configuration(usb_device_t *usb_device) 343 { 344 assert(usb_device); 345 /* Get number of possible configurations from device descriptor */ 346 const size_t configuration_count = 347 usb_device->descriptors.device.configuration_count; 348 usb_log_debug("Hub has %zu configurations.\n", configuration_count); 349 350 if (configuration_count < 1) { 307 351 usb_log_error("There are no configurations available\n"); 308 352 return EINVAL; 309 353 } 310 354 355 if (usb_device->descriptors.configuration_size 356 < sizeof(usb_standard_configuration_descriptor_t)) { 357 usb_log_error("Configuration descriptor is not big enough" 358 " to fit standard configuration descriptor.\n"); 359 return EOVERFLOW; 360 } 361 362 // TODO: Make sure that the cast is correct 311 363 usb_standard_configuration_descriptor_t *config_descriptor 312 364 = (usb_standard_configuration_descriptor_t *) 313 hub_info->usb_device->descriptors.configuration; 314 315 /* Set configuration. */ 316 int opResult = usb_request_set_configuration( 317 &hub_info->usb_device->ctrl_pipe, 318 config_descriptor->configuration_number); 319 365 usb_device->descriptors.configuration; 366 367 /* Set configuration. Use the configuration that was in 368 * usb_device->descriptors.configuration i.e. The first one. */ 369 const int opResult = usb_request_set_configuration( 370 &usb_device->ctrl_pipe, config_descriptor->configuration_number); 320 371 if (opResult != EOK) { 321 372 usb_log_error("Failed to set hub configuration: %s.\n", 322 373 str_error(opResult)); 323 return opResult; 324 } 325 usb_log_debug("\tUsed configuration %d\n", 326 config_descriptor->configuration_number); 327 328 return EOK; 329 } 330 331 /** 332 * create and start fibril with hub control loop 333 * 334 * Before the fibril is started, the control pipe and host controller 335 * connection of the hub is open. 336 * 337 * @param hub_info hub representing structure 338 * @return error code 339 */ 340 static int usb_hub_start_hub_fibril(usb_hub_info_t *hub_info) { 341 int rc; 342 343 rc = usb_device_auto_poll(hub_info->usb_device, 0, 344 hub_port_changes_callback, ((hub_info->port_count + 1) / 8) + 1, 345 usb_hub_polling_terminated_callback, hub_info); 346 if (rc != EOK) { 347 usb_log_error("Failed to create polling fibril: %s.\n", 348 str_error(rc)); 349 free(hub_info); 350 return rc; 351 } 352 353 usb_log_info("Controlling hub `%s' (%zu ports).\n", 354 hub_info->usb_device->ddf_dev->name, hub_info->port_count); 355 return EOK; 356 } 357 358 //********************************************* 359 // 360 // change handling functions 361 // 362 //********************************************* 363 364 /** 365 * process hub over current change 374 } else { 375 usb_log_debug("\tUsed configuration %d\n", 376 config_descriptor->configuration_number); 377 } 378 return opResult; 379 } 380 /*----------------------------------------------------------------------------*/ 381 /** 382 * Process hub over current change 366 383 * 367 384 * This means either to power off the hub or power it on. 368 * @param hub_ infohub instance385 * @param hub_dev hub instance 369 386 * @param status hub status bitmask 370 387 * @return error code 371 388 */ 372 static int usb_process_hub_over_current(usb_hub_info_t *hub_info, 373 usb_hub_status_t status) { 374 int opResult; 375 if (usb_hub_is_status(status, USB_HUB_FEATURE_HUB_OVER_CURRENT)) { 376 //poweroff all ports 377 unsigned int port; 378 for (port = 1; port <= hub_info->port_count; ++port) { 379 opResult = usb_hub_clear_port_feature( 380 hub_info->control_pipe, port, 381 USB_HUB_FEATURE_PORT_POWER); 382 if (opResult != EOK) { 383 usb_log_warning( 384 "Cannot power off port %d; %s\n", 385 port, str_error(opResult)); 386 } 387 } 388 } else { 389 //power all ports 390 unsigned int port; 391 for (port = 1; port <= hub_info->port_count; ++port) { 392 opResult = usb_hub_set_port_feature( 393 hub_info->control_pipe, port, 394 USB_HUB_FEATURE_PORT_POWER); 395 if (opResult != EOK) { 396 usb_log_warning( 397 "Cannot power off port %d; %s\n", 398 port, str_error(opResult)); 399 } 400 } 401 } 402 return opResult; 403 } 404 405 /** 406 * process hub local power change 407 * 408 * This change is ignored. 409 * @param hub_info hub instance 410 * @param status hub status bitmask 411 * @return error code 412 */ 413 static int usb_process_hub_local_power_change(usb_hub_info_t *hub_info, 414 usb_hub_status_t status) { 415 int opResult = EOK; 416 opResult = usb_hub_clear_feature(hub_info->control_pipe, 417 USB_HUB_FEATURE_C_HUB_LOCAL_POWER); 418 if (opResult != EOK) { 419 usb_log_error("Cannnot clear hub power change flag: " 420 "%s\n", 421 str_error(opResult)); 422 } 423 return opResult; 424 } 425 426 /** 427 * process hub interrupts 428 * 429 * The change can be either in the over-current condition or 430 * local-power change. 431 * @param hub_info hub instance 432 */ 433 static void usb_hub_process_global_interrupt(usb_hub_info_t *hub_info) { 389 static void usb_hub_over_current(const usb_hub_dev_t *hub_dev, 390 usb_hub_status_t status) 391 { 392 if (status & USB_HUB_STATUS_OVER_CURRENT) { 393 /* Hub should remove power from all ports if it detects OC */ 394 usb_log_warning("Detected hub over-current condition, " 395 "all ports should be powered off."); 396 return; 397 } 398 399 /* Ports are always powered. */ 400 if (!hub_dev->power_switched) 401 return; 402 403 /* Over-current condition is gone, it is safe to turn the ports on. */ 404 for (size_t port = 0; port < hub_dev->port_count; ++port) { 405 const int ret = usb_hub_port_set_feature( 406 &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER); 407 if (ret != EOK) { 408 usb_log_warning("HUB OVER-CURRENT GONE: Cannot power on" 409 " port %zu: %s\n", hub_dev->ports[port].port_number, 410 str_error(ret)); 411 } else { 412 if (!hub_dev->per_port_power) 413 return; 414 } 415 } 416 417 } 418 /*----------------------------------------------------------------------------*/ 419 /** 420 * Process hub interrupts. 421 * 422 * The change can be either in the over-current condition or local-power change. 423 * @param hub_dev hub instance 424 */ 425 static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev) 426 { 427 assert(hub_dev); 428 assert(hub_dev->usb_device); 434 429 usb_log_debug("Global interrupt on a hub\n"); 435 usb_pipe_t *pipe = hub_info->control_pipe; 436 int opResult; 437 438 usb_port_status_t status; 430 usb_pipe_t *control_pipe = &hub_dev->usb_device->ctrl_pipe; 431 432 usb_hub_status_t status; 439 433 size_t rcvd_size; 440 usb_device_request_setup_packet_t request; 441 //int opResult; 442 usb_hub_set_hub_status_request(&request); 443 //endpoint 0 444 445 opResult = usb_pipe_control_read( 446 pipe, 447 &request, sizeof (usb_device_request_setup_packet_t), 448 &status, 4, &rcvd_size 449 ); 434 /* NOTE: We can't use standard USB GET_STATUS request, because 435 * hubs reply is 4byte instead of 2 */ 436 const int opResult = usb_pipe_control_read(control_pipe, 437 &get_hub_status_request, sizeof(get_hub_status_request), 438 &status, sizeof(usb_hub_status_t), &rcvd_size); 450 439 if (opResult != EOK) { 451 440 usb_log_error("Could not get hub status: %s\n", … … 453 442 return; 454 443 } 455 if (rcvd_size != sizeof (usb_port_status_t)) {444 if (rcvd_size != sizeof(usb_hub_status_t)) { 456 445 usb_log_error("Received status has incorrect size\n"); 457 446 return; 458 447 } 459 //port reset 460 if ( 461 usb_hub_is_status(status, 16 + USB_HUB_FEATURE_C_HUB_OVER_CURRENT)) { 462 usb_process_hub_over_current(hub_info, status); 463 } 464 if ( 465 usb_hub_is_status(status, 16 + USB_HUB_FEATURE_C_HUB_LOCAL_POWER)) { 466 usb_process_hub_local_power_change(hub_info, status); 467 } 468 } 469 448 449 /* Handle status changes */ 450 if (status & USB_HUB_STATUS_C_OVER_CURRENT) { 451 usb_hub_over_current(hub_dev, status); 452 /* Ack change in hub OC flag */ 453 const int ret = usb_request_clear_feature( 454 &hub_dev->usb_device->ctrl_pipe, USB_REQUEST_TYPE_CLASS, 455 USB_REQUEST_RECIPIENT_DEVICE, 456 USB_HUB_FEATURE_C_HUB_OVER_CURRENT, 0); 457 if (ret != EOK) { 458 usb_log_error("Failed to clear hub over-current " 459 "change flag: %s.\n", str_error(opResult)); 460 } 461 } 462 463 if (status & USB_HUB_STATUS_C_LOCAL_POWER) { 464 /* NOTE: Handling this is more complicated. 465 * If the transition is from bus power to local power, all 466 * is good and we may signal the parent hub that we don't 467 * need the power. 468 * If the transition is from local power to bus power 469 * the hub should turn off all the ports and devices need 470 * to be reinitialized taking into account the limited power 471 * that is now available. 472 * There is no support for power distribution in HelenOS, 473 * (or other OSes/hub devices that I've seen) so this is not 474 * implemented. 475 * Just ACK the change. 476 */ 477 const int ret = usb_request_clear_feature( 478 control_pipe, USB_REQUEST_TYPE_CLASS, 479 USB_REQUEST_RECIPIENT_DEVICE, 480 USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0); 481 if (opResult != EOK) { 482 usb_log_error("Failed to clear hub power change " 483 "flag: %s.\n", str_error(ret)); 484 } 485 } 486 } 487 /*----------------------------------------------------------------------------*/ 470 488 /** 471 489 * callback called from hub polling fibril when the fibril terminates 472 490 * 473 * Should perform a cleanup - deletes hub_info.491 * Does not perform cleanup, just marks the hub as not running. 474 492 * @param device usb device afected 475 493 * @param was_error indicates that the fibril is stoped due to an error 476 * @param data pointer to usb_hub_ info_t structure494 * @param data pointer to usb_hub_dev_t structure 477 495 */ 478 496 static void usb_hub_polling_terminated_callback(usb_device_t *device, 479 bool was_error, void *data) { 480 usb_hub_info_t * hub = data; 497 bool was_error, void *data) 498 { 499 usb_hub_dev_t *hub = data; 481 500 assert(hub); 482 501 … … 492 511 */ 493 512 if (hub->pending_ops_count > 0) { 494 fibril_mutex_lock(&hub->port_mutex); 495 size_t port; 496 for (port = 0; port < hub->port_count; port++) { 497 usb_hub_port_t *the_port = hub->ports + port; 498 fibril_mutex_lock(&the_port->reset_mutex); 499 the_port->reset_completed = true; 500 the_port->reset_okay = false; 501 fibril_condvar_broadcast(&the_port->reset_cv); 502 fibril_mutex_unlock(&the_port->reset_mutex); 503 } 504 fibril_mutex_unlock(&hub->port_mutex); 513 for (size_t port = 0; port < hub->port_count; ++port) { 514 usb_hub_port_reset_fail(&hub->ports[port]); 515 } 505 516 } 506 517 /* And now wait for them. */ … … 510 521 } 511 522 fibril_mutex_unlock(&hub->pending_ops_mutex); 512 513 usb_device_destroy(hub->usb_device); 514 515 free(hub->ports); 516 free(hub); 517 } 518 519 520 521 523 hub->running = false; 524 } 522 525 /** 523 526 * @}
Note:
See TracChangeset
for help on using the changeset viewer.