| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- /*
- * Copyright : (C) 2022 Phytium Information Technology, Inc.
- * All Rights Reserved.
- *
- * This program is OPEN SOURCE software: you can redistribute it and/or modify it
- * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
- * either version 1.0 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the Phytium Public License for more details.
- *
- *
- * FilePath: fusb_generic_hub.c
- * Date: 2022-02-11 13:33:11
- * LastEditTime: 2022-02-18 09:19:27
- * Description: This files is for implmentation of generic hub function
- *
- * Modify History:
- * Ver Who Date Changes
- * ----- ------ -------- --------------------------------------
- * 1.0 Zhugengyu 2022/2/7 init commit
- */
- #include "fsleep.h"
- #include "fdebug.h"
- #include "fusb.h"
- #include "fusb_generic_hub.h"
- #define FUSB_DEBUG_TAG "FUSB_HUB"
- #define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- void FUsbGenericHubDestory(FUsbDev *const dev)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- FUsb *instace = dev->controller->usb;
- if (!hub)
- return;
- /* First, detach all devices behind this hub */
- int port;
- for (port = 1; port <= hub->num_ports; ++port)
- {
- if (hub->ports[port] >= 0)
- {
- FUSB_INFO("generic_hub: Detachment at port %d ", port);
- FUsbDetachDev(dev->controller, hub->ports[port]);
- hub->ports[port] = FUSB_NO_DEV_ADDR;
- }
- }
- /* Disable all ports */
- if (hub->ops->disable_port)
- {
- for (port = 1; port <= hub->num_ports; ++port)
- hub->ops->disable_port(dev, port);
- }
- FUSB_FREE(instace, hub->ports); // free(hub->ports);
- FUSB_FREE(instace, hub); // free(hub);
- }
- static int FUsbGenericHubDebounce(FUsbDev *const dev, const int port)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- const int step_ms = 1; /* linux uses 25ms, we're busy anyway */
- const int at_least_ms = 100; /* 100ms as in usb20 spec 9.1.2 */
- const int timeout_ms = 1500; /* linux uses this value */
- int total_ms = 0;
- int stable_ms = 0;
- while (stable_ms < at_least_ms && total_ms < timeout_ms)
- {
- fsleep_millisec(step_ms);
- const int changed = hub->ops->port_status_changed(dev, port);
- const int connected = hub->ops->port_connected(dev, port);
- if (changed < 0 || connected < 0)
- return -1;
- if (!changed && connected)
- {
- stable_ms += step_ms;
- }
- else
- {
- FUSB_INFO("generic_hub: Unstable connection at %d ",
- port);
- stable_ms = 0;
- }
- total_ms += step_ms;
- }
- if (total_ms >= timeout_ms)
- FUSB_INFO("generic_hub: Debouncing timed out at %d ", port);
- return 0; /* ignore timeouts, try to always go on */
- }
- int FUsbGenericHubWaitForPort(FUsbDev *const dev, const int port,
- const int wait_for,
- int (*const port_op)(FUsbDev *, int),
- int timeout_steps, const int step_us)
- {
- int state;
- do
- {
- state = port_op(dev, port);
- if (state < 0)
- return -1;
- else if (!!state == wait_for)
- return timeout_steps;
- fsleep_microsec(step_us);
- --timeout_steps;
- }
- while (timeout_steps);
- return 0;
- }
- int FUsbGenericHubResetPort(FUsbDev *const dev, const int port)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- if (hub->ops->start_port_reset(dev, port) < 0)
- return -1;
- /* wait for 10ms (usb20 spec 11.5.1.5: reset should take 10 to 20ms) */
- fsleep_millisec(10);
- /* now wait 12ms for the hub to finish the reset */
- const int ret = FUsbGenericHubWaitForPort(
- /* time out after 120 * 100us = 12ms */
- dev, port, 0, hub->ops->port_in_reset, 120, 100);
- if (ret < 0)
- return -1;
- else if (!ret)
- FUSB_INFO("generic_hub: Reset timed out at port %d ", port);
- return 0; /* ignore timeouts, try to always go on */
- }
- static int FUsbGenericHubDetachDev(FUsbDev *const dev, const int port)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- FUsbDetachDev(dev->controller, hub->ports[port]);
- hub->ports[port] = FUSB_NO_DEV_ADDR;
- return 0;
- }
- static int FUsbGenericHubAttachDev(FUsbDev *const dev, const int port)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- if (FUsbGenericHubDebounce(dev, port) < 0)
- return -1;
- if (hub->ops->reset_port)
- {
- if (hub->ops->reset_port(dev, port) < 0)
- return -1;
- if (!hub->ops->port_connected(dev, port))
- {
- FUSB_INFO(
- "generic_hub: Port %d disconnected after "
- "reset. Possibly upgraded, rescan required. ",
- port);
- return 0;
- }
- /* after reset the port will be enabled automatically */
- const int ret = FUsbGenericHubWaitForPort(
- /* time out after 1,000 * 10us = 10ms */
- dev, port, 1, hub->ops->port_enabled, 1000, 10);
- if (ret < 0)
- return -1;
- else if (!ret)
- FUSB_INFO("generic_hub: Port %d still "
- "disabled after 10ms ",
- port);
- }
- const FUsbSpeed speed = hub->ops->port_speed(dev, port);
- if (speed >= 0)
- {
- FUSB_DEBUG("generic_hub: Success at port %d ", port);
- if (hub->ops->reset_port)
- fsleep_millisec(10); /* Reset recovery time
- (usb20 spec 7.1.7.5) */
- hub->ports[port] = FUsbAttachDev(
- dev->controller, dev->address, port, speed);
- }
- return 0;
- }
- int FUsbGenericHubScanPort(FUsbDev *const dev, const int port)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- if (hub->ports[port] >= 0)
- {
- FUSB_INFO("generic_hub: Detachment at port %d ", port);
- const int ret = FUsbGenericHubDetachDev(dev, port);
- if (ret < 0)
- return ret;
- }
- if (hub->ops->port_connected(dev, port))
- {
- FUSB_INFO("generic_hub: Attachment at port %d ", port);
- return FUsbGenericHubAttachDev(dev, port);
- }
- return 0;
- }
- static void FUsbGenericHubPoll(FUsbDev *const dev)
- {
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- if (!hub)
- return;
- if (hub->ops->hub_status_changed &&
- hub->ops->hub_status_changed(dev) != FUSB_CC_SUCCESS)
- {
- return;
- }
- int port;
- for (port = 1; port <= hub->num_ports; ++port)
- {
- const FUsbTransCode ret = hub->ops->port_status_changed(dev, port);
- if (ret < 0)
- {
- FUSB_WARN("Transcode %d", ret);
- return;
- }
- else if (ret == FUSB_CC_SUCCESS)
- {
- FUSB_INFO("generic_hub: Port change at %d ", port);
- if (FUsbGenericHubScanPort(dev, port) < 0)
- return;
- }
- }
- }
- int FUsbGenericHubInit(FUsbDev *const dev, const int num_ports,
- const FUsbGenericHubOps *const ops)
- {
- int port;
- FUsb *instance = dev->controller->usb;
- dev->destroy = FUsbGenericHubDestory;
- dev->poll = FUsbGenericHubPoll;
- FASSERT(NULL == dev->data);
- dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbGenericHub), FUSB_DEFAULT_ALIGN);
- if (NULL == dev->data)
- {
- FUSB_ERROR("generic_hub: ERROR: Out of memory ");
- return -1;
- }
- FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
- hub->num_ports = num_ports;
- FASSERT(NULL == hub->ports);
- hub->ports = FUSB_ALLOCATE(instance, sizeof(*hub->ports) * (num_ports + 1), FUSB_DEFAULT_ALIGN);
- hub->ops = ops;
- if (NULL == hub->ports)
- {
- FUSB_ERROR("generic_hub: ERROR: Out of memory ");
- FUSB_FREE(instance, dev->data);
- dev->data = NULL;
- return -1;
- }
- for (port = 1; port <= num_ports; ++port)
- hub->ports[port] = FUSB_NO_DEV_ADDR;
- /* Enable all ports */
- if (ops->enable_port)
- {
- for (port = 1; port <= num_ports; ++port)
- ops->enable_port(dev, port);
- /* wait once for all ports */
- fsleep_millisec(20);
- }
- return 0;
- }
|