Linux内核USB2.0控制器初始化代码分析

内核版本:4.1.15版本

1、设备树

本文分析的imx6ull的USB代码。

usbotg1: usb@02184000 {
	compatible = "fsl,imx6ul-usb", "fsl,imx27-usb";
	reg = <0x02184000 0x200>;
	interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_USBOH3>;
	fsl,usbphy = <&usbphy1>;
	fsl,usbmisc = <&usbmisc 0>;
	fsl,anatop = <&anatop>;
	ahb-burst-config = <0x0>;
	tx-burst-size-dword = <0x10>;
	rx-burst-size-dword = <0x10>;
	status = "disabled";
};

2、USB初始化分析

IMX6ULL的USB初始化驱动是一个platform_driver,设备树匹配的属性为“fsl,imx6ul-usb”,入口函数为ci_hdrc_imx_probe。

static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);


static struct platform_driver ci_hdrc_imx_driver = {
	.probe = ci_hdrc_imx_probe,
	.remove = ci_hdrc_imx_remove,
	.driver = {
		.name = "imx_usb",
		.of_match_table = ci_hdrc_imx_dt_ids,
		.pm = &ci_hdrc_imx_pm_ops,
	 },
};

module_platform_driver(ci_hdrc_imx_driver);

ci_hdrc_imx_probe函数的主要工作如下:

struct ci_hdrc_imx_data *data;
struct ci_hdrc_platform_data pdata = {
	.name		= dev_name(&pdev->dev),
	.capoffset	= DEF_CAPOFFSET,
	.notify_event	= ci_hdrc_imx_notify_event,
};

data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
pdata.usb_phy = data->phy;

data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
			pdev->resource, pdev->num_resources,
			&pdata);

第8~9行:获取phy,并将值赋值给pdata。
第11行:ci_hdrc_add_device()创建一个新的设备,这个函数很重要,是USB的核心。

struct platform_device *ci_hdrc_add_device(struct device *dev,
			struct resource *res, int nres,
			struct ci_hdrc_platform_data *platdata)
{
	struct platform_device *pdev;
	int id, ret;

	ret = ci_get_platdata(dev, platdata);
	
	id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);

	pdev = platform_device_alloc("ci_hdrc", id);

	pdev->dev.parent = dev;
	pdev->dev.dma_mask = dev->dma_mask;
	pdev->dev.dma_parms = dev->dma_parms;
	dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask);

	ret = platform_device_add_resources(pdev, res, nres);

	ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));

	ret = platform_device_add(pdev);
}

第8行:对platdata进行配置。
第10行:获取一个新ID。
第12行:注册一个名为ci_hdrc的设备。
第17行:设置DMA。

3、USB核心初始化分析

3.1数据结构分析

struct ci_hdrc是USB OTG控制器的核心数据结构,所有工作都围绕此数据结构展开。该数据结构的意义如下面的代码所示,省略了一些不太重要的内容。

[drivers\usb\chipidea\ci.h]
struct ci_hdrc {
	struct device			*dev;
	spinlock_t			lock;
	struct hw_bank			hw_bank;
	int				irq;
	struct ci_role_driver		*roles[CI_ROLE_END];
	enum ci_role			role;
	bool				is_otg;
	struct usb_otg			otg;
	struct otg_fsm			fsm;
	struct hrtimer			otg_fsm_hrtimer;
	ktime_t				hr_timeouts[NUM_OTG_FSM_TIMERS];
	unsigned			enabled_otg_timer_bits;
	enum otg_fsm_timer		next_otg_timer;
	struct timer_list		hnp_polling_timer;
	struct work_struct		hnp_polling_work;
	struct work_struct		work;
	struct workqueue_struct		*wq;

	struct dma_pool			*qh_pool;
	struct dma_pool			*td_pool;

	struct usb_gadget		gadget;
	struct usb_gadget_driver	*driver;
	unsigned			hw_ep_max;
	struct ci_hw_ep			ci_hw_ep[ENDPT_MAX];
	u32				ep0_dir;
	struct ci_hw_ep			*ep0out, *ep0in;

	struct usb_request		*status;
	bool				setaddr;
	u8				address;
	u8				remote_wakeup;
	u8				suspended;
	u8				test_mode;

