| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- * 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_hub.c
- * Date: 2022-02-11 13:33:07
- * LastEditTime: 2022-02-17 17:48:52
- * Description: This files is for implmentation of USB hub function
- * you may refer to chapter 11 Hub specification for details
- *
- * Modify History:
- * Ver Who Date Changes
- * ----- ------ -------- --------------------------------------
- * 1.0 Zhugengyu 2022/2/7 init commit
- */
- #include "fkernel.h"
- #include "fdebug.h"
- #include "fassert.h"
- #include "fusb_private.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__)
- /* assume that FUSB_REQ_HOST_TO_DEVICE is overwritten if necessary */
- #define DR_PORT FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_OTHER) /* 10100011B */
- #define DR_HUB FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV) /* 10100000B */
- #define FUSB_HUB_PORT_STATUS 0
- #define FUSB_HUB_PORT_CHANGE 1
- #define FUSB_HUB_PORT_BUF_LEN 2
- /* status (and status change) bits, refer to Table 10-10, Port Status Field in USB spec */
- #define FUSB_HUB_STATUS_PORT_CONNECTION BIT(0) /* reflects if device is currently connected to this port */
- #define FUSB_HUB_STATUS_PORT_ENABLE BIT(1) /* reflects if this port is enabled */
- #define FUSB_HUB_STATUS_PORT_SUSPEND BIT(2) /* reflects if this port is suspend, only for USB2 */
- #define FUSB_HUB_STATUS_PORT_OVER_CURRENT BIT(3) /* reports over-current conditions in this port */
- #define FUSB_HUB_STATUS_PORT_RESET BIT(4) /* reset signaling asserted */
- #define FUSB_HUB_STATUS_BH_PORT_RESET BIT(5) /* warm reset completed */
- #define FUSB_HUB_STATUS_PORT_LINK_STATE BIT(6) /* link state changed */
- #define FUSB_HUB_STATUS_PORT_PORT_CONFIG_ERROR BIT(7) /* port fails to config */
- /* feature selectors (for setting / clearing features), refer to USB spec. Table 10-17. Hub Class Feature Selectors for details */
- #define FUSB_HUB_SEL_PORT_RESET 4
- #define FUSB_HUB_SEL_PORT_POWER 8
- #define FUSB_HUB_SEL_C_PORT_CONNECTION 16
- #define FUSB_HUB_SEL_C_PORT_ENABLE 17 /* USB2 only */
- #define FUSB_HUB_SEL_C_PORT_SUSPEND 18 /* USB2 only */
- #define FUSB_HUB_SEL_C_PORT_OVER_CURRENT 19
- #define FUSB_HUB_SEL_C_PORT_RESET 20
- #define FUSB_HUB_SEL_C_PORT_LINK_STATE 25
- #define FUSB_HUB_SEL_C_PORT_CONFIG_ERROR 26
- #define FUSB_HUB_SEL_C_BH_PORT_RESET 27
- /* request type (USB 3.0 hubs only) */
- #define SET_HUB_DEPTH 12
- /**
- * @name: FUsbHubIntrEp
- * @msg: 获取USB Hub的中断端点
- * @return {FUsbEndpoint *} 中断类型的功能端点
- * @param {FUsbDev} *dev, Hub实例
- */
- static FUsbEndpoint *FUsbHubIntrEp(FUsbDev *const dev)
- {
- FASSERT(dev);
- int i;
- for (i = 0; i < dev->num_endp; ++i)
- {
- if (dev->endpoints[i].type == FUSB_INTERRUPT_EP &&
- dev->endpoints[i].direction == FUSB_IN)
- return &dev->endpoints[i];
- }
- return NULL;
- }
- /**
- * @name: FUsbHubPortStatusChange
- * @msg: Usb Hub的Port状态变化回调函数
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, Port号
- */
- static FUsbTransCode FUsbHubPortStatusChange(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret >= FUSB_CC_ZERO_BYTES)
- {
- ret = buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION;
- if (ret)
- {
- ret = FUsbClearFeature(dev, port, FUSB_HUB_SEL_C_PORT_CONNECTION, DR_PORT);
- }
- }
- return ret;
- }
- /**
- * @name: FUsbHubPortConnected
- * @msg: Usb Hub的Port连接回调函数
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, Port号
- */
- static FUsbTransCode FUsbHubPortConnected(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret >= FUSB_CC_ZERO_BYTES)
- {
- ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_CONNECTION;
- }
- return ret;
- }
- /**
- * @name: FUsbHubPortInReset
- * @msg: 检查Hub port是否处于Reset状态
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, Port号
- */
- static FUsbTransCode FUsbHubPortInReset(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret >= FUSB_CC_ZERO_BYTES)
- ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_RESET;
- return ret;
- }
- /**
- * @name: FUsbHubPortEnabled
- * @msg: 检查Hub port是否已使能
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, Port号
- */
- static FUsbTransCode FUsbHubPortEnabled(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret >= FUSB_CC_ZERO_BYTES)
- ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_ENABLE;
- return ret;
- }
- /**
- * @name: FUsbHubPortSpeed
- * @msg: 获取Hub port的速度类型
- * @return {FUsbSpeed} Port的速度类型,支持SuperSpeed和HighSpeed
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, Port号
- */
- static FUsbSpeed FUsbHubPortSpeed(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- int speed;
- if (ret >= FUSB_CC_ZERO_BYTES && (buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_ENABLE))
- {
- /* SuperSpeed hubs can only have SuperSpeed devices. */
- if (FUsbIsSuperSpeed(dev->speed))
- return dev->speed;
- /*[bit] 10 9 (USB 2.0 port status word)
- * 0 0 full speed
- * 0 1 low speed
- * 1 0 high speed
- * 1 1 invalid
- */
- speed = (buf[FUSB_HUB_PORT_STATUS] >> 9) & 0x3;
- if (speed != 0x3) /* high-speed device */
- return speed;
- }
- return FUSB_UNKNOWN_SPEED;
- }
- /**
- * @name: FUsbHubEnablePort
- * @msg: 使能Hub port
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, port号
- */
- static FUsbTransCode FUsbHubEnablePort(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- return FUsbSetFeature(dev, port, FUSB_HUB_SEL_PORT_POWER, DR_PORT);
- }
- /**
- * @name: FUsbHubStartPortReset
- * @msg: 开始Reset Hub port
- * @return {FUsbTransCode} USB请求返回值
- * @param {FUsbDev} *dev, Hub实例
- * @param {int} port, port号
- */
- static FUsbTransCode FUsbHubStartPortReset(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- return FUsbSetFeature(dev, port, FUSB_HUB_SEL_PORT_RESET, DR_PORT);
- }
- static void FUsbHubSetHubDepth(FUsbDev *const dev)
- {
- FASSERT(dev);
- FUsbDevReq dr =
- {
- .bmRequestType = FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE,
- FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV),
- .bRequest = SET_HUB_DEPTH,
- .wValue = 0,
- .wIndex = 0,
- .wLength = 0,
- };
- FUsbDev *parent = dev;
- FASSERT(dev->controller);
- while (parent->hub > 0)
- {
- FASSERT(dev->controller->devices[parent->hub]);
- parent = dev->controller->devices[parent->hub];
- dr.wValue++;
- }
- FUsbTransCode ret = dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, NULL);
- if (ret < FUSB_CC_ZERO_BYTES)
- {
- FUSB_ERROR("Failed SET_HUB_DEPTH(%d) on hub %d: %d ",
- dr.wValue, dev->address, ret);
- }
- return;
- }
- static const FUsbGenericHubOps FUSB_HUB_OPS =
- {
- .hub_status_changed = NULL,
- .port_status_changed = FUsbHubPortStatusChange,
- .port_connected = FUsbHubPortConnected,
- .port_in_reset = FUsbHubPortInReset,
- .port_enabled = FUsbHubPortEnabled,
- .port_speed = FUsbHubPortSpeed,
- .enable_port = FUsbHubEnablePort,
- .disable_port = NULL,
- .start_port_reset = FUsbHubStartPortReset,
- .reset_port = FUsbGenericHubResetPort,
- };
- /* Clear CSC if set and enumerate port if it's connected regardless of change
- bits. Some broken hubs don't set CSC if already connected during reset. */
- static void FUsbHubPortInit(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */
- FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret < FUSB_CC_ZERO_BYTES)
- return;
- if (buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION)
- FUsbClearFeature(dev, port, FUSB_HUB_SEL_C_PORT_CONNECTION, DR_PORT);
- if (buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_CONNECTION)
- {
- FUSB_INFO("usbhub: Port coldplug at %d ", port);
- FUsbGenericHubScanPort(dev, port);
- }
- return;
- }
- /**
- * @name: FUsbHubHandlePortChange
- * @msg: Hub端口状态变化的处理回调函数
- * @return {*}
- * @param {FUsbDev} *dev
- * @param {int} port
- */
- static FUsbTransCode FUsbHubHandlePortChange(FUsbDev *const dev, const int port)
- {
- FASSERT(dev);
- static const struct
- {
- unsigned short change_bit;
- unsigned short clear_sel;
- } change_bits[] =
- {
- {FUSB_HUB_STATUS_PORT_CONNECTION, FUSB_HUB_SEL_C_PORT_CONNECTION},
- {FUSB_HUB_STATUS_PORT_ENABLE, FUSB_HUB_SEL_C_PORT_ENABLE},
- {FUSB_HUB_STATUS_PORT_SUSPEND, FUSB_HUB_SEL_C_PORT_SUSPEND},
- {FUSB_HUB_STATUS_PORT_OVER_CURRENT, FUSB_HUB_SEL_C_PORT_OVER_CURRENT},
- {FUSB_HUB_STATUS_PORT_RESET, FUSB_HUB_SEL_C_PORT_RESET},
- {FUSB_HUB_STATUS_BH_PORT_RESET, FUSB_HUB_SEL_C_BH_PORT_RESET},
- {FUSB_HUB_STATUS_PORT_LINK_STATE, FUSB_HUB_SEL_C_PORT_LINK_STATE},
- {FUSB_HUB_STATUS_PORT_PORT_CONFIG_ERROR, FUSB_HUB_SEL_C_PORT_CONFIG_ERROR},
- };
- FUsbTransCode ret = 0;
- unsigned int i;
- unsigned short checked_bits = 0;
- unsigned short buf[FUSB_HUB_PORT_BUF_LEN] = {0, 0}; /* Hub Status and Change Status */
- ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf);
- if (ret < FUSB_CC_ZERO_BYTES)
- return ret;
- /*
- * Second word holds the change bits. The interrupt transfer shows
- * a logical or of these bits, so we have to clear them all.
- */
- for (i = 0; i < ARRAY_SIZE(change_bits); ++i)
- {
- if (buf[FUSB_HUB_PORT_CHANGE] & change_bits[i].change_bit)
- {
- /* clear feature if specific change bit = 1 */
- FUsbClearFeature(dev, port, change_bits[i].clear_sel, DR_PORT);
- }
- checked_bits |= change_bits[i].change_bit;
- }
- if (buf[FUSB_HUB_PORT_CHANGE] & ~checked_bits)
- FUSB_DEBUG("Spurious change bit at port %d ", port);
- /* Now, handle connection changes. */
- if (buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION)
- {
- FUSB_DEBUG("Port change at %d ", port);
- ret = FUsbGenericHubScanPort(dev, port);
- }
- return ret;
- }
- /**
- * @name: FUsbHubPoll
- * @msg: 轮询Hub的所有端口,检查端口状态变化
- * @return {*}
- * @param {FUsbDev} *dev, Hub设备实例
- */
- static void FUsbHubPoll(FUsbDev *const dev)
- {
- FASSERT(dev);
- int port, i;
- u8 buf[32] = {0};
- const u8 *ibuf;
- /* First, gather all change bits from finished interrupt transfers. */
- const size_t port_bytes = min(ARRAY_SIZE(buf),
- (size_t)DIV_ROUND_UP(FUSB_GEN_HUB_GET(dev)->num_ports + 1, 8));
- while (NULL != (ibuf = dev->controller->poll_intr_queue(FUSB_GEN_HUB_GET(dev)->data)))
- {
- for (i = 0; (size_t)i < port_bytes; ++i)
- buf[i] |= ibuf[i];
- }
- for (port = 1; port <= FUSB_GEN_HUB_GET(dev)->num_ports; ++port)
- {
- /* ports start at bit1; bit0 is hub status change */
- if (buf[port / 8] & (1 << (port % 8)))
- {
- if (FUsbHubHandlePortChange(dev, port) < 0)
- return;
- }
- }
- return;
- }
- /**
- * @name: FUsbHubDestory
- * @msg: USB Hub的去初始化函数
- * @return {*}
- * @param {FUsbDev} *dev, Hub设备实例
- */
- static void FUsbHubDestory(FUsbDev *const dev)
- {
- FASSERT(dev);
- FUsbEndpoint *const intr_ep = FUsbHubIntrEp(dev);
- FASSERT(intr_ep); /* interrupt ep must exists */
- dev->controller->destroy_intr_queue(intr_ep, FUSB_GEN_HUB_GET(dev)->data);
- FUsbGenericHubDestory(dev);
- }
- /**
- * @name: FUsbHubInit
- * @msg: USB Hub的初始化函数,由应用程序注册到FUSB框架中
- * @return {*}
- * @param {FUsbDev} *dev, Hub设备实例
- */
- void FUsbHubInit(FUsbDev *dev)
- {
- FASSERT(dev);
- FUsbEndpoint *const intr_ep = FUsbHubIntrEp(dev); /* get the first intrrupt ep found */
- if (NULL == intr_ep)
- {
- FUSB_ERROR("No interrupt-in endpoint found ");
- return;
- }
- FASSERT(dev->controller);
- /* Get number of ports from hub descriptor */
- int type = FUsbIsSuperSpeed(dev->speed) ? FUSB_DESC_TYPE_SUPER_SPEED_HUB : FUSB_DESC_TYPE_HUB; /* similar enough */
- FUsbHubDescriptor desc; /* won't fit the whole thing, we don't care */
- if (FUsbGetDescriptor(dev, FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV), type, 0, &desc, sizeof(desc)) != sizeof(desc))
- {
- FUSB_ERROR("FUsbGetDescriptor(HUB) failed ");
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- if (FUsbIsSuperSpeed(dev->speed))
- {
- FUsbHubSetHubDepth(dev);
- }
- /*
- * Register interrupt transfer:
- * one bit per port + one bit for the hub,
- * 20 transfers in the queue, like our HID driver,
- * one transfer per 256ms
- */
- void *const intrq = dev->controller->create_intr_queue(
- intr_ep, intr_ep->maxpacketsize, 20, 256);
- if (NULL == intrq)
- {
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- /*
- * Limit the number of ports by the max packet size of
- * the interrupt endpoint. This shouldn't be necessary
- * but prevents a potential overflow in FUsbHubPoll().
- */
- const unsigned int num_ports =
- min((int)desc.bNbrPorts, intr_ep->maxpacketsize * 8 - 1);
- if (FUsbGenericHubInit(dev, num_ports, &FUSB_HUB_OPS))
- {
- dev->controller->destroy_intr_queue(intr_ep, intrq);
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- unsigned int port;
- for (port = 1; port <= num_ports; ++port)
- {
- FUsbHubPortInit(dev, port);
- }
- FUSB_GEN_HUB_GET(dev)->data = intrq;
- dev->poll = FUsbHubPoll;
- dev->destroy = FUsbHubDestory;
- return;
- }
|