Linux USB 3.0驱动分析(八)——Gadget UDC驱动分析

时间:2024-03-06 19:58:16
一.概述Gadget

 USB设备控制器(UDC)驱动指的是作为其他USB主机控制器外设USB硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个USB设备依附于一个USB主机控制器上。例如,当某运行Linux系统的手机作为PC的U盘时,手机中的底层USB控制器行使USB设备控制器的功能,这时运行在底层的是UDC驱动,手机要成为U盘,在UDC驱动之上需要另外一个驱动,对于USB大容量存储器而言,这个驱动为File Storage驱动,称为Function驱动。

 

  Linux USB Gadget软件结构一文中分析Linux USB Gadget软件分为三层。这三层其中两层是与硬件无关的,分别是Gadget功能驱动层,USB设备层。一层是与硬件相关的是UDC层。每一层都提供一种关键的数据结构与函数与其他层交互。

    Gadget功能驱动层:  最主要的结构是struct usb_composite_driver,这个结构在这层定义,并且实现结构中的各个函数。
    USB设备层:  最主要的数据结构是struct usb_composite_dev与usb_gadget_driver。前一个代表一个USB复合设备,而后一个是Gadget驱动,与UDC层交互。

    Gadet Function: 功能层的功能接口(interface)
    UDC层:  最主要的数据结构是struct usb_gadget,通常包含在其他结构体中。这个结构体代表了一个USB设备控制器的所有关于USB通信的信息。

其中Gadget功能驱动层和USB设备层对应上图Gadget Function驱动; Gadet Function对应上图Gadet Function API ; UDC层对应上图UDC驱动。

 

 

 
二.UDC驱动分析
在前面《Linux USB3.0驱动分析(七)——USB主机控制器HCD分析》一文中分析了在dwc3_core_init_mode中会根据dr_mode的值来初始化UDC,当时只分析为host的
情况。这节我们来分析当dr_mode为otg的情况,这种情况可以在host或者device直接切换,调用初始化了工作队列INIT_WORK(&dwc->drd_work, __dwc3_set_mode);,然后调用dwc3_drd_init函数初始化。
主要是根据不同的硬件初始化,主要针对是否有extcon
 

 

int dwc3_drd_init(struct dwc3 *dwc)
{
    int ret, irq;
	//External Connectors是usb用于状态通知的驱动,当phy收到中断,处理完usb状态后,通过extcon驱动,广播到已监听该extcon的所有驱动
    dwc->edev = dwc3_get_extcon(dwc); 
    if (IS_ERR(dwc->edev))
        return PTR_ERR(dwc->edev); 

    if (device_property_read_bool(dwc->dev, "usb-role-switch")) { //如果dts里面定义了这个选项usb-role-switch
        dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
        /* usb role switch是一种能够或选择USB连接器角色的设备。在USB控制器是双角色的平台上,控制器
		 *驱动程序需要注册切换开关。在USB主机和USB设备控制器后面的连接器都是分开的,会有一个 mux,这个mux的驱动程序需要注册开关。*/
        dwc->role_switch = usb_role_switch_register(dwc->dev, 
                                &dwc3_role_switch); //注册role_switch,会生成一个%s-role-switch的节点用来控制role.
        if (IS_ERR(dwc->role_switch))
            return PTR_ERR(dwc->role_switch);
    } else if (dwc->edev) {
        dwc->edev_nb.notifier_call = dwc3_drd_notifier; //当extcon状态变化会调用这个函数,里面会设置dwc->desired_dr_role = mode;
        ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
                           &dwc->edev_nb); //注册一个通知块,以获得extcon中的任何状态更改的通知。
        if (ret < 0) {
            dev_err(dwc->dev, "couldn\'t register cable notifier\n");
            return ret;
        }
        dwc3_drd_update(dwc); //获取extcon状态,里面会设置dwc->desired_dr_role = mode;
    } else { //如果没有extcon,我们这里使用的是typec接口
        dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
        dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
        /* use OTG block to get ID event */
        irq = dwc3_otg_get_irq(dwc); //获取一个中断
        if (irq < 0)
            return irq;
        dwc->otg_irq = irq;
        /* disable all OTG IRQs */
        dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
        /* clear all events */
        dwc3_otg_clear_events(dwc);

        ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
                       dwc3_otg_thread_irq,
                       IRQF_SHARED, "dwc3-otg", dwc); //申请一个中断,dwc3_otg_irq是当IRQ发生时被调用的函数,后面再调用dwc3_otg_thread_irq(相当于底半部)
        if (ret) {
            dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
                dwc->otg_irq, ret);
            ret = -ENODEV;
            return ret;
        }
        dwc3_otg_init(dwc); //主要是硬件初始化
        dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); //设置dwc->desired_dr_role = mode;并启动工作队列__dwc3_set_mode
    }
    return 0;
}

 