	struct ci_hdrc_platform_data	*platdata;
	int				vbus_active;
	struct phy			*phy;
	/* old usb_phy interface */
	struct usb_phy			*usb_phy;
	struct usb_hcd			*hcd;
	struct dentry			*debugfs;
	bool				id_event;
	bool				b_sess_valid_event;
	bool				vbus_glitch_check_event;
	bool				imx28_write_fix;
	bool				supports_runtime_pm;
	bool				in_lpm;
	bool				wakeup_int;
	enum ci_revision		rev;
	/* register save area for suspend&resume */
	u32				pm_command;
	u32				pm_status;
	u32				pm_intr_enable;
	u32				pm_frame_index;
	u32				pm_segment;
	u32				pm_frame_list;
	u32				pm_async_next;
	u32				pm_configured_flag;
	u32				pm_portsc;
	u32				pm_usbmode;
	struct work_struct		power_lost_work;
};

3.2驱动分析

可以根据上一节设备名ci_hdrc找到注册驱动。

static struct platform_driver ci_hdrc_driver = {
	.probe	= ci_hdrc_probe,
	.remove	= ci_hdrc_remove,
	.driver	= {
		.name	= "ci_hdrc",
		.pm	= &ci_pm_ops,
	},
};

static int __init ci_hdrc_platform_register(void)
{
	ci_hdrc_host_driver_init();
	return platform_driver_register(&ci_hdrc_driver);
}
module_init(ci_hdrc_platform_register);

static void __exit ci_hdrc_platform_unregister(void)
{
	platform_driver_unregister(&ci_hdrc_driver);
}
module_exit(ci_hdrc_platform_unregister);

现在我们来看proc函数:

struct device	*dev = &pdev->dev;
struct ci_hdrc	*ci;
struct resource	*res;
void __iomem	*base;
int		ret;
enum usb_dr_mode dr_mode;

if (!dev_get_platdata(dev)) 

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);

ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);

ci->dev = dev;
ci->platdata = dev_get_platdata(dev);

ret = hw_device_init(ci, base);

if (ci->platdata->phy) {
	ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
	ci->usb_phy = ci->platdata->usb_phy;
} else {
	ci->phy = devm_phy_get(dev->parent, "usb-phy");
	ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);

	/* if both generic PHY and USB PHY layers aren't enabled */
	if (PTR_ERR(ci->phy) == -ENOSYS &&
			PTR_ERR(ci->usb_phy) == -ENXIO)

	if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))

	if (IS_ERR(ci->phy))
		ci->phy = NULL;
	else if (IS_ERR(ci->usb_phy))
		ci->usb_phy = NULL;
}

ret = ci_usb_phy_init(ci);

ci->irq = platform_get_irq(pdev, 0);

ci_get_otg_capable(ci);

dr_mode = ci->platdata->dr_mode;
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
	ret = ci_hdrc_host_init(ci);
}

if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
	ret = ci_hdrc_gadget_init(ci);
}

if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET])

if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
	ret = ci_hdrc_otg_init(ci);
}

platform_set_drvdata(pdev, ci);
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
		ci->platdata->name, ci);
		
if (ci->supports_runtime_pm) {
	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
	pm_runtime_mark_last_busy(ci->dev);
	pm_runtime_use_autosuspend(&pdev->dev);
}

第2行:ci就是重要的结构体,这个函数的主要作用就是对其进行赋值。
第8行:安全性检查,看是否含有私有数据。
第10~11行:获取地址空间,并对其重映射。
第13行:为ci分配地址空间。
第16行:将dev传来的私有数据进行赋值给ci。
第18行:硬件初始化。
第20~38行:获取PHY信息。
第40行:初始化PHY。
第42行:获取中断号。
第44行:判断是否是OTG。
第46~58行:根据不同的模式进行初始化。49行ci_hdrc_host_init()是主机进行初始化,53行ci_hdrc_gadget_init()是gadget进行初始化,这两段很重要,以后会专门进行讲解。57行是对OTG模式进行初始化,会创建一个内核线程。
第60行:设置私有数据。
第61行:请求ci_irq中断。
第64~70行:电源管理函数。

4、设备控制器驱动框架分析

