u-boot下usb命令执行过程
这篇文档旨在分析u-boot
下执行usb
命令时整个命令的执行流程,让初学者对u-boot
下usb
相关知识有一定的了解。这里主要从执行的命令出发,一步一步分析代码、协议、寄存器的读写过程。
文章目录
usb组织形式总览
usb设备的逻辑组织中,包含设备、配置、接口和端点4个层次。每个usb设备的都提供不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需要从其中选定一个),配置由多个接口组成。
在u-boot
代码的数据结构组织形式也可以看出:
usb_device
包含了一个配置usb_config
,配置里面包含最多8个接口usb_interface
,接口里面最多有16个端点usb_endpoint_descriptor
。
设备device
,配置config
、接口interface
和端点endpoint
都有各自的描述符,在协议中有规定:
而hub属于特殊的usb设备,还有另外的hub描述符:
u-boot usb命令总览
在u-boot
命令行输入usb
,可以看到以下打印:
usb - USB sub-system
Usage:
usb start - start (scan) USB controller
usb reset - reset (rescan) USB controller
usb stop [f] - stop USB [f]=force stop
usb tree - show USB device tree
usb info [dev] - show available USB devices
usb test [dev] [port] [mode] - set USB 2.0 test mode
(specify port 0 to indicate the device's upstream port)
Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]
usb storage - show details of USB storage devices
usb dev [dev] - show or set current USB storage device
usb part [dev] - print partition table of one or all USB storage devices
usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'
to memory address `addr'
usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'
from memory address `addr'
在代码里,此时执行的是如下代码:
U_BOOT_CMD(
usb, 5, 1, do_usb,
"USB sub-system",
"start - start (scan) USB controller\n"
"usb reset - reset (rescan) USB controller\n"
"usb stop [f] - stop USB [f]=force stop\n"
"usb tree - show USB device tree\n"
"usb info [dev] - show available USB devices\n"
"usb test [dev] [port] [mode] - set USB 2.0 test mode\n"
" (specify port 0 to indicate the device's upstream port)\n"
" Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n"
#ifdef CONFIG_USB_STORAGE
"usb storage - show details of USB storage devices\n"
"usb dev [dev] - show or set current USB storage device\n"
"usb part [dev] - print partition table of one or all USB storage"
" devices\n"
"usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
" to memory address `addr'\n"
"usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
" from memory address `addr'"
#endif /* CONFIG_USB_STORAGE */
);
从代码上可以看到,上面的命令其实应该分为两个部分:
- 一个是通用命令,如:start、reset、info等
- 二是存储相关命令,如:part、read、write等
在u-boot
的命令行下使用usb
设备,需要先输入usb start
,那么接下来就从usb start
开始分析。
从usb start开始
从上面的U_BOOT_CMD( usb, 5, 1, do_usb,xxx)
可以得知,命令子串start
是由do_usb
进行解析的:
static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
...
if (strncmp(argv[1], "start", 5) == 0) {
printf("starting USB...\n");
usb_stop();
do_usb_start();
return 0;
}
...
return CMD_RET_USAGE;
}
命令子串start
是先执行usb_stop()
,再do_usb_start()
,这里选择着重关注do_usb_start()
函数的hub
本身初始化:
static void do_usb_start(void)
{
...
if (usb_init() < 0)
return;
...
}
usb_init()
函数工作内容如下:
- 初始化全局变量
hub_dev
的数据内容 - 初始化全局变量
usb_dev
的数据内容 - 初始化底层控制器
- 为新usb设备申请资源
- 识别新设备
int usb_init(void)
{
struct usb_device *dev;
...
// 清空全局变量`hub_dev`的数据内容
usb_hub_reset();
/* first make all devices unknown */
// 清空全局变量`usb_dev`的数据内容,并将里面的`devnum`赋值为`-1`
for (i = 0; i < USB_MAX_DEVICE; i++) {
memset(&usb_dev[i], 0, sizeof(struct usb_device));
usb_dev[i].devnum = -1;
}
/* init low_level USB */
for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
// 初始化底层控制器
ret = usb_lowlevel_init(i, USB_INIT_HOST, &ctrl);
...
printf("scanning bus %d for devices... ", i);
// 为新usb设备申请资源
ret = usb_alloc_new_device(ctrl, &dev);
if (ret)
break;
/*
* device 0 is always present
* (root hub, so let it analyze)
*/
// 识别新设备
ret = usb_new_device(dev);
if (ret)
usb_free_device(dev->controller);
}
return usb_started ? 0 : -ENODEV;
}
1. 初始化全局变量hub_dev
的数据内容
这个函数一目了然:
void usb_hub_reset(void)
{
usb_hub_index = 0;
/* Zero out global hub_dev in case its re-used again */
memset(hub_dev, 0, sizeof(hub_dev));
}
2. 初始化全局变量usb_dev
的数据内容
在usb_init()
可以看到,清空全局变量usb_dev
的数据内容,并将里面的devnum
赋值为-1
。
3. 初始化底层控制器
初始化底层控制器的usb_lowlevel_init()
函数:
/**
* Intialises the XHCI host controller
* and allocates the necessary data structures
*
* @param index index to the host controller data structure
* @return pointer to the intialised controller
*/
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
{
struct xhci_hccr *hccr;
struct xhci_hcor *hcor;
struct xhci_ctrl *ctrl;
int ret;
*controller = NULL;
// hub初始化,对SOC的控制寄存器的复位、时钟选择等
phy_hiusb_init(index);
// 控制器 xchi 的代码描述,并从SOC的控制器读取数据初始化
if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
return -ENODEV;
// 复位 xhci 控制器, xhci 应该是另外的控制器的协议,这里没有资料
if (xhci_reset(hcor) != 0)
return -ENODEV;
ctrl = &xhcic[index];
ctrl->hccr = hccr;
ctrl->hcor = hcor;
// xchi 控制器的相关初始化
ret = xhci_lowlevel_init(ctrl);
if (ret) {
ctrl->hccr = NULL;
ctrl->hcor = NULL;
} else {
*controller = &xhcic[index];
}
return ret;
}
上面的phy_hiusb_init
是hub初始化,对SOC的控制寄存器的复位、时钟选择等。
void phy_hiusb_init(int index)
{
if (index)
hisi_usb_config();
else
hisi_usb3_config();
hisi_usb_eye_config();
}
EXPORT_SYMBOL(phy_hiusb_init);
void hisi_usb_config(void)
{
unsigned int reg;
/*set usb2 CRG default val*/
// USB2 控制器 UTMI 时钟源选择。 选择 USB2.0 PHY 时钟
// USB2 控制器 UTMI 时钟门控。 打开
// USB3 控制器 SUSPEND 时钟门控。 打开
// USB2 控制器 REF 时钟门控。 打开
// USB2 控制器总线时钟门控。 打开
// USB2 控制器 VCC 软复位请求。 复位
reg = readl(USB3_CTRL_CFG);
reg &= ~(USB2_DEF_CFG_MASK);
reg |= USB2_DEF_CRG;
writel(reg, USB3_CTRL_CFG);
udelay(200);
/*U2 vcc reset*/
// USB2 控制器 VCC 软复位请求。复位
reg = readl(USB3_CTRL_CFG);
reg |= USB2_VCC_SRST_REQ;
writel(reg, USB3_CTRL_CFG);
udelay(100);
/*release TPOR default release*/
// USB2 PHY1 TPOR 软复位请求。不复位
reg = readl(USB2_PHY_CFG);
reg &= ~USB2_PHY1_PORT_TREQ;
writel(reg, USB2_PHY_CFG);
udelay(200);
/*utmi clock sel*/
// USB2 UTMI 时钟源选择。选择内部 60MHz。
reg = readl(USB3_CTRL_CFG);
reg &= ~USB2_UTMI_CKSEL;
writel(reg, USB3_CTRL_CFG);
udelay(200);
/*open phy ref clk default open*/
// USB2 PHY1 REFCLK 门控。打开时钟
reg = readl(USB2_PHY_CFG);
reg |= USB2_PHY1_CKEN;
writel(reg, USB2_PHY_CFG);
udelay(200);
/*U2 phy reset release*/
// USB2 PHY1 POR 软复位请求。不复位
reg = readl(USB2_PHY_CFG);
reg &= ~USB2_PHY1_REQ;
writel(reg, USB2_PHY_CFG);
udelay(200);
/*config U2 Controller release*/
// USB2 控制器 VCC 软复位请求。不复位
reg = readl(USB3_CTRL_CFG);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, USB3_CTRL_CFG);
udelay(100);
/* u2 port default host */
// 端口配置类型。Host 配置。
reg = readl(USB2_CTRL_REG_BASE + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= DEFAULT_HOST_MOD;
writel(reg, USB2_CTRL_REG_BASE + REG_GCTL);
udelay(20);
}
void hisi_usb3_config(void)
{
unsigned int reg;
/*set usb3 CRG default val*/
// USB3 控制器 UTMI 时钟门控。 打开
// USB3 控制器 PIPE 时钟门控。 打开
// USB3 控制器 SUSPEND 时钟门控。 打开
// USB3 控制器 REF 时钟门控。 打开
// USB3 控制器总线时钟门控。 打开
// USB3_0 控制器 VCC 软复位请求。 复位
reg = readl(USB3_CTRL_CFG);
reg &= ~(USB3_DEF_CFG_MASK);
reg |= USB3_DEF_CRG;
writel(reg, USB3_CTRL_CFG);
udelay(200);
/*disable port0 ss*/
// USB PORT0 SuperSpeed 模式禁用。模式关闭。
reg = readl(MISC_REG_BASE + USB_PORT0);
reg |= U3_PORT_DISABLE;
writel(reg, MISC_REG_BASE + USB_PORT0);
/*
* According to description of SYSSYAT register, different
* HW modes of COMBPHY require specific configurations.
*/
reg = readl(REG_BASE_SCTL + REG_SYSSTAT);
if (GET_COMBPHY_MODE(reg) == USB3) {
/* combphy reset in USB3 mode */
// COMBPHY 端口软复位请求。复位。
reg = readl(USB3_COMBPHY_CFG);
reg |= COMBPHY_SRST_REQ;
writel(reg, USB3_COMBPHY_CFG);
udelay(100);
} else if (GET_COMBPHY_MODE(reg) == PCIE_X1) {
/* Choose clock from CRG in PCIE X1 mode */
// USB3 时钟源选择。 选择 CRG 125MHz 时钟。
reg = readl(USB3_CTRL_CFG);
reg |= USB3_PCLK_OCC_SEL;
writel(reg, USB3_CTRL_CFG);
udelay(100);
}
/*release TPOR default release*/
// USB2 PHY0 TPOR 软复位请求。 不复位。
reg = readl(USB2_PHY_CFG);
reg &= ~USB2_PHY0_PORT_TREQ;
writel(reg, USB2_PHY_CFG);
udelay(200);
/*utmi clock sel*/
// USB3 UTMI 时钟源选择。选择 USB2.0 PHY 时钟。
reg = readl(USB3_CTRL_CFG);
reg &= ~USB3_UTMI_CKSEL;
writel(reg, USB3_CTRL_CFG);
udelay(200);
/*open phy ref clk default open*/
// USB2 PHY0 REFCLK 门控。打开时钟。
reg = readl(USB2_PHY_CFG);
reg |= USB2_PHY0_CKEN;
writel(reg, USB2_PHY_CFG);
udelay(200);
/*U2 phy reset release*/
// USB2 PHY0 POR 软复位请求。不复位。
reg = readl(USB2_PHY_CFG);
reg &= ~USB2_PHY0_REQ;
writel(reg, USB2_PHY_CFG);
udelay(200);
/* Release the specific configuration of COMBPHY. */
reg = readl(REG_BASE_SCTL + REG_SYSSTAT);
if (GET_COMBPHY_MODE(reg) == USB3) {
/*open ref CKEN*/
// USB3.0 模式下 打开时钟。
reg = readl(USB3_COMBPHY_CFG);
reg |= COMBPHY0_REF_CKEN;
writel(reg, USB3_COMBPHY_CFG);
udelay(100);
// COMBPHY 端口软复位请求。不复位。
reg = readl(USB3_COMBPHY_CFG);
reg &= ~COMBPHY_SRST_REQ;
writel(reg, USB3_COMBPHY_CFG);
udelay(100);
}
/*config U3 Controller USB3_0 PHY OUTPUT*/
// USB3_0 控制器 VCC 软复位请求。不复位。
reg = readl(USB3_CTRL_CFG);
reg &= ~USB3_VCC_SRST_REQ;
writel(reg, USB3_CTRL_CFG);
udelay(200);
// USB3.0 软复位。复位。
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(200);
/* u3 port default host */
// 端口配置类型。Host 配置。
reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= DEFAULT_HOST_MOD;
writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
udelay(20);
// USB3.0 软复位。不复位。
// USB3.0 PHY 挂起使能。不挂起。
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~SUSPEND_USB3_SS_PHY; //disable suspend
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(20);
// 0x2 TXFIFO 门限值设置,有效值在 1-15 以内。
// 0x31 发送 burst 的最大值,仅在 host 模式下 SuperSpeed 的 bulk,Isochronous 和 Interrupt 传输的 Out 端点时有效,有效值 在 1-16 之间。
writel(PERI_USB3_GTXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
udelay(20);
}
void hisi_usb_eye_config(void)
{
unsigned int reg;
/* port0 phy high-spped DC adjust: 0% --> 4% */
/* port0 pre elec adjust: 0 --> 1x */
// USB2.0 Port0 PHY High-Speed DC 电平调整。 0% --> 4%
// USB2.0 Port0 PHY High-Speed 发送器预加重电流控制。1x 预加重电
reg = readl(MISC_REG_BASE + USB2_PHY0);
reg &= ~USB2_PHY0_TXVREFTUNE;
reg &= ~USB2_PHY0_TXPRE;
reg |= USB2_PHY0_VREF_VAL;
reg |= USB2_PHY0_PRE_VAL;
writel(reg, MISC_REG_BASE + USB2_PHY0);
udelay(100);
/* port1 phy high-spped DC adjust: 0% --> 4% */
/* port1 pre elec adjust: 0 --> 1x */
reg = readl(MISC_REG_BASE + USB2_PHY1);
reg &= ~USB2_PHY1_TXVREFTUNE;
reg &= ~USB2_PHY1_TXPRE;
reg |= USB2_PHY1_VREF_VAL;
reg |= USB2_PHY1_PRE_VAL;
writel(reg, MISC_REG_BASE + USB2_PHY1);
udelay(100);
/* usb3 Tx margin adjust: 0 --> 900mv */
// PIPE 接口发送端余量。
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg &= ~USB3_TX_MARGIN;
reg |= USB3_TX_MARGIN_VAL;
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
}
4. 为新usb设备申请资源
这里的controller
是上面的xchi
控制器初始化完后获得的,devp
是返回调用者的设备资源指针。这里就是向全局变量usb_dev
数组获取资源,并对此设备的父子设备赋值为NULL,控制器赋值为传入的controller
参数。
int usb_alloc_new_device(struct udevice *controller, struct usb_device **devp)
{
int i;
debug("New Device %d\n", dev_index);
if (dev_index == USB_MAX_DEVICE) {
printf("ERROR, too many USB Devices, max=%d\n", USB_MAX_DEVICE);
return -ENOSPC;
}
/* default Address is 0, real addresses start with 1 */
usb_dev[dev_index].devnum = dev_index + 1;
usb_dev[dev_index].maxchild = 0;
for (i = 0; i < USB_MAXCHILDREN; i++)
usb_dev[dev_index].children[i] = NULL;
usb_dev[dev_index].parent = NULL;
usb_dev[dev_index].controller = controller;
dev_index++;
*devp = &usb_dev[dev_index - 1];
return 0;
}
5. 识别新设备
控制器初始化完毕,且为设备申请的资源也有了,就可以开始识别设备流程了。从整理的这个流程来看,识别新设备要走的代码可以说是涉及很多代码:
单是最后的一个usb_control_msg()
函数,也要走下面的代码流程:
从usb_new_device()
来看,主要共作是两个部分:
- 对设备进行设置
- hub探测
此时的初始化,应该都是对hub设备进行的初始化。
/*
* By the time we get here, the device has gotten a new device ID
* and is in the default state. We need to identify the thing and
* get the ball rolling..
*
* Returns 0 for success, != 0 for error.
*/
int usb_new_device(struct usb_device *dev)
{
bool do_read = true;
int err;
/*
* XHCI needs to issue a Address device command to setup
* proper device context structures, before it can interact
* with the device. So a get_descriptor will fail before any
* of that is done for XHCI unlike EHCI.
*/
#ifdef CONFIG_USB_XHCI_HCD
do_read = false;
#endif
err = usb_setup_device(dev, do_read, dev->parent);
if (err)
return err;
/* Now probe if the device is a hub */
err = usb_hub_probe(dev, 0);
if (err < 0)
return err;
return 0;
}
上面的英文注释也解释到:
XHCI 需要发出地址设备命令来设置正确的设备上下文结构,然后才能与设备进行交互。因此,与 EHCI 不同,在 XHCI 完成任何工作之前,get_descriptor都会失败。
1. 对设备进行设置
先看usb_setup_device
函数:
int usb_setup_device(struct usb_device *dev, bool do_read,
struct usb_device *parent)
{
int addr;
int ret;
/* We still haven't set the Address yet */
addr = dev->devnum;
dev->devnum = 0;
// 向 xchi 控制器申请 slot 和 相关数据结构,设置设备描述符,设置设备地址等
ret = usb_prepare_device(dev, addr, do_read, parent);
if (ret)
return ret;
// 设置设备的配置
ret = usb_select_config(dev);
return ret;
}
usb_prepare_device()
完成向 xchi 控制器申请 slot 和 相关数据结构,设置设备描述符,设置设备地址等工作:
static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read,
struct usb_device *parent)
{
int err;
/*
* Allocate usb 3.0 device context.
* USB 3.0 (xHCI) protocol tries to allocate device slot
* and related data structures first. This call does that.
* Refer to sec 4.3.2 in xHCI spec rev1.0
*/
// 向 xchi 申请 slot_id 及 各类资源,这里暂时不过多描述
err = usb_alloc_device(dev);
if (err) {
printf("Cannot allocate device context to get SLOT_ID\n");
return err;
}
// 建立设备描述符
err = usb_setup_descriptor(dev, do_read);
if (err)
return err;
// 这里目前为空
err = usb_hub_port_reset(dev, parent);
if (err)
return err;
dev->devnum = addr;
// 设置设备地址,通过默认地址 (0) 寻址设备来实现
err = usb_set_address(dev); /* set address */
if (err < 0) {
printf("\n USB device not accepting new address " \
"(error=%lX)\n", dev->status);
return err;
}
mdelay(10); /* Let the SET_ADDRESS settle */
return 0;
}
usb_select_config()
设置设备配置:
int usb_select_config(struct usb_device *dev)
{
unsigned char *tmpbuf = NULL;
int err;
// 获取设备描述符长度
err = get_descriptor_len(dev, USB_DT_DEVICE_SIZE, USB_DT_DEVICE_SIZE);
if (err)
return err;
/* correct le values */
le16_to_cpus(&dev->descriptor.bcdUSB);
le16_to_cpus(&dev->descriptor.idVendor);
le16_to_cpus(&dev->descriptor.idProduct);
le16_to_cpus(&dev->descriptor.bcdDevice);
/*
* Kingston DT Ultimate 32GB USB 3.0 seems to be extremely sensitive
* about this first Get Descriptor request. If there are any other
* requests in the first microframe, the stick crashes. Wait about
* one microframe duration here (1mS for USB 1.x , 125uS for USB 2.0).
*/
mdelay(1);
/* only support for one config for now */
// 获取配置长度
err = usb_get_configuration_len(dev, 0);
if (err >= 0) {
tmpbuf = (unsigned char *)malloc_cache_aligned(err);
if (!tmpbuf)
err = -ENOMEM;
else
err = usb_get_configuration_no(dev, 0, tmpbuf, err);
}
if (err < 0) {
printf("usb_new_device: Cannot read configuration, " \
"skipping device %04x:%04x\n",
dev->descriptor.idVendor, dev->descriptor.idProduct);
free(tmpbuf);
return err;
}
// 将配置保存在内存
usb_parse_config(dev, tmpbuf, 0);
free(tmpbuf);
// 根据配置设定端点的最大packet值
usb_set_maxpacket(dev);
/*
* we set the default configuration here
* This seems premature. If the driver wants a different configuration
* it will need to select itself.
*/
// 设置默认配置
err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue);
if (err < 0) {
printf("failed to set default configuration " \
"len %d, status %lX\n", dev->act_len, dev->status);
return err;
}
/*
* Wait until the Set Configuration request gets processed by the
* device. This is required by at least SanDisk Cruzer Pop USB 2.0
* and Kingston DT Ultimate 32GB USB 3.0 on DWC2 OTG controller.
*/
mdelay(10);
debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
dev->descriptor.iManufacturer, dev->descriptor.iProduct,
dev->descriptor.iSerialNumber);
memset(dev->mf, 0, sizeof(dev->mf));
memset(dev->prod, 0, sizeof(dev->prod));
memset(dev->serial, 0, sizeof(dev->serial));
if (dev->descriptor.iManufacturer)
usb_string(dev, dev->descriptor.iManufacturer,
dev->mf, sizeof(dev->mf));
if (dev->descriptor.iProduct)
usb_string(dev, dev->descriptor.iProduct,
dev->prod, sizeof(dev->prod));
if (dev->descriptor.iSerialNumber)
usb_string(dev, dev->descriptor.iSerialNumber,
dev->serial, sizeof(dev->serial));
debug("Manufacturer %s\n", dev->mf);
debug("Product %s\n", dev->prod);
debug("SerialNumber %s\n", dev->serial);
return 0;
}
2. hub探测
usb_hub_probe()
函数检测设备的接口描述符类型是不是hub及端点方向等内容,成功后再为hub设备设置配置。
int usb_hub_probe(struct usb_device *dev, int ifnum)
{
int ret;
// 检测设备的接口描述符类型是不是hub及端点方向等内容
ret = usb_hub_check(dev, ifnum);
if (ret)
return 0;
// 为hub设置设置配置
ret = usb_hub_configure(dev);
return ret;
}
usb_hub_check()
:这个函数看注释就好了,满满的吐槽风~~
static int usb_hub_check(struct usb_device *dev, int ifnum)
{
struct usb_interface *iface;
struct usb_endpoint_descriptor *ep = NULL;
iface = &dev->config.if_desc[ifnum];
/* Is it a hub? */
// 判断接口类型
if (iface->desc.bInterfaceClass != USB_CLASS_HUB)
goto err;
/* Some hubs have a subclass of 1, which AFAICT according to the */
/* specs is not defined, but it works */
if ((iface->desc.bInterfaceSubClass != 0) &&
(iface->desc.bInterfaceSubClass != 1))
goto err;
/* Multiple endpoints? What kind of mutant ninja-hub is this? */
// 端点只能有一个
if (iface->desc.bNumEndpoints != 1)
goto err;
ep = &iface->ep_desc[0];
/* Output endpoint? Curiousier and curiousier.. */
// 端点输入方向为IN
if (!(ep->bEndpointAddress & USB_DIR_IN))
goto err;
/* If it's not an interrupt endpoint, we'd better punt! */
// 端点为中断端点
if ((ep->bmAttributes & 3) != 3)
goto err;
/* We found a hub */
debug("USB hub found\n");
return 0;
err:
debug("USB hub not found: bInterfaceClass=%d, bInterfaceSubClass=%d, bNumEndpoints=%d\n",
iface->desc.bInterfaceClass, iface->desc.bInterfaceSubClass,
iface->desc.bNumEndpoints);
if (ep) {
debug(" bEndpointAddress=%#x, bmAttributes=%d",
ep->bEndpointAddress, ep->bmAttributes);
}
return -ENOENT;
}
usb_hub_configure()
:
static int usb_hub_configure(struct usb_device *dev)
{
int i, length;
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ);
unsigned char *bitmap;
short hubCharacteristics;
struct usb_hub_descriptor *descriptor;
struct usb_hub_device *hub;
__maybe_unused struct usb_hub_status *hubsts;
int ret;
/* "allocate" Hub device */
// 从全局变量 hub_dev 中获取资源
hub = usb_hub_allocate();
if (hub == NULL)
return -ENOMEM;
hub->pusb_dev = dev;
/* Get the the hub descriptor */
// 发送 USB_REQ_GET_DESCRIPTOR 请求获取hub的设备描述符
ret = usb_get_hub_descriptor(dev, buffer, 4);
if (ret < 0) {
debug("usb_hub_configure: failed to get hub " \
"descriptor, giving up %lX\n", dev->status);
return ret;
}
descriptor = (struct usb_hub_descriptor *)buffer;
length = min_t(int, descriptor->bLength,
sizeof(struct usb_hub_descriptor));
ret = usb_get_hub_descriptor(dev, buffer, length);
if (ret < 0) {
debug("usb_hub_configure: failed to get hub " \
"descriptor 2nd giving up %lX\n", dev->status);
return ret;
}
memcpy((unsigned char *)&hub->desc, buffer, length);
/* adjust 16bit values */
put_unaligned(le16_to_cpu(get_unaligned(
&descriptor->wHubCharacteristics)),
&hub->desc.wHubCharacteristics);
/* set the bitmap */
bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0];
/* devices not removable by default */
memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8);
bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0];
memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */
for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++)
hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i];
for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++)
hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i];
dev->maxchild = descriptor->bNbrPorts;
debug("%d ports detected\n", dev->maxchild);
hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics);
switch (hubCharacteristics & HUB_CHAR_LPSM) {
case 0x00:
debug("ganged power switching\n");
break;
case 0x01:
debug("individual port power switching\n");
break;
case 0x02:
case 0x03:
debug("unknown reserved power switching mode\n");
break;
}
if (hubCharacteristics & HUB_CHAR_COMPOUND)
debug("part of a compound device\n");
else
debug("standalone hub\n");
switch (hubCharacteristics & HUB_CHAR_OCPM) {
case 0x00:
debug("global over-current protection\n");
break;
case 0x08:
debug("individual port over-current protection\n");
break;
case 0x10:
case 0x18:
debug("no over-current protection\n");
break;
}
debug("power on to power good time: %dms\n",
descriptor->bPwrOn2PwrGood * 2);
debug("hub controller current requirement: %dmA\n",
descriptor->bHubContrCurrent);
for (i = 0; i < dev->maxchild; i++)
debug("port %d is%s removable\n", i + 1,
hub->desc.DeviceRemovable[(i + 1) / 8] & \
(1 << ((i + 1) % 8)) ? " not" : "");
if (sizeof(struct usb_hub_status) > USB_BUFSIZ) {
debug("usb_hub_configure: failed to get Status - " \
"too long: %d\n", descriptor->bLength);
return -EFBIG;
}
// 发送 USB_REQ_GET_STATUS 请求获取hub状态
ret = usb_get_hub_status(dev, buffer);
if (ret < 0) {
debug("usb_hub_configure: failed to get Status %lX\n",
dev->status);
return ret;
}
#ifdef DEBUG
hubsts = (struct usb_hub_status *)buffer;
#endif
debug("get_hub_status returned status %X, change %X\n",
le16_to_cpu(hubsts->wHubStatus),
le16_to_cpu(hubsts->wHubChange));
debug("local power source is %s\n",
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \
"lost (inactive)" : "good");
debug("%sover-current condition exists\n",
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \
"" : "no ");
usb_hub_power_on(hub);
/*
* Reset any devices that may be in a bad state when applying
* the power. This is a __weak function. Resetting of the devices
* should occur in the board file of the device.
*/
for (i = 0; i < dev->maxchild; i++)
usb_hub_reset_devices(i + 1);
/*
* Only add the connected USB devices, including potential hubs,
* to a scanning list. This list will get scanned and devices that
* are detected (either via port connected or via port timeout)
* will get removed from this list. Scanning of the devices on this
* list will continue until all devices are removed.
*/
// 将hub的子设备加入到usb_scan_list扫描列表
for (i = 0; i < dev->maxchild; i++) {
struct usb_device_scan *usb_scan;
usb_scan = calloc(1, sizeof(*usb_scan));
if (!usb_scan) {
printf("Can't allocate memory for USB device!\n");
return -ENOMEM;
}
usb_scan->dev = dev;
usb_scan->hub = hub;
usb_scan->port = i;
list_add_tail(&usb_scan->list, &usb_scan_list);
}
/*
* And now call the scanning code which loops over the generated list
*/
// 扫描 usb_scan_list 里的子设备
ret = usb_device_list_scan();
return ret;
}
usb_device_list_scan()
对usb_scan_list
里面的设备进行扫描:
static int usb_device_list_scan(void)
{
struct usb_device_scan *usb_scan;
struct usb_device_scan *tmp;
static int running;
int ret = 0;
/* Only run this loop once for each controller */
if (running)
return 0;
running = 1;
while (1) {
/* We're done, once the list is empty again */
if (list_empty(&usb_scan_list))
goto out;
list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) {
int ret;
/* Scan this port */
ret = usb_scan_port(usb_scan);
if (ret)
goto out;
}
}
out:
/*
* This USB controller has finished scanning all its connected
* USB devices. Set "running" back to 0, so that other USB controllers
* will scan their devices too.
*/
running = 0;
return ret;
}
scan_list);
}
/*
* And now call the scanning code which loops over the generated list
*/
// 扫描 usb_scan_list 里的子设备
ret = usb_device_list_scan();
return ret;
}
`usb_device_list_scan()`对`usb_scan_list`里面的设备进行扫描:
```c
static int usb_device_list_scan(void)
{
struct usb_device_scan *usb_scan;
struct usb_device_scan *tmp;
static int running;
int ret = 0;
/* Only run this loop once for each controller */
if (running)
return 0;
running = 1;
while (1) {
/* We're done, once the list is empty again */
if (list_empty(&usb_scan_list))
goto out;
list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) {
int ret;
/* Scan this port */
ret = usb_scan_port(usb_scan);
if (ret)
goto out;
}
}
out:
/*
* This USB controller has finished scanning all its connected
* USB devices. Set "running" back to 0, so that other USB controllers
* will scan their devices too.
*/
running = 0;
return ret;
}