rt-thread/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.c

516 lines
16 KiB
C
Raw Normal View History

/*
* 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("Usb hub: 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;
}