2022-11-10 22:22:48 +08:00
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* ----- ------ -------- --------------------------------------
|
2023-05-11 10:25:21 +08:00
|
|
|
|
* 1.0 zhugengyu 2022/2/7 init commit
|
2022-11-10 22:22:48 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#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 &&
|
2023-05-11 10:25:21 +08:00
|
|
|
|
dev->endpoints[i].direction == FUSB_IN)
|
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return &dev->endpoints[i];
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_RESET;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_ENABLE;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
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))
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return dev->speed;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
/*[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 */
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return speed;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
if (buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
FUsbClearFeature(dev, port, FUSB_HUB_SEL_C_PORT_CONNECTION, DR_PORT);
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
if (buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_CONNECTION)
|
|
|
|
|
{
|
2023-05-11 10:25:21 +08:00
|
|
|
|
FUSB_INFO("Usb hub: Port coldplug at %d ", port);
|
2022-11-10 22:22:48 +08:00
|
|
|
|
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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return ret;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
FUSB_DEBUG("Spurious change bit at port %d ", port);
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
|
|
|
|
|
/* 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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
buf[i] |= ibuf[i];
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2023-05-11 10:25:21 +08:00
|
|
|
|
{
|
2022-11-10 22:22:48 +08:00
|
|
|
|
return;
|
2023-05-11 10:25:21 +08:00
|
|
|
|
}
|
2022-11-10 22:22:48 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|