USB控制器可以呈现两种不同的状态。USB作为HOST时,称为USB主机控制器,使用的是USB主机控制器驱动。USB作为Device时,称为USB设备控制器驱动,使用的UDC驱动。本次我们分析的是USB作为Device时的驱动框架。
设备控制区驱动可以分为5层。最上层的是Gadget Function驱动,所代表的是具体的设备,如U盘。USB摄像头等。接下来是Gadget Function API层,该层为一个抽象层,向上和向下提供统一的API,提高了驱动的兼容性。Composite层是一个可选的中间层,可通过一种配置或多种配置支持多种设备。目前最流行的是使用基于Composite和configfs实现的USB gadget configfs,可在用户空间灵活的配置USB设备。UDC驱动直接访问硬件,控制USB设备与USB主机之间的通信。USB设备控制器通过USB线缆连接USB主机控制器,负责USB数据的发送和接收。
编写gadget框架的关键在于理解udc、gadget和composite之间的关系和架构层次。在实际应用中gadget是不需要我们编写的,而我们需要编写的是composite层,以及对udc层进行修改。

4.1 底层硬件操作UDC驱动建立

int ci_hdrc_gadget_init(struct ci_hdrc *ci)
{
	struct ci_role_driver *rdrv;

	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);

	rdrv->start	= udc_id_switch_for_device;
	rdrv->stop	= udc_id_switch_for_host;
	rdrv->irq	= udc_irq;
	rdrv->suspend	= udc_suspend;
	rdrv->resume	= udc_resume;
	rdrv->name	= "gadget";
	ci->roles[CI_ROLE_GADGET] = rdrv;

	return udc_start(ci);
}

第5行:为rdrv分配地址。
第7~12行:设置各种函数。
第14行:udc进行设置,这个是重中之重。

static int udc_start(struct ci_hdrc *ci)
{
	struct device *dev = ci->dev;
	struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
	int retval = 0;

	spin_lock_init(&ci->lock);

	ci->gadget.ops          = &usb_gadget_ops;
	ci->gadget.speed        = USB_SPEED_UNKNOWN;
	ci->gadget.max_speed    = USB_SPEED_HIGH;
	ci->gadget.name         = ci->platdata->name;
	ci->gadget.otg_caps	= otg_caps;

	INIT_LIST_HEAD(&ci->gadget.ep_list);

	/* alloc resources */
	ci->qh_pool = dma_pool_create("ci_hw_qh", dev,
				       sizeof(struct ci_hw_qh),
				       64, CI_HDRC_PAGE_SIZE);

	ci->td_pool = dma_pool_create("ci_hw_td", dev,
				       sizeof(struct ci_hw_td),
				       64, CI_HDRC_PAGE_SIZE);

	retval = init_eps(ci);

	ci->gadget.ep0 = &ci->ep0in->ep;

	retval = usb_add_gadget_udc(dev, &ci->gadget);
}

第9~13行:设置gadget。
第18~24行:创建DMA空间。
第26行:设置gadget的endpoint。
第30行:注册udc。

int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{
	return usb_add_gadget_udc_release(parent, gadget, NULL);
}

int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
		void (*release)(struct device *dev))
{
	mutex_lock(&udc_lock);
	list_add_tail(&udc->list, &udc_list);

	/* pick up one of pending gadget drivers */
	list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
		if (!driver->udc_name || strcmp(driver->udc_name,
						dev_name(&udc->dev)) == 0) {
			ret = udc_bind_to_driver(udc, driver);
			if (ret)
				goto err4;
			list_del(&driver->pending);
			break;
		}
	}
	mutex_unlock(&udc_lock);
}

这段代码的主要含义是把 Device 加入到 udc_list 链表,然后尝试和 gadget_driver_pending_list 链表中的 Driver 进行 match()。

4.2 UDC开始

如果match成功,就会调用usb_gadget_udc_start(),这个函数实际就是ci_udc_start(),以下就是函数内容:

static int ci_udc_start(struct usb_gadget *gadget,
			 struct usb_gadget_driver *driver)
{
	struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);

	ci->ep0out->ep.desc = &ctrl_endpt_out_desc;
	retval = usb_ep_enable(&ci->ep0out->ep);

	ci->ep0in->ep.desc = &ctrl_endpt_in_desc;
	retval = usb_ep_enable(&ci->ep0in->ep);

	ci->driver = driver;

	/* Start otg fsm for B-device */
	if (ci_otg_is_fsm_mode(ci)) {
		if (ci->fsm.id)
			ci_hdrc_otg_fsm_start(ci);
	}

	if (ci->vbus_active)
		ci_hdrc_gadget_connect(&ci->gadget, 1);
}

