本文主要是介绍dwc3 DR_MODE 处理初始化 OTG gadget,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
dwc3控制器是怎么处理otg-CSDN博客
dwc3_probe
static int dwc3_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct resource *res, dwc_res;struct dwc3 *dwc;int ret;void __iomem *regs;int irq;char dma_ipc_log_ctx_name[40];if (count >= DWC_CTRL_COUNT) {dev_err(dev, "Err dwc instance %d >= %d available\n",count, DWC_CTRL_COUNT);ret = -EINVAL;return ret;}dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);if (!dwc)return -ENOMEM;dwc->dev = dev;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {dev_err(dev, "missing memory resource\n");return -ENODEV;}dwc->reg_phys = res->start;dwc->xhci_resources[0].start = res->start;dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +DWC3_XHCI_REGS_END;dwc->xhci_resources[0].flags = res->flags;dwc->xhci_resources[0].name = res->name;irq = platform_get_irq(to_platform_device(dwc->dev), 0);ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",dwc);if (ret) {dev_err(dwc->dev, "failed to request irq #%d --> %d\n",irq, ret);return -ENODEV;}if (notify_event)/* will be enabled in dwc3_msm_resume() */disable_irq(irq);dwc->irq = irq;/** Request memory region but exclude xHCI regs,* since it will be requested by the xhci-plat driver.*/dwc_res = *res;dwc_res.start += DWC3_GLOBALS_REGS_START;regs = devm_ioremap_resource(dev, &dwc_res);if (IS_ERR(regs))return PTR_ERR(regs);dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);if (!dwc->dwc_wq) {dev_err(dev,"%s: Unable to create workqueue dwc_wq\n", __func__);goto err0;}INIT_WORK(&dwc->bh_work, dwc3_bh_work);dwc->regs = regs;dwc->regs_size = resource_size(&dwc_res);//解析dtsi dwc3_get_properties(dwc);dwc->reset = devm_reset_control_array_get(dev, true, true);if (IS_ERR(dwc->reset))return PTR_ERR(dwc->reset);if (dev->of_node) {ret = devm_clk_bulk_get_all(dev, &dwc->clks);if (ret == -EPROBE_DEFER)goto err0;/** Clocks are optional, but new DT platforms should support all* clocks as required by the DT-binding.*/if (ret < 0)dwc->num_clks = 0;elsedwc->num_clks = ret;}ret = dwc3_extract_num_phys(dwc);if (ret) {dev_err(dwc->dev, "Unable to extract number of PHYs\n");goto err0;}dwc->usb2_phy = devm_kzalloc(dwc->dev,sizeof(*dwc->usb2_phy) * dwc->num_hsphy, GFP_KERNEL);dwc->usb3_phy = devm_kzalloc(dwc->dev,sizeof(*dwc->usb3_phy) * dwc->num_ssphy, GFP_KERNEL);ret = reset_control_deassert(dwc->reset);if (ret)goto err0;ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);if (ret)goto assert_reset;platform_set_drvdata(pdev, dwc);init_waitqueue_head(&dwc->wait_linkstate);spin_lock_init(&dwc->lock);pm_runtime_no_callbacks(dev);pm_runtime_set_active(dev);if (dwc->enable_bus_suspend) {pm_runtime_set_autosuspend_delay(dev,DWC3_DEFAULT_AUTOSUSPEND_DELAY);pm_runtime_use_autosuspend(dev);}pm_runtime_enable(dev);pm_runtime_forbid(dev);ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);if (ret) {dev_err(dwc->dev, "failed to allocate event buffers\n");ret = -ENOMEM;goto err1;}ret = dwc3_alloc_scratch_buffers(dwc);if (ret)goto err2;dwc3_debugfs_init(dwc);if (!notify_event) {ret = dwc3_core_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize core: %d\n",ret);goto err3;}ret = dwc3_event_buffers_setup(dwc);if (ret) {dev_err(dwc->dev, "failed to setup event buffers\n");goto err3;}ret = dwc3_core_init_mode(dwc);if (ret) {dwc3_event_buffers_cleanup(dwc);goto err3;}} else if (dwc->dr_mode == USB_DR_MODE_OTG ||dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {ret = dwc3_gadget_init(dwc);if (ret) {dev_err(dwc->dev, "gadget init failed %d\n", ret);goto err3;}}dwc->dwc_ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,dev_name(dwc->dev), 0);if (!dwc->dwc_ipc_log_ctxt)dev_dbg(dwc->dev, "ipc_log_ctxt is not available\n");snprintf(dma_ipc_log_ctx_name, sizeof(dma_ipc_log_ctx_name),"%s.ep_events", dev_name(dwc->dev));dwc->dwc_dma_ipc_log_ctxt = ipc_log_context_create(2 * NUM_LOG_PAGES,dma_ipc_log_ctx_name, 0);if (!dwc->dwc_dma_ipc_log_ctxt)dev_dbg(dwc->dev, "ipc_log_ctxt for ep_events is not available\n");dwc3_instance[count] = dwc;dwc->index = count;count++;pm_runtime_allow(dev);return 0;err3:dwc3_debugfs_exit(dwc);dwc3_free_scratch_buffers(dwc);
err2:dwc3_free_event_buffers(dwc);
err1:pm_runtime_allow(&pdev->dev);pm_runtime_disable(&pdev->dev);clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:reset_control_assert(dwc->reset);destroy_workqueue(dwc->dwc_wq);
err0:return ret;
}
dwc3_core_init_mode dtsi 配置dr_mode = USB_DR_MODE_OTG
static int __maybe_unused dwc3_core_init_mode(struct dwc3 *dwc)
{struct device *dev = dwc->dev;int ret;switch (dwc->dr_mode) {case USB_DR_MODE_PERIPHERAL:dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);if (dwc->usb2_phy[0])otg_set_vbus(dwc->usb2_phy[0]->otg, false);phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);ret = dwc3_gadget_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize gadget\n");return ret;}dwc->vbus_active = true;break;case USB_DR_MODE_HOST:dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);if (dwc->usb2_phy[0])otg_set_vbus(dwc->usb2_phy[0]->otg, true);phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);ret = dwc3_host_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize host\n");return ret;}break;case USB_DR_MODE_OTG:INIT_WORK(&dwc->drd_work, __dwc3_set_mode);ret = dwc3_drd_init(dwc);if (ret) {if (ret != -EPROBE_DEFER)dev_err(dev, "failed to initialize dual-role\n");return ret;}break;default:dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);return -EINVAL;}return 0;
}
CONFIG_USB_ROLE_SWITCH =y
dwc3_drd_init
dwc3_setup_role_switch
int dwc3_drd_init(struct dwc3 *dwc)
{int ret, irq;dwc->edev = dwc3_get_extcon(dwc);if (IS_ERR(dwc->edev))return PTR_ERR(dwc->edev);if (ROLE_SWITCH &&device_property_read_bool(dwc->dev, "usb-role-switch")) {ret = dwc3_setup_role_switch(dwc);if (ret < 0)return ret;} else if (dwc->edev) {dwc->edev_nb.notifier_call = dwc3_drd_notifier;ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,&dwc->edev_nb);if (ret < 0) {dev_err(dwc->dev, "couldn't register cable notifier\n");return ret;}dwc3_drd_update(dwc);} else {dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;解析dtsi的 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);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);}return 0;
}
将otg 模式默认设置为从设备 USB_DR_MODE_PERIPHERA
static int dwc3_setup_role_switch(struct dwc3 *dwc)
{struct usb_role_switch_desc dwc3_role_switch = {NULL};const char *str;u32 mode;int ret;ret = device_property_read_string(dwc->dev, "role-switch-default-mode",&str);if (ret >= 0 && !strncmp(str, "host", strlen("host"))) {dwc->role_switch_default_mode = USB_DR_MODE_HOST;mode = DWC3_GCTL_PRTCAP_HOST;} else {dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;mode = DWC3_GCTL_PRTCAP_DEVICE;}dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);dwc3_role_switch.set = dwc3_usb_role_switch_set;dwc3_role_switch.get = dwc3_usb_role_switch_get;dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);if (IS_ERR(dwc->role_sw))return PTR_ERR(dwc->role_sw);dwc3_set_mode(dwc, mode);return 0;
}
usb_role_switch_register
struct usb_role_switch *
usb_role_switch_register(struct device *parent,const struct usb_role_switch_desc *desc)
{struct usb_role_switch *sw;int ret;if (!desc || !desc->set)return ERR_PTR(-EINVAL);sw = kzalloc(sizeof(*sw), GFP_KERNEL);if (!sw)return ERR_PTR(-ENOMEM);mutex_init(&sw->lock);sw->allow_userspace_control = desc->allow_userspace_control;sw->usb2_port = desc->usb2_port;sw->usb3_port = desc->usb3_port;sw->udc = desc->udc;sw->set = desc->set;sw->get = desc->get;sw->dev.parent = parent;sw->dev.fwnode = desc->fwnode;sw->dev.class = role_class;sw->dev.type = &usb_role_dev_type;dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent));ret = device_register(&sw->dev);if (ret) {put_device(&sw->dev);return ERR_PTR(ret);}/* TODO: Symlinks for the host port and the device controller. */return sw;
}
EXPORT_SYMBOL_GPL(usb_role_switch_register);
dwc3_set_mode
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{unsigned long flags;spin_lock_irqsave(&dwc->lock, flags);dwc->desired_dr_role = mode;spin_unlock_irqrestore(&dwc->lock, flags);向system_freezable_wq 添加drd_workqueue_work(system_freezable_wq, &dwc->drd_work);
}
__dwc3_set_mode触发
current_dr_role
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{u32 reg;reg = dwc3_readl(dwc->regs, DWC3_GCTL);reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));reg |= DWC3_GCTL_PRTCAPDIR(mode);dwc3_writel(dwc->regs, DWC3_GCTL, reg);dwc->current_dr_role = mode;
}
EXPORT_SYMBOL(dwc3_set_prtcap);
desired_dr_role = DWC3_GCTL_PRTCAP_DEVICE
设置usb phy 模式PHY_MODE_USB_DEVICE
static void __dwc3_set_mode(struct work_struct *work)
{struct dwc3 *dwc = work_to_dwc(work);unsigned long flags;int ret;u32 reg;if (dwc->dr_mode != USB_DR_MODE_OTG)return;if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)dwc3_otg_update(dwc, 0);if (!dwc->desired_dr_role)return;if (dwc->desired_dr_role == dwc->current_dr_role)return;if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)return;switch (dwc->current_dr_role) {case DWC3_GCTL_PRTCAP_HOST:dwc3_host_exit(dwc);break;case DWC3_GCTL_PRTCAP_DEVICE:dwc3_gadget_exit(dwc);dwc3_event_buffers_cleanup(dwc);break;case DWC3_GCTL_PRTCAP_OTG:dwc3_otg_exit(dwc);spin_lock_irqsave(&dwc->lock, flags);dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;spin_unlock_irqrestore(&dwc->lock, flags);dwc3_otg_update(dwc, 1);break;default:break;}spin_lock_irqsave(&dwc->lock, flags);dwc3_set_prtcap(dwc, dwc->desired_dr_role);spin_unlock_irqrestore(&dwc->lock, flags);switch (dwc->desired_dr_role) {case DWC3_GCTL_PRTCAP_HOST:ret = dwc3_host_init(dwc);if (ret) {dev_err(dwc->dev, "failed to initialize host\n");} else {if (dwc->usb2_phy[0])otg_set_vbus(dwc->usb2_phy[0]->otg, true);phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);if (dwc->dis_split_quirk) {reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);reg |= DWC3_GUCTL3_SPLITDISABLE;dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);}}break;case DWC3_GCTL_PRTCAP_DEVICE:dwc3_event_buffers_setup(dwc);if (dwc->usb2_phy[0])otg_set_vbus(dwc->usb2_phy[0]->otg, false);phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);ret = dwc3_gadget_init(dwc);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;}}
set vbus 为false
/* Context: can sleep */
static inline int
otg_set_vbus(struct usb_otg *otg, bool enabled)
{if (otg && otg->set_vbus)return otg->set_vbus(otg, enabled);return -ENOTSUPP;
}
mv_otg_set_vbus
static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
{struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);if (mvotg->pdata->set_vbus == NULL)return -ENODEV;return mvotg->pdata->set_vbus(on);
}
dwc3_gadget_init
RK DWC3 gadget模块 分析_udc dwc-CSDN博客
/*** dwc3_gadget_init - initializes gadget related registers* @dwc: pointer to our controller context structure** Returns 0 on success otherwise negative errno.*/
int dwc3_gadget_init(struct dwc3 *dwc)
{int ret;int irq;irq = dwc3_gadget_get_irq(dwc);if (irq < 0) {ret = irq;goto err0;}dwc->irq_gadget = irq;INIT_WORK(&dwc->remote_wakeup_work, dwc3_gadget_remote_wakeup_work);dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,sizeof(*dwc->ep0_trb) * 2,&dwc->ep0_trb_addr, GFP_KERNEL);if (!dwc->ep0_trb) {dev_err(dwc->dev, "failed to allocate ep0 trb\n");ret = -ENOMEM;goto err0;}dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);if (!dwc->setup_buf) {ret = -ENOMEM;goto err1;}dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,&dwc->bounce_addr, GFP_KERNEL);if (!dwc->bounce) {ret = -ENOMEM;goto err2;}init_completion(&dwc->ep0_in_setup);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 = !dwc->usb2_gadget_lpm_disable;/** FIXME We might be setting max_speed to <SUPER, however versions* <2.20a of dwc3 have an issue with metastability (documented* elsewhere in this driver) which tells us we can't set max speed to* anything lower than SUPER.** Because gadget.max_speed is only used by composite.c and function* drivers (i.e. it won't go into dwc3's registers) we are allowing this* to happen so we avoid sending SuperSpeed Capability descriptor* together with our BOS descriptor as that could confuse host into* thinking we can handle super speed.** Note that, in fact, we won't even support GetBOS requests when speed* is less than super speed because we don't have means, yet, to tell* composite.c that we are USB 2.0 + LPM ECN.*/if (dwc->revision < DWC3_REVISION_220A &&!dwc->dis_metastability_quirk)dev_info(dwc->dev, "changing max_speed on rev %08x\n",dwc->revision);dwc->gadget.max_speed = dwc->max_hw_supp_speed;/** REVISIT: Here we should clear all pending IRQs to be* sure we're starting from a well known location.*/if (!dwc->num_eps)dwc->num_eps = DWC3_ENDPOINTS_NUM;ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);if (ret)goto err3;//将gadget dev注册 gadget与udc 绑定, 把udc添加进udc_list列表中ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);if (ret) {dev_err(dwc->dev, "failed to register udc\n");goto err4;}return 0;err4:dwc3_gadget_free_endpoints(dwc);err3:dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,dwc->bounce_addr);err2:kfree(dwc->setup_buf);err1:dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,dwc->ep0_trb, dwc->ep0_trb_addr);err0:return ret;
}/* -------------------------------------------------------------------------- */
3.usb_add_gadget_udc
注册gadget设备,添加一个gadget到udc class driver列表:
usb_add_gadget_udc@drivers/usb/gadget/udc/core.c
->usb_add_gadget_udc_release
原文链接:https://blog.csdn.net/kenny_wju/article/details/124651142
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,void (*release)(struct device *dev))
{struct usb_udc *udc;int ret = -ENOMEM;udc = kzalloc(sizeof(*udc), GFP_KERNEL);dev_set_name(&gadget->dev, "gadget");INIT_WORK(&gadget->work, usb_gadget_state_work);ret = device_register(&gadget->dev); //gadget dev注册//初始化udc字段device_initialize(&udc->dev);udc->dev.release = usb_udc_release;udc->dev.class = udc_class;udc->dev.groups = usb_udc_attr_groups;udc->dev.parent = parent;ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));udc->gadget = gadget;gadget->udc = udc;mutex_lock(&udc_lock);list_add_tail(&udc->list, &udc_list); //把udc添加进udc_list列表中ret = device_add(&udc->dev);usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);udc->vbus = true;/* pick up one of pending gadget drivers */ret = check_pending_gadget_drivers(udc);}
Linux usb 4. Device 详解_dwc2 not connected-CSDN博客
check_pending_gadget_drivers
/* should be called with udc_lock held */
static int check_pending_gadget_drivers(struct usb_udc *udc)
{struct usb_gadget_driver *driver;int ret = 0;/*遍历 `gadget_driver_pending_list` 链表中的 Driver,和 Device 进行 match()且一个 Driver 只能 match 一个 Device,Driver match 成功后会从链表删除*/list_for_each_entry(driver, &gadget_driver_pending_list, pending)if (!driver->udc_name || strcmp(driver->udc_name,dev_name(&udc->dev)) == 0) {//从pending list中找到drive 没有udc_name //或者name 匹配的进行绑定 udc 和driverret = udc_bind_to_driver(udc, driver);if (ret != -EPROBE_DEFER)list_del_init(&driver->pending);break;}return ret;
}
module_usb_composite_driver
static struct usb_composite_driver webcam_driver = {.name = "g_webcam",.dev = &webcam_device_descriptor,.strings = webcam_device_strings,.max_speed = USB_SPEED_SUPER,.bind = webcam_bind,.unbind = webcam_unbind,
};
module_usb_composite_driver(webcam_driver)#define module_usb_composite_driver(__usb_composite_driver) \module_driver(__usb_composite_driver, usb_composite_probe, \usb_composite_unregister)#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \转换后就是
usb_composite_probe(usb_composite_driver)
先进行composite probe 将driver pending绑到pending list
usb_composite_probe
int usb_composite_probe(struct usb_composite_driver *driver)
{struct usb_gadget_driver *gadget_driver;if (!driver || !driver->dev || !driver->bind)return -EINVAL;if (!driver->name)driver->name = "composite";driver->gadget_driver = composite_driver_template;gadget_driver = &driver->gadget_driver;gadget_driver->function = (char *) driver->name;gadget_driver->driver.name = driver->name;gadget_driver->max_speed = driver->max_speed;return usb_gadget_probe_driver(gadget_driver);
}
EXPORT_SYMBOL_GPL(usb_composite_probe);
usb_gadget_probe_driver
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{struct usb_udc *udc = NULL;int ret = -ENODEV;if (!driver || !driver->bind || !driver->setup)return -EINVAL;mutex_lock(&udc_lock);if (driver->udc_name) {//如果 Driver 有 udc_name查询udc list 找到match的 udc name list_for_each_entry(udc, &udc_list, list) {ret = strcmp(driver->udc_name, dev_name(&udc->dev));if (!ret)break;}if (ret)ret = -ENODEV;else if (udc->driver)ret = -EBUSY;elsegoto found;} else {// 如果 Driver 没有 udc_name,尝试适配 udc_list 链表中第一个没有适配的 Device */list_for_each_entry(udc, &udc_list, list) {/* For now we take the first one */if (!udc->driver)goto found;}}//如果匹配不到将driver 添加到pengding listif (!driver->match_existing_only) {list_add_tail(&driver->pending, &gadget_driver_pending_list);pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",driver->function);ret = 0;}mutex_unlock(&udc_lock);return ret;
found:ret = udc_bind_to_driver(udc, driver);mutex_unlock(&udc_lock);return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
usb_gadget_probe_driver
将usb_composite_driver 的gadge_driver->pending 添加到gadget_driver_pending_list
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{struct usb_udc *udc = NULL;int ret = -ENODEV;if (!driver || !driver->bind || !driver->setup)return -EINVAL;mutex_lock(&udc_lock);if (driver->udc_name) {list_for_each_entry(udc, &udc_list, list) {ret = strcmp(driver->udc_name, dev_name(&udc->dev));if (!ret)break;}if (ret)ret = -ENODEV;else if (udc->driver)ret = -EBUSY;elsegoto found;} else {list_for_each_entry(udc, &udc_list, list) {/* For now we take the first one */if (!udc->driver)goto found;}}if (!driver->match_existing_only) {list_add_tail(&driver->pending, &gadget_driver_pending_list);pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",driver->function);ret = 0;}mutex_unlock(&udc_lock);return ret;
found:ret = udc_bind_to_driver(udc, driver);mutex_unlock(&udc_lock);return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
linux usb gadget驱动详解(三)_linux gadget u盘驱动-CSDN博客
driver->bind (composite_bind)
->composite->bind(usb_composite_driver bind)
->gadget/legacy(composite driver)
->usb_get_function_instance
//获取对应name的function 查function list(func_list)匹配对应的function
usb_add_config->(composite driver config bind)
->usb_get_function //调用usb_function_register注册的alloc_func接口,获取usb_function
->usb_add_function //配置 usb_function config 将function 添加到config->function上,
//调用usb_function bind函数uvc_function_bind
/* ------------------------------------------------------------------------- */static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{int ret;dev_dbg(&udc->dev, "registering UDC driver [%s]\n",driver->function);//将udc->driver与usb gadget driver composite_driver_template绑定udc->driver = driver;udc->gadget->dev.driver = &driver->driver;usb_gadget_udc_set_speed(udc, driver->max_speed);//composite_bindret = driver->bind(udc->gadget, driver);if (ret)goto err1;ret = usb_gadget_udc_start(udc);if (ret) {driver->unbind(udc->gadget);goto err1;}usb_udc_connect_control(udc);kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);return 0;
err1:if (ret != -EISNAM)dev_err(&udc->dev, "failed to start %s: %d\n",udc->driver->function, ret);udc->driver = NULL;udc->gadget->dev.driver = NULL;return ret;
}
注册对应的function接口
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc)
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \static int __init _name ## mod_init(void) \{ \return usb_function_register(&_name ## usb_func); \} \static void __exit _name ## mod_exit(void) \{ \usb_function_unregister(&_name ## usb_func); \} \module_init(_name ## mod_init); \module_exit(_name ## mod_exit)#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \static struct usb_function_driver _name ## usb_func = { \.name = __stringify(_name), \.mod = THIS_MODULE, \.alloc_inst = _inst_alloc, \.alloc_func = _func_alloc, \}; \MODULE_ALIAS("usbfunc:"__stringify(_name));static struct usb_function_driver _name ## usb_func = { \.name = __stringify(_name), \.mod = THIS_MODULE, \.alloc_inst = uvc_alloc_inst, \.alloc_func = uvc_alloc, \}; usb_function_register(uvc_usb_func)
static int
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{struct usb_composite_dev *cdev = c->cdev;struct uvc_device *uvc = to_uvc(f);struct usb_string *us;unsigned int max_packet_mult;unsigned int max_packet_size;struct usb_ep *ep;struct f_uvc_opts *opts;int ret = -EINVAL;uvcg_info(f, "%s()\n", __func__);opts = fi_to_f_uvc_opts(f->fi);/* Sanity check the streaming endpoint module parameters.*/opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);/* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */if (opts->streaming_maxburst &&(opts->streaming_maxpacket % 1024) != 0) {opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);uvcg_info(f, "overriding streaming_maxpacket to %d\n",opts->streaming_maxpacket);}/* Fill in the FS/HS/SS Video Streaming specific descriptors from the* module parameters.** NOTE: We assume that the user knows what they are doing and won't* give parameters that their UDC doesn't support.*/if (opts->streaming_maxpacket <= 1024) {max_packet_mult = 1;max_packet_size = opts->streaming_maxpacket;} else if (opts->streaming_maxpacket <= 2048) {max_packet_mult = 2;max_packet_size = opts->streaming_maxpacket / 2;} else {max_packet_mult = 3;max_packet_size = opts->streaming_maxpacket / 3;}uvc_fs_streaming_ep.wMaxPacketSize =cpu_to_le16(min(opts->streaming_maxpacket, 1023U));uvc_fs_streaming_ep.bInterval = opts->streaming_interval;uvc_hs_streaming_ep.wMaxPacketSize =cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));/* A high-bandwidth endpoint must specify a bInterval value of 1 */if (max_packet_mult > 1)uvc_hs_streaming_ep.bInterval = 1;elseuvc_hs_streaming_ep.bInterval = opts->streaming_interval;uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);uvc_ss_streaming_ep.bInterval = opts->streaming_interval;uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;uvc_ss_streaming_comp.wBytesPerInterval =cpu_to_le16(max_packet_size * max_packet_mult *(opts->streaming_maxburst + 1));/* Allocate endpoints. */ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);if (!ep) {uvcg_info(f, "Unable to allocate control EP\n");goto error;}uvc->control_ep = ep;// 根据usb_composite_probe 初始化usb_composite_driver//(webcam_driver)中配置得速率判断,判断设备支持得速率if (gadget_is_superspeed(c->cdev->gadget))ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,&uvc_ss_streaming_comp);else if (gadget_is_dualspeed(cdev->gadget))ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);elseep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);if (!ep) {uvcg_info(f, "Unable to allocate streaming EP\n");goto error;}uvc->video.ep = ep;uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;us = usb_gstrings_attach(cdev, uvc_function_strings,ARRAY_SIZE(uvc_en_us_strings));if (IS_ERR(us)) {ret = PTR_ERR(us);goto error;}uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;ret = us[UVC_STRING_STREAMING_IDX].id;uvc_streaming_intf_alt0.iInterface = ret;uvc_streaming_intf_alt1.iInterface = ret;/* Allocate interface IDs. */if ((ret = usb_interface_id(c, f)) < 0)goto error;uvc_iad.bFirstInterface = ret;uvc_control_intf.bInterfaceNumber = ret;uvc->control_intf = ret;opts->control_interface = ret;if ((ret = usb_interface_id(c, f)) < 0)goto error;uvc_streaming_intf_alt0.bInterfaceNumber = ret;uvc_streaming_intf_alt1.bInterfaceNumber = ret;uvc->streaming_intf = ret;opts->streaming_interface = ret;/* Copy descriptors */f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);if (IS_ERR(f->fs_descriptors)) {ret = PTR_ERR(f->fs_descriptors);f->fs_descriptors = NULL;goto error;}if (gadget_is_dualspeed(cdev->gadget)) {f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);if (IS_ERR(f->hs_descriptors)) {ret = PTR_ERR(f->hs_descriptors);f->hs_descriptors = NULL;goto error;}}if (gadget_is_superspeed(c->cdev->gadget)) {f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);if (IS_ERR(f->ss_descriptors)) {ret = PTR_ERR(f->ss_descriptors);f->ss_descriptors = NULL;goto error;}}/* Preallocate control endpoint request. */uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);if (uvc->control_req == NULL || uvc->control_buf == NULL) {ret = -ENOMEM;goto error;}uvc->control_req->buf = uvc->control_buf;uvc->control_req->complete = uvc_function_ep0_complete;uvc->control_req->context = uvc;// //注册video 节点if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {uvcg_err(f, "failed to register V4L2 device\n");goto error;}/* Initialise video. */ret = uvcg_video_init(&uvc->video, uvc);if (ret < 0)goto error;//注册video v4l2_dev 绑定v4l2_ops ioctl_op/* Register a V4L2 device. */ret = uvc_register_video(uvc);if (ret < 0) {uvcg_err(f, "failed to register video device\n");goto error;}return 0;error:v4l2_device_unregister(&uvc->v4l2_dev);if (uvc->control_req)usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);kfree(uvc->control_buf);usb_free_all_descriptors(f);return ret;
}
uvcg_video_init
/** Initialize the UVC video stream.*/
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{INIT_LIST_HEAD(&video->req_free);spin_lock_init(&video->req_lock);//初始化camera 分辨率video->uvc = uvc;video->fcc = V4L2_PIX_FMT_YUYV;video->bpp = 16;video->width = 320;video->height = 240;video->imagesize = 320 * 240 * 2;/* Initialize the video buffers queue. */uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT,&video->mutex);return 0;
}
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,struct mutex *lock)
{int ret;//V4L2_BUF_TYPE_VIDEO_OUTPUT指定buf的类型output,用于视频输出设备queue->queue.type = type;queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;queue->queue.drv_priv = queue;queue->queue.buf_struct_size = sizeof(struct uvc_buffer);queue->queue.ops = &uvc_queue_qops;queue->queue.lock = lock;queue->queue.mem_ops = &vb2_vmalloc_memops;queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC| V4L2_BUF_FLAG_TSTAMP_SRC_EOF;//调用vb2_queue_init方法创建并初始化一个vb2_queue用于数据缓冲区的管理。ret = vb2_queue_init(&queue->queue);if (ret)return ret;spin_lock_init(&queue->irqlock);INIT_LIST_HEAD(&queue->irqqueue);queue->flags = 0;return 0;
}
uvc驱动中的v4l2_v4l2 uvc-CSDN博客
这篇关于dwc3 DR_MODE 处理初始化 OTG gadget的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!