我们这里使用PTN5110芯片,由这个来来获得模式方面的信息,代码在Tcpci.c (drivers\usb\typec\tcpm) 
不管是extcon还是typec的情况芯片的情况,最后都会调用到__dwc3_set_mode来设置模式,主要是设置成host或者device
static void __dwc3_set_mode(struct work_struct *work)
{
    switch (dwc->desired_dr_role) {
    case DWC3_GCTL_PRTCAP_HOST: //host模式,之前已经分析过了
        ret = dwc3_host_init(dwc);
        if (ret) {
            dev_err(dwc->dev, "failed to initialize host\n");
        } else {
            if (dwc->usb2_phy)
                otg_set_vbus(dwc->usb2_phy->otg, true);
            phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
            phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
        }
        break;
    case DWC3_GCTL_PRTCAP_DEVICE: //从设备模式
        dwc3_event_buffers_setup(dwc);
        if (dwc->usb2_phy)
            otg_set_vbus(dwc->usb2_phy->otg, false);
        phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); //设置phy模式
        phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
        ret = dwc3_gadget_init(dwc); //初始化gadget
        if (ret)
            dev_err(dwc->dev, "failed to initialize peripheral\n");
        break;
    case DWC3_GCTL_PRTCAP_OTG: //这里一般应该是初始化调用
        dwc3_otg_init(dwc);
        dwc3_otg_update(dwc, 0);
        break;
    default:
        break;
    }
}
host的情况之前已经分析了,现在我们分析device的情况,也就是gadget的初始化。在具体的UDC驱动中,需要封装usb_gadget和每个端点usb_ep,实现usb_gadget的usb_gadget_ops并实现端点的usb_ep_ops,完成usb_request。这些事情搞定后,注册一个UDC,通过usb_add_gadget_udc()API来进行的。
static const struct usb_gadget_ops dwc3_gadget_ops = {
	.get_frame		= dwc3_gadget_get_frame,
	.wakeup			= dwc3_gadget_wakeup,
	.set_selfpowered	= dwc3_gadget_set_selfpowered,
	.pullup			= dwc3_gadget_pullup, //下拉开始枚举
	.udc_start		= dwc3_gadget_start, //启动
	.udc_stop		= dwc3_gadget_stop,  //停止
	.udc_set_speed		= dwc3_gadget_set_speed,
	.get_config_params	= dwc3_gadget_config_params,
};

int dwc3_gadget_init(struct dwc3 *dwc)
{
    dwc->gadget.ops            = &dwc3_gadget_ops; //设置操作函数
    dwc->gadget.speed        = USB_SPEED_UNKNOWN;
    dwc->gadget.sg_supported    = true;
    dwc->gadget.name        = "dwc3-gadget";
    dwc->gadget.lpm_capable        = true;
    dwc->gadget.is_otg        = (dwc->dr_mode == USB_DR_MODE_OTG) &&
                      (dwc->otg_caps.hnp_support ||
                       dwc->otg_caps.srp_support ||
                       dwc->otg_caps.adp_support); //设置模式
    /*
     * REVISIT: Here we should clear all pending IRQs to be
     * sure we\'re starting from a well known location.
     */
    ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps); //初始化usb端口,dwc->num_eps个
    if (ret)
        goto err3;
    ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); //向udc类驱动程序列表中添加一个新的gadget
    if (ret) {
        dev_err(dwc->dev, "failed to register udc\n");
        goto err4;
    }
    dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
    return 0;
}
 
1.dwc3_gadget_init_endpoints
我们来仔细看看dwc3_gadget_init_endpoints做了什么,dwc3_gadget_init_endpoints -> dwc3_gadget_init_endpoint
static const struct usb_ep_ops dwc3_gadget_ep0_ops = { //端点0的操作函数
	.enable		= dwc3_gadget_ep0_enable,
	.disable	= dwc3_gadget_ep0_disable,
	.alloc_request	= dwc3_gadget_ep_alloc_request,
	.free_request	= dwc3_gadget_ep_free_request,
	.queue		= dwc3_gadget_ep0_queue,
	.dequeue	= dwc3_gadget_ep_dequeue,
	.set_halt	= dwc3_gadget_ep0_set_halt,
	.set_wedge	= dwc3_gadget_ep_set_wedge,
};

static const struct usb_ep_ops dwc3_gadget_ep_ops = { //除了端点0之外的端点操作函数
	.enable		= dwc3_gadget_ep_enable,
	.disable	= dwc3_gadget_ep_disable,
	.alloc_request	= dwc3_gadget_ep_alloc_request,
	.free_request	= dwc3_gadget_ep_free_request,
	.queue		= dwc3_gadget_ep_queue,
	.dequeue	= dwc3_gadget_ep_dequeue,
	.set_halt	= dwc3_gadget_ep_set_halt,
	.set_wedge	= dwc3_gadget_ep_set_wedge,
};

static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
    dep->dwc = dwc;
    dep->number = epnum;
    dep->direction = direction;
    dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
    dwc->eps[epnum] = dep;
    dep->combo_num = 0;
    dep->start_cmd_status = 0;
    snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
            direction ? "in" : "out");

    dep->endpoint.name = dep->name;
    if (!(dep->number > 1)) {
        dep->endpoint.desc = &dwc3_gadget_ep0_desc; //默认的描述符
        dep->endpoint.comp_desc = NULL;
    }
    if (num == 0)
        ret = dwc3_gadget_init_control_endpoint(dep); //初始化控制端点0, dep->endpoint.ops = &dwc3_gadget_ep0_ops; dwc3_gadget_ep0_ops是操作函数
    else if (direction)
        ret = dwc3_gadget_init_in_endpoint(dep); //初始化输入端点, dep->endpoint.ops = &dwc3_gadget_ep_ops; dwc3_gadget_ep_ops是操作函数
    else
        ret = dwc3_gadget_init_out_endpoint(dep); //初始化输出端点,dep->endpoint.ops = &dwc3_gadget_ep_ops; dwc3_gadget_ep_ops是操作函数
}
 
2.usb_add_gadget_udc
向udc类驱动程序列表中添加一个新的gadget,usb_add_gadget_udc -> usb_add_gadget_udc_release。注册usb_gadget,并选择一个等待的gadget驱动,使用udc_bind_to_driver驱动绑定
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
        void (*release)(struct device *dev))
{
    dev_set_name(&gadget->dev, "gadget");
    INIT_WORK(&gadget->work, usb_gadget_state_work); //这个工作用于通知,sysfs_notify
    gadget->dev.parent = parent;

    if (release)
        gadget->dev.release = release;
    else
        gadget->dev.release = usb_udc_nop_release;

    device_initialize(&gadget->dev); //初始化设备结构

    udc = kzalloc(sizeof(*udc), GFP_KERNEL); //分配usb_udc结构体
    if (!udc)
        goto err_put_gadget;

    device_initialize(&udc->dev);
    udc->dev.release = usb_udc_release;
    udc->dev.class = udc_class;
    udc->dev.groups = usb_udc_attr_groups; //创建sysfs调试节点
    udc->dev.parent = parent;
    ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
    if (ret)
        goto err_put_udc;

    ret = device_add(&gadget->dev); //将usb_gadget设备添加到设备层次结构中
    if (ret)
        goto err_put_udc;

    udc->gadget = gadget; //赋值这个usb_gadget
    gadget->udc = udc;

    mutex_lock(&udc_lock);
    list_add_tail(&udc->list, &udc_list); //加入到udc_list

    ret = device_add(&udc->dev); //描述了一个usb设备控制器的设备,添加到设备层次结构中
    if (ret)
        goto err_unlist_udc;

    usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
    udc->vbus = true;
    /* pick up one of pending gadget drivers */
    ret = check_pending_gadget_drivers(udc); //选择一个等待的gadget驱动,使用udc_bind_to_driver驱动绑定,后面我们会分析这个函数
    if (ret)
        goto err_del_udc;
    mutex_unlock(&udc_lock);
    return 0;
}
 
3.dwc3_gadget_start
dwc3_gadget_start会在udc_bind_to_driver绑定驱动和udc之后调用
static int dwc3_gadget_start(struct usb_gadget *g,
        struct usb_gadget_driver *driver)
{
    irq = dwc->irq_gadget;
    ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
            IRQF_SHARED, "dwc3", dwc->ev_buf); //申请中断,上半部是dwc3_interrupt,下半部是dwc3_thread_interrupt
    if (ret) {
        dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
                irq, ret);
        goto err0;
    }
    dwc->gadget_driver    = driver;

    if (pm_runtime_active(dwc->dev))
        __dwc3_gadget_start(dwc); //启动udc,主要是寄存器方面的设置,还有开始接收SETUP包,是能中断等
    spin_unlock_irqrestore(&dwc->lock, flags);
    return 0;
}
中断上半部dwc3_interrupt只是简单的获取中断相关寄存器信息,主要工作都由下半部dwc3_thread_interrupt完成。
 
参考:
Linux USB Gadget--软件结构

 

dwc3 linux usb3.0 driver架构_在路上-CSDN博客