第6行:设置端点长度,方向,类型等。
第7行:usb_ep_enable()是在init_eps()初始化端点中完成的api赋值,函数为ep_enable()。
第9行:将composite层提供的usb_gadget_driver结构体地址保存到ci_hdrc结构体中。

4.3 上层操作

Host要分配地址,把地址分配给设备,这部分有usb_gadget实现。
Host要读取各种描述符,这些都由上层程序提供。
上层的描述符通过中间层将底层的usb_gadget传回给Host,中间层就是usb_gadget_driver。实现一些通用的USB访问方法,比如Host访问描述符时,由usb_gadget_driver提供。

通过提供描述符还可以分为两层:
usb_composite_driver:提供设备、配置描述符。
function driver:提供接口、端点描述符。

目前存在两种风格的 Gadget Driver,其中包括:

  • Legacy。这是早期风格的 Gadget Driver,只能通过静态编译的方式指定使用哪些 Function。
  • Configfs。这是目前流行的 Gadget Driver,可以通过 configfs 文件系统,不用重新编译内核,动态的配置需要使用的 Function。

4.3.1 configfs

举例创建一个ACM Funtion:

//1 挂载configfs文件系统
mount -t configfs none /sys/kernerl/config
cd /sys/kernerl/config/usb_gadget

//2 创建g1目录,实例一个gadget模板
mkdir g1
cd g1

//3 定义产品的VID、PID
echo "0x1d6b" > idVendor
echo "0x0104" > idProduct

//4 实例化英语语言ID
mkdir strings/0x409
ls strings/0x409/

//5 将开发商、产品和序列号字符串写入内核
echo "0123456789" > strings/0x409/serialnumber
echo "AAAA Inc." > strings/0x409/manufacturer
echo "Bar Gadget" > strings/0x409/product

//6 创建功能(Fuction)实例,注意,一个功能如果有多个实例的话,扩展名必须用数字编号
mkdir functions/acm.GS0

//7 创建配置实例
mkdir configs/c.1
ls configs/c.1

//8 定义配置描述符使用的字符串
mkdir configs/c.1/strings/0x409
ls configs/c.1/strings/0x409/
echo "ACM" > configs/c.1/strings/0x409/configuration

//9 捆绑功能 `Function` 实例到 `Configuration` 配置c.1
ln -s functions/acm.GS0 configs/c.1

//10 查找本机可获得的UDC实例
# ls /sys/class/udc/
10200000.usb

//11 将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备
echo "10200000.usb" > UDC

4.3.2 Legacy实例分析

以zero实例进行分析。
入口函数:

static struct usb_composite_driver zero_driver = {
	.name		= "zero",
	.dev		= &device_desc,
	.strings	= dev_strings,
	.max_speed	= USB_SPEED_SUPER,
	.bind		= zero_bind,
	.unbind		= zero_unbind,
	.suspend	= zero_suspend,
	.resume		= zero_resume,
};

module_usb_composite_driver(zero_driver);

进入module_usb_composite_driver函数,找到probe函数:

#define module_usb_composite_driver(__usb_composite_driver) \
	module_driver(__usb_composite_driver, usb_composite_probe, \
		       usb_composite_unregister)

下图为主要流程:
在这里插入图片描述
在4.1(底层硬件操作UDC驱动建立)中也出现了udc_bind_to_driver,在gadget device建立时,会把device加入到udc_list 链表,然后尝试和 udc_bind_to_driver链表中的 Driver 进行 match()。
而在此处是gadget driver创建时,首先尝试和 udc_list 链表中的 Device 进行 match(),match() 不成功则把 Driver 加入到 udc_bind_to_driver链表中。

USB Function驱动和UDC绑定成功后,通过调用udc_bind_to_driver()中的usb_gadget_udc_start(udc)接口开启USB设备控制器,接收主机发送的请求。

4.4 从获取描述符的角度理解Gadget框架

核心函数为:

ci_hdrc_probe
	devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
			ci->platdata->name, ci);

中断函数的处理流程:

ci_irq
	/* Handle device/host interrupt */
	if (ci->role != CI_ROLE_END)
		ret = ci_role(ci)->irq(ci);  // udc_irq,这个irq在ci_hdrc_gadget_init时被初始化
		
		rdrv->irq	= udc_irq;
		isr_tr_complete_handler(ci);
			isr_setup_packet_handler(ci);

函数isr_setup_packet_handler是处理endpoint 0接收到的控制传输的关键。

参考资料

USB总线
IMX6ULL内核源码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值