Linux USB 3.0驅動分析(八)——Gadget UDC驅動分析


一.概述 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博客
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM