source: mainline/uspace/drv/bus/usb/usbhub/usbhub.c@ e0a5d4c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e0a5d4c was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 22.9 KB
Line 
1/*
2 * Copyright (c) 2010 Matus Dekanek
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2018 Ondrej Hlavaty, Petr Manek
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup drvusbhub
32 * @{
33 */
34/** @file
35 * @brief usb hub main functionality
36 */
37
38#include <ddf/driver.h>
39#include <stdbool.h>
40#include <errno.h>
41#include <str_error.h>
42#include <inttypes.h>
43#include <stdio.h>
44
45#include <usb/usb.h>
46#include <usb/debug.h>
47#include <usb/dev/pipes.h>
48#include <usb/classes/classes.h>
49#include <usb/descriptor.h>
50#include <usb/dev/recognise.h>
51#include <usb/dev/request.h>
52#include <usb/classes/hub.h>
53#include <usb/dev/poll.h>
54#include <usbhc_iface.h>
55
56#include "usbhub.h"
57#include "status.h"
58
59#define HUB_FNC_NAME "hub"
60
61#define HUB_STATUS_CHANGE_EP(protocol) { \
62 .transfer_type = USB_TRANSFER_INTERRUPT, \
63 .direction = USB_DIRECTION_IN, \
64 .interface_class = USB_CLASS_HUB, \
65 .interface_subclass = 0, \
66 .interface_protocol = (protocol), \
67 .flags = 0 \
68}
69
70/**
71 * Hub status-change endpoint description.
72 *
73 * According to USB 2.0 specification, there are two possible arrangements of
74 * endpoints, depending on whether the hub has a MTT or not.
75 *
76 * Under any circumstances, there shall be exactly one endpoint descriptor.
77 * Though to be sure, let's map the protocol precisely. The possible
78 * combinations are:
79 * | bDeviceProtocol | bInterfaceProtocol
80 * Only single TT | 0 | 0
81 * MTT in Single-TT mode | 2 | 1
82 * MTT in MTT mode | 2 | 2 (iface alt. 1)
83 */
84static const usb_endpoint_description_t
85 status_change_single_tt_only = HUB_STATUS_CHANGE_EP(0),
86 status_change_mtt_available = HUB_STATUS_CHANGE_EP(1);
87
88const usb_endpoint_description_t *usb_hub_endpoints [] = {
89 &status_change_single_tt_only,
90 &status_change_mtt_available,
91};
92
93/** Standard get hub global status request */
94static const usb_device_request_setup_packet_t get_hub_status_request = {
95 .request_type = USB_HUB_REQ_TYPE_GET_HUB_STATUS,
96 .request = USB_HUB_REQUEST_GET_STATUS,
97 .index = 0,
98 .value = 0,
99 .length = sizeof(usb_hub_status_t),
100};
101
102static errno_t usb_set_first_configuration(usb_device_t *);
103static errno_t usb_hub_process_hub_specific_info(usb_hub_dev_t *);
104static void usb_hub_over_current(const usb_hub_dev_t *, usb_hub_status_t);
105static errno_t usb_hub_polling_init(usb_hub_dev_t *, usb_endpoint_mapping_t *);
106static void usb_hub_global_interrupt(const usb_hub_dev_t *);
107
108static bool usb_hub_polling_error_callback(usb_device_t *dev,
109 errno_t err_code, void *arg)
110{
111 assert(dev);
112 assert(arg);
113
114 usb_log_error("Device %s polling error: %s",
115 usb_device_get_name(dev), str_error(err_code));
116
117 return true;
118}
119
120/**
121 * Initialize hub device driver structure.
122 *
123 * Creates hub representation and fibril that periodically checks hub's status.
124 * Hub representation is passed to the fibril.
125 * @param usb_dev generic usb device information
126 * @return error code
127 */
128errno_t usb_hub_device_add(usb_device_t *usb_dev)
129{
130 errno_t err;
131 assert(usb_dev);
132
133 /* Create driver soft-state structure */
134 usb_hub_dev_t *hub_dev =
135 usb_device_data_alloc(usb_dev, sizeof(usb_hub_dev_t));
136 if (hub_dev == NULL) {
137 usb_log_error("Failed to create hub driver structure.");
138 return ENOMEM;
139 }
140 hub_dev->usb_device = usb_dev;
141 hub_dev->speed = usb_device_get_speed(usb_dev);
142
143 /* Set hub's first configuration. (There should be only one) */
144 if ((err = usb_set_first_configuration(usb_dev))) {
145 usb_log_error("Could not set hub configuration: %s", str_error(err));
146 return err;
147 }
148
149 /* Get port count and create attached_devices. */
150 if ((err = usb_hub_process_hub_specific_info(hub_dev))) {
151 usb_log_error("Could process hub specific info, %s", str_error(err));
152 return err;
153 }
154
155 const usb_endpoint_description_t *status_change = hub_dev->mtt_available
156 ? &status_change_mtt_available
157 : &status_change_single_tt_only;
158
159 usb_endpoint_mapping_t *status_change_mapping
160 = usb_device_get_mapped_ep_desc(hub_dev->usb_device, status_change);
161 if (!status_change_mapping) {
162 usb_log_error("Failed to map the Status Change Endpoint of a hub.");
163 return EIO;
164 }
165
166 /* Create hub control function. */
167 usb_log_debug("Creating DDF function '" HUB_FNC_NAME "'.");
168 hub_dev->hub_fun = usb_device_ddf_fun_create(hub_dev->usb_device,
169 fun_exposed, HUB_FNC_NAME);
170 if (hub_dev->hub_fun == NULL) {
171 usb_log_error("Failed to create hub function.");
172 return ENOMEM;
173 }
174
175 /* Bind hub control function. */
176 if ((err = ddf_fun_bind(hub_dev->hub_fun))) {
177 usb_log_error("Failed to bind hub function: %s.", str_error(err));
178 goto err_ddf_fun;
179 }
180
181 /* Start hub operation. */
182 if ((err = usb_hub_polling_init(hub_dev, status_change_mapping))) {
183 usb_log_error("Failed to start polling: %s.", str_error(err));
184 goto err_bound;
185 }
186
187 usb_log_info("Controlling %s-speed hub '%s' (%p: %zu ports).",
188 usb_str_speed(hub_dev->speed),
189 usb_device_get_name(hub_dev->usb_device), hub_dev,
190 hub_dev->port_count);
191
192 return EOK;
193
194err_bound:
195 ddf_fun_unbind(hub_dev->hub_fun);
196err_ddf_fun:
197 ddf_fun_destroy(hub_dev->hub_fun);
198 return err;
199}
200
201static errno_t usb_hub_cleanup(usb_hub_dev_t *hub)
202{
203 free(hub->polling.buffer);
204 usb_polling_fini(&hub->polling);
205
206 for (size_t port = 0; port < hub->port_count; ++port) {
207 usb_port_fini(&hub->ports[port].base);
208 }
209 free(hub->ports);
210
211 const errno_t ret = ddf_fun_unbind(hub->hub_fun);
212 if (ret != EOK) {
213 usb_log_error("(%p) Failed to unbind '%s' function: %s.",
214 hub, HUB_FNC_NAME, str_error(ret));
215 return ret;
216 }
217 ddf_fun_destroy(hub->hub_fun);
218
219 usb_log_info("(%p) USB hub driver stopped and cleaned.", hub);
220
221 /* Device data (usb_hub_dev_t) will be freed by usbdev. */
222 return EOK;
223}
224
225/**
226 * Turn off power to all ports.
227 *
228 * @param usb_dev generic usb device information
229 * @return error code
230 */
231errno_t usb_hub_device_remove(usb_device_t *usb_dev)
232{
233 assert(usb_dev);
234 usb_hub_dev_t *hub = usb_device_data_get(usb_dev);
235 assert(hub);
236
237 usb_log_info("(%p) USB hub removed, joining polling fibril.", hub);
238
239 /* Join polling fibril (ignoring error code). */
240 usb_polling_join(&hub->polling);
241 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub);
242
243 /* Destroy hub. */
244 return usb_hub_cleanup(hub);
245}
246
247/**
248 * Remove all attached devices
249 * @param usb_dev generic usb device information
250 * @return error code
251 */
252errno_t usb_hub_device_gone(usb_device_t *usb_dev)
253{
254 assert(usb_dev);
255 usb_hub_dev_t *hub = usb_device_data_get(usb_dev);
256 assert(hub);
257
258 usb_log_info("(%p) USB hub gone, joining polling fibril.", hub);
259
260 /* Join polling fibril (ignoring error code). */
261 usb_polling_join(&hub->polling);
262 usb_log_info("(%p) USB hub polling stopped, freeing memory.", hub);
263
264 /* Destroy hub. */
265 return usb_hub_cleanup(hub);
266}
267
268/**
269 * Initialize and start the polling of the Status Change Endpoint.
270 *
271 * @param mapping The mapping of Status Change Endpoint
272 */
273static errno_t usb_hub_polling_init(usb_hub_dev_t *hub_dev,
274 usb_endpoint_mapping_t *mapping)
275{
276 errno_t err;
277 usb_polling_t *polling = &hub_dev->polling;
278
279 if ((err = usb_polling_init(polling)))
280 return err;
281
282 polling->device = hub_dev->usb_device;
283 polling->ep_mapping = mapping;
284 polling->request_size = ((hub_dev->port_count + 1 + 7) / 8);
285 polling->buffer = malloc(polling->request_size);
286 polling->on_data = hub_port_changes_callback;
287 polling->on_error = usb_hub_polling_error_callback;
288 polling->arg = hub_dev;
289
290 if ((err = usb_polling_start(polling))) {
291 /* Polling is already initialized. */
292 free(polling->buffer);
293 usb_polling_fini(polling);
294 return err;
295 }
296
297 return EOK;
298}
299
300/**
301 * Callback for polling hub for changes.
302 *
303 * @param dev Device where the change occured.
304 * @param change_bitmap Bitmap of changed ports.
305 * @param change_bitmap_size Size of the bitmap in bytes.
306 * @param arg Custom argument, points to @c usb_hub_dev_t.
307 * @return Whether to continue polling.
308 */
309bool hub_port_changes_callback(usb_device_t *dev,
310 uint8_t *change_bitmap, size_t change_bitmap_size, void *arg)
311{
312 usb_hub_dev_t *hub = arg;
313 assert(hub);
314
315 /* It is an error condition if we didn't receive enough data */
316 if (change_bitmap_size == 0) {
317 return false;
318 }
319
320 /* Lowest bit indicates global change */
321 const bool change = change_bitmap[0] & 1;
322 if (change) {
323 usb_hub_global_interrupt(hub);
324 }
325
326 /* Nth bit indicates change on port N */
327 for (size_t port = 0; port < hub->port_count; ++port) {
328 const size_t bit = port + 1;
329 const bool change = (change_bitmap[bit / 8] >> (bit % 8)) & 1;
330 if (change) {
331 usb_hub_port_process_interrupt(&hub->ports[port]);
332 }
333 }
334 return true;
335}
336
337static void usb_hub_power_ports(usb_hub_dev_t *hub_dev)
338{
339 if (!hub_dev->power_switched) {
340 usb_log_info("(%p): Power switching not supported, "
341 "ports always powered.", hub_dev);
342 return;
343 }
344
345 usb_log_info("(%p): Hub port power switching enabled (%s).", hub_dev,
346 hub_dev->per_port_power ? "per port" : "ganged");
347
348 for (unsigned int port = 0; port < hub_dev->port_count; ++port) {
349 usb_log_debug("(%p): Powering port %u.", hub_dev, port + 1);
350 const errno_t ret = usb_hub_set_port_feature(hub_dev, port + 1,
351 USB_HUB_FEATURE_PORT_POWER);
352
353 if (ret != EOK) {
354 usb_log_error("(%p-%u): Cannot power on port: %s.",
355 hub_dev, hub_dev->ports[port].port_number,
356 str_error(ret));
357 /* Continue to try at least other ports */
358 }
359 }
360}
361
362/**
363 * Load hub-specific information into hub_dev structure and process if needed
364 *
365 * Read port count and initialize structures holding per port information.
366 * If there are any non-removable devices, start initializing them.
367 * This function is hub-specific and should be run only after the hub is
368 * configured using usb_set_first_configuration function.
369 * @param hub_dev hub representation
370 * @return error code
371 */
372static errno_t usb_hub_process_hub_specific_info(usb_hub_dev_t *hub_dev)
373{
374 assert(hub_dev);
375
376 usb_log_debug("(%p): Retrieving descriptor.", hub_dev);
377 usb_pipe_t *control_pipe = usb_device_get_default_pipe(hub_dev->usb_device);
378
379 usb_descriptor_type_t desc_type = hub_dev->speed >= USB_SPEED_SUPER
380 ? USB_DESCTYPE_SSPEED_HUB : USB_DESCTYPE_HUB;
381
382 /* Get hub descriptor. */
383 usb_hub_descriptor_header_t descriptor;
384 size_t received_size;
385 errno_t opResult = usb_request_get_descriptor(control_pipe,
386 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE,
387 desc_type, 0, 0, &descriptor,
388 sizeof(usb_hub_descriptor_header_t), &received_size);
389 if (opResult != EOK) {
390 usb_log_error("(%p): Failed to receive hub descriptor: %s.",
391 hub_dev, str_error(opResult));
392 return opResult;
393 }
394
395 usb_log_debug("(%p): Setting port count to %d.", hub_dev,
396 descriptor.port_count);
397 hub_dev->port_count = descriptor.port_count;
398 hub_dev->control_pipe = control_pipe;
399
400 usb_log_debug("(%p): Setting hub depth to %u.", hub_dev,
401 usb_device_get_depth(hub_dev->usb_device));
402 if ((opResult = usb_hub_set_depth(hub_dev))) {
403 usb_log_error("(%p): Failed to set hub depth: %s.",
404 hub_dev, str_error(opResult));
405 return opResult;
406 }
407
408 hub_dev->ports = calloc(hub_dev->port_count, sizeof(usb_hub_port_t));
409 if (!hub_dev->ports) {
410 return ENOMEM;
411 }
412
413 for (size_t port = 0; port < hub_dev->port_count; ++port) {
414 usb_hub_port_init(&hub_dev->ports[port], hub_dev, port + 1);
415 }
416
417 hub_dev->power_switched =
418 !(descriptor.characteristics & HUB_CHAR_NO_POWER_SWITCH_FLAG);
419 hub_dev->per_port_power =
420 descriptor.characteristics & HUB_CHAR_POWER_PER_PORT_FLAG;
421
422 const uint8_t protocol = usb_device_descriptors(hub_dev->usb_device)
423 ->device.device_protocol;
424 hub_dev->mtt_available = (protocol == 2);
425
426 usb_hub_power_ports(hub_dev);
427
428 return EOK;
429}
430
431/**
432 * Set configuration of and USB device
433 *
434 * Check whether there is at least one configuration and sets the first one.
435 * This function should be run prior to running any hub-specific action.
436 * @param usb_device usb device representation
437 * @return error code
438 */
439static errno_t usb_set_first_configuration(usb_device_t *usb_device)
440{
441 assert(usb_device);
442 /* Get number of possible configurations from device descriptor */
443 const size_t configuration_count =
444 usb_device_descriptors(usb_device)->device.configuration_count;
445 usb_log_debug("Hub has %zu configurations.", configuration_count);
446
447 if (configuration_count < 1) {
448 usb_log_error("There are no configurations available");
449 return EINVAL;
450 }
451
452 const size_t config_size =
453 usb_device_descriptors(usb_device)->full_config_size;
454 const usb_standard_configuration_descriptor_t *config_descriptor =
455 usb_device_descriptors(usb_device)->full_config;
456
457 if (config_size < sizeof(usb_standard_configuration_descriptor_t)) {
458 usb_log_error("Configuration descriptor is not big enough"
459 " to fit standard configuration descriptor.\n");
460 return EOVERFLOW;
461 }
462
463 /* Set configuration. Use the configuration that was in
464 * usb_device->descriptors.configuration i.e. The first one. */
465 errno_t opResult = usb_request_set_configuration(
466 usb_device_get_default_pipe(usb_device),
467 config_descriptor->configuration_number);
468 if (opResult != EOK) {
469 usb_log_error("Failed to set hub configuration: %s.",
470 str_error(opResult));
471 } else {
472 usb_log_debug("\tUsed configuration %d",
473 config_descriptor->configuration_number);
474 }
475
476 return opResult;
477}
478
479/**
480 * Process hub over current change
481 *
482 * This means either to power off the hub or power it on.
483 * @param hub_dev hub instance
484 * @param status hub status bitmask
485 * @return error code
486 */
487static void usb_hub_over_current(const usb_hub_dev_t *hub_dev,
488 usb_hub_status_t status)
489{
490 if (status & USB_HUB_STATUS_OVER_CURRENT) {
491 /* Hub should remove power from all ports if it detects OC */
492 usb_log_warning("(%p) Detected hub over-current condition, "
493 "all ports should be powered off.", hub_dev);
494 return;
495 }
496
497 /* Ports are always powered. */
498 if (!hub_dev->power_switched)
499 return;
500
501 /* Over-current condition is gone, it is safe to turn the ports on. */
502 for (size_t port = 0; port < hub_dev->port_count; ++port) {
503 const errno_t ret = usb_hub_set_port_feature(hub_dev, port,
504 USB_HUB_FEATURE_PORT_POWER);
505 if (ret != EOK) {
506 usb_log_warning("(%p-%u): HUB OVER-CURRENT GONE: Cannot"
507 " power on port: %s\n", hub_dev,
508 hub_dev->ports[port].port_number, str_error(ret));
509 } else {
510 if (!hub_dev->per_port_power)
511 return;
512 }
513 }
514}
515
516/**
517 * Set feature on the real hub port.
518 *
519 * @param port Port structure.
520 * @param feature Feature selector.
521 */
522errno_t usb_hub_set_depth(const usb_hub_dev_t *hub)
523{
524 assert(hub);
525
526 /* Slower hubs do not care about depth */
527 if (hub->speed < USB_SPEED_SUPER)
528 return EOK;
529
530 const usb_device_request_setup_packet_t set_request = {
531 .request_type = USB_HUB_REQ_TYPE_SET_HUB_DEPTH,
532 .request = USB_HUB_REQUEST_SET_HUB_DEPTH,
533 .value = uint16_host2usb(usb_device_get_depth(hub->usb_device) - 1),
534 .index = 0,
535 .length = 0,
536 };
537 return usb_pipe_control_write(hub->control_pipe, &set_request,
538 sizeof(set_request), NULL, 0);
539}
540
541/**
542 * Set feature on the real hub port.
543 *
544 * @param port Port structure.
545 * @param feature Feature selector.
546 */
547errno_t usb_hub_set_port_feature(const usb_hub_dev_t *hub, size_t port_number,
548 usb_hub_class_feature_t feature)
549{
550 assert(hub);
551 const usb_device_request_setup_packet_t clear_request = {
552 .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE,
553 .request = USB_DEVREQ_SET_FEATURE,
554 .index = uint16_host2usb(port_number),
555 .value = feature,
556 .length = 0,
557 };
558 return usb_pipe_control_write(hub->control_pipe, &clear_request,
559 sizeof(clear_request), NULL, 0);
560}
561
562/**
563 * Clear feature on the real hub port.
564 *
565 * @param port Port structure.
566 * @param feature Feature selector.
567 */
568errno_t usb_hub_clear_port_feature(const usb_hub_dev_t *hub, size_t port_number,
569 usb_hub_class_feature_t feature)
570{
571 assert(hub);
572 const usb_device_request_setup_packet_t clear_request = {
573 .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE,
574 .request = USB_DEVREQ_CLEAR_FEATURE,
575 .value = feature,
576 .index = uint16_host2usb(port_number),
577 .length = 0,
578 };
579 return usb_pipe_control_write(hub->control_pipe,
580 &clear_request, sizeof(clear_request), NULL, 0);
581}
582
583/**
584 * Retrieve port status.
585 *
586 * @param[in] port Port structure
587 * @param[out] status Where to store the port status.
588 * @return Error code.
589 */
590errno_t usb_hub_get_port_status(const usb_hub_dev_t *hub, size_t port_number,
591 usb_port_status_t *status)
592{
593 assert(hub);
594 assert(status);
595
596 /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6
597 * Generic GET_STATUS request cannot be used because of the difference
598 * in status data size (2B vs. 4B)*/
599 const usb_device_request_setup_packet_t request = {
600 .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS,
601 .request = USB_HUB_REQUEST_GET_STATUS,
602 .value = 0,
603 .index = uint16_host2usb(port_number),
604 .length = sizeof(usb_port_status_t),
605 };
606 size_t recv_size;
607
608 uint32_t buffer;
609 const errno_t rc = usb_pipe_control_read(hub->control_pipe,
610 &request, sizeof(usb_device_request_setup_packet_t),
611 &buffer, sizeof(buffer), &recv_size);
612 if (rc != EOK)
613 return rc;
614
615 if (recv_size != sizeof(*status))
616 return ELIMIT;
617
618 *status = uint32_usb2host(buffer);
619 return EOK;
620}
621
622/**
623 * Process hub interrupts.
624 *
625 * The change can be either in the over-current condition or local-power change.
626 * @param hub_dev hub instance
627 */
628static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev)
629{
630 assert(hub_dev);
631 assert(hub_dev->usb_device);
632 usb_log_debug("(%p): Global interrupt on th hub.", hub_dev);
633 usb_pipe_t *control_pipe =
634 usb_device_get_default_pipe(hub_dev->usb_device);
635
636 usb_hub_status_t status;
637 size_t rcvd_size;
638 /* NOTE: We can't use standard USB GET_STATUS request, because
639 * hubs reply is 4byte instead of 2 */
640 const errno_t opResult = usb_pipe_control_read(control_pipe,
641 &get_hub_status_request, sizeof(get_hub_status_request),
642 &status, sizeof(usb_hub_status_t), &rcvd_size);
643 if (opResult != EOK) {
644 usb_log_error("(%p): Could not get hub status: %s.", hub_dev,
645 str_error(opResult));
646 return;
647 }
648 if (rcvd_size != sizeof(usb_hub_status_t)) {
649 usb_log_error("(%p): Received status has incorrect size: "
650 "%zu != %zu", hub_dev, rcvd_size, sizeof(usb_hub_status_t));
651 return;
652 }
653
654 /* Handle status changes */
655 if (status & USB_HUB_STATUS_C_OVER_CURRENT) {
656 usb_hub_over_current(hub_dev, status);
657 /* Ack change in hub OC flag */
658 const errno_t ret = usb_request_clear_feature(
659 control_pipe, USB_REQUEST_TYPE_CLASS,
660 USB_REQUEST_RECIPIENT_DEVICE,
661 USB_HUB_FEATURE_C_HUB_OVER_CURRENT, 0);
662 if (ret != EOK) {
663 usb_log_error("(%p): Failed to clear hub over-current "
664 "change flag: %s.\n", hub_dev, str_error(opResult));
665 }
666 }
667
668 if (status & USB_HUB_STATUS_C_LOCAL_POWER) {
669 /* NOTE: Handling this is more complicated.
670 * If the transition is from bus power to local power, all
671 * is good and we may signal the parent hub that we don't
672 * need the power.
673 * If the transition is from local power to bus power
674 * the hub should turn off all the ports and devices need
675 * to be reinitialized taking into account the limited power
676 * that is now available.
677 * There is no support for power distribution in HelenOS,
678 * (or other OSes/hub devices that I've seen) so this is not
679 * implemented.
680 * Just ACK the change.
681 */
682 const errno_t ret = usb_request_clear_feature(
683 control_pipe, USB_REQUEST_TYPE_CLASS,
684 USB_REQUEST_RECIPIENT_DEVICE,
685 USB_HUB_FEATURE_C_HUB_LOCAL_POWER, 0);
686 if (opResult != EOK) {
687 usb_log_error("(%p): Failed to clear hub power change "
688 "flag: %s.\n", hub_dev, str_error(ret));
689 }
690 }
691}
692
693/**
694 * Instead of just sleeping, we may as well sleep on a condition variable.
695 * This has the advantage that we may instantly wait other hub from the polling
696 * sleep, mitigating the delay of polling while still being synchronized with
697 * other devices in need of the default address (there shall not be any).
698 */
699static FIBRIL_CONDVAR_INITIALIZE(global_hub_default_address_cv);
700static FIBRIL_MUTEX_INITIALIZE(global_hub_default_address_guard);
701
702/**
703 * Reserve a default address for a port across all other devices connected to
704 * the bus. We aggregate requests for ports to minimize delays between
705 * connecting multiple devices from one hub - which happens e.g. when the hub
706 * is connected with already attached devices.
707 */
708errno_t usb_hub_reserve_default_address(usb_hub_dev_t *hub, async_exch_t *exch,
709 usb_port_t *port)
710{
711 assert(hub);
712 assert(exch);
713 assert(port);
714 assert(fibril_mutex_is_locked(&port->guard));
715
716 errno_t err = usbhc_reserve_default_address(exch);
717 /*
718 * EINVAL signalls that its our hub (hopefully different port) that has
719 * this address reserved
720 */
721 while (err == EAGAIN || err == EINVAL) {
722 /* Drop the port guard, we're going to wait */
723 fibril_mutex_unlock(&port->guard);
724
725 /* This sleeping might be disturbed by other hub */
726 fibril_mutex_lock(&global_hub_default_address_guard);
727 fibril_condvar_wait_timeout(&global_hub_default_address_cv,
728 &global_hub_default_address_guard, 2000000);
729 fibril_mutex_unlock(&global_hub_default_address_guard);
730
731 fibril_mutex_lock(&port->guard);
732 err = usbhc_reserve_default_address(exch);
733 }
734
735 if (err)
736 return err;
737
738 /*
739 * As we dropped the port guard, we need to check whether the device is
740 * still connected. If the release fails, we still hold the default
741 * address -- but then there is probably a bigger problem with the HC
742 * anyway.
743 */
744 if (port->state != PORT_CONNECTING) {
745 err = usb_hub_release_default_address(hub, exch);
746 return err ? err : EINTR;
747 }
748
749 return EOK;
750}
751
752/**
753 * Release the default address from a port.
754 */
755errno_t usb_hub_release_default_address(usb_hub_dev_t *hub, async_exch_t *exch)
756{
757 const errno_t ret = usbhc_release_default_address(exch);
758
759 /*
760 * This is an optimistic optimization - it may wake
761 * one hub from polling sleep instantly.
762 */
763 fibril_condvar_signal(&global_hub_default_address_cv);
764
765 return ret;
766}
767
768/**
769 * @}
770 */
Note: See TracBrowser for help on using the repository browser.