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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

明明的随机数处理问题分析与解决方案

明明的随机数处理问题分析与解决方案 引言问题描述解决方案数据结构设计具体步骤伪代码C语言实现详细解释读取输入去重操作排序操作输出结果复杂度分析 引言 明明生成了N个1到500之间的随机整数,我们需要对这些整数进行处理,删去重复的数字,然后进行排序并输出结果。本文将详细讲解如何通过算法、数据结构以及C语言来解决这个问题。我们将会使用数组和哈希表来实现去重操作,再利用排序算法对结果

8. 自然语言处理中的深度学习:从词向量到BERT

引言 深度学习在自然语言处理(NLP)领域的应用极大地推动了语言理解和生成技术的发展。通过从词向量到预训练模型(如BERT)的演进,NLP技术在机器翻译、情感分析、问答系统等任务中取得了显著成果。本篇博文将探讨深度学习在NLP中的核心技术,包括词向量、序列模型(如RNN、LSTM),以及BERT等预训练模型的崛起及其实际应用。 1. 词向量的生成与应用 词向量(Word Embedding)

使用协程实现高并发的I/O处理

文章目录 1. 协程简介1.1 什么是协程?1.2 协程的特点1.3 Python 中的协程 2. 协程的基本概念2.1 事件循环2.2 协程函数2.3 Future 对象 3. 使用协程实现高并发的 I/O 处理3.1 网络请求3.2 文件读写 4. 实际应用场景4.1 网络爬虫4.2 文件处理 5. 性能分析5.1 上下文切换开销5.2 I/O 等待时间 6. 最佳实践6.1 使用 as

Level3 — PART 3 — 自然语言处理与文本分析

目录 自然语言处理概要 分词与词性标注 N-Gram 分词 分词及词性标注的难点 法则式分词法 全切分 FMM和BMM Bi-direction MM 优缺点 统计式分词法 N-Gram概率模型 HMM概率模型 词性标注(Part-of-Speech Tagging) HMM 文本挖掘概要 信息检索(Information Retrieval) 全文扫描 关键词