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驅動。
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;
}
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;
}
}
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;
}
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是操作函數
}
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;
}
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 linux usb3.0 driver架構_在路上-CSDN博客