dwc3 DR_MODE 处理初始化 OTG gadget

2024-05-30 13:20

本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016266

相关文章

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

Python结合requests和Cheerio处理网页内容的操作步骤

《Python结合requests和Cheerio处理网页内容的操作步骤》Python因其简洁明了的语法和强大的库支持,成为了编写爬虫程序的首选语言之一,requests库是Python中用于发送HT... 目录一、前言二、环境搭建三、requests库的基本使用四、Cheerio库的基本使用五、结合req

使用Python处理CSV和Excel文件的操作方法

《使用Python处理CSV和Excel文件的操作方法》在数据分析、自动化和日常开发中,CSV和Excel文件是非常常见的数据存储格式,ython提供了强大的工具来读取、编辑和保存这两种文件,满足从基... 目录1. CSV 文件概述和处理方法1.1 CSV 文件格式的基本介绍1.2 使用 python 内

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Python中处理NaN值的技巧分享

《Python中处理NaN值的技巧分享》在数据科学和数据分析领域,NaN(NotaNumber)是一个常见的概念,它表示一个缺失或未定义的数值,在Python中,尤其是在使用pandas库处理数据时,... 目录NaN 值的来源和影响使用 pandas 的 isna()和 isnull()函数直接比较 Na

详解Python中通用工具类与异常处理

《详解Python中通用工具类与异常处理》在Python开发中,编写可重用的工具类和通用的异常处理机制是提高代码质量和开发效率的关键,本文将介绍如何将特定的异常类改写为更通用的ValidationEx... 目录1. 通用异常类:ValidationException2. 通用工具类:Utils3. 示例文