u-boot下usb命令

u-boot下usb命令执行过程

这篇文档旨在分析u-boot下执行usb命令时整个命令的执行流程,让初学者对u-bootusb相关知识有一定的了解。这里主要从执行的命令出发,一步一步分析代码、协议、寄存器的读写过程。

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()函数工作内容如下:

  1. 初始化全局变量hub_dev的数据内容
  2. 初始化全局变量usb_dev的数据内容
  3. 初始化底层控制器
  4. 为新usb设备申请资源
  5. 识别新设备
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()来看,主要共作是两个部分:

  1. 对设备进行设置
  2. 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;
}
  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值