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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/19437.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

管道液位传感器可以检测哪些液体?

管道液位传感器是一种专门用于检测流动性比较好的液体的传感器装置。它采用光学感应原理&#xff0c;不涉及任何机械运动&#xff0c;具有长寿命、安装方便和微功耗的特点。相比传统机械式液位传感器&#xff0c;光电管道传感器有效解决了低精度和卡死失效等问题&#xff0c;同…

Django 解决 CSRF 问题

在 Django 出现 CSRF 问题 要解决这个问题&#xff0c;就得在 html 里这么修改 <!DOCTYPE html> <html><head></head><body><form action"/login/" method"post">{% csrf_token %}</form></body> </…

C++基础知识之类和对象

一、类 类是一种用户自定义的数据类型&#xff0c;用于封装数据和方法。它定义了一组属性&#xff08;数据成员&#xff09;和方法&#xff08;成员函数&#xff09;&#xff0c;并且可以被多个对象共享。在面向对象编程中&#xff0c;类是一种用于创建对象的蓝图或模板。它定义…

短视频脚本创作的五个方法 沈阳短视频剪辑培训

说起脚本&#xff0c;我们大概都听过影视剧脚本、剧本&#xff0c;偶尔可能在某些综艺节目里听过台本。其中剧本是影视剧拍摄的大纲&#xff0c;用来指导影视剧剧情的走向和发展&#xff0c;而台本则是综艺节目流程走向的指导大纲。 那么&#xff0c;短视频脚本是什么&#xf…

探析GPT-4o:技术之巅的跃进

如何评价GPT-4o? 简介&#xff1a;最近&#xff0c;GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价&#xff0c;包括版本间的对比分析、GPT-4o的技术能力以及个人感受等。 随着人工智能领域的不断发展&#xff0c;GPT系列模型一直处于行业的前沿。最近&#xff0c;GPT-4…

前端实习记录——git篇(一些问题与相关命令)

1、版本控制 &#xff08;1&#xff09;版本回滚 git log // 查看版本git reset --mixed HEAD^ // 回滚到修改状态&#xff0c;文件内容没有变化git reset --soft HEAD^ // 回滚暂存区&#xff0c;^的个数代表几个版本git reset --hard HEAD^ // 回滚到修改状态&#xff…

生态农业:引领未来农业新篇章

生态农业&#xff0c;正以其独特的魅力和创新理念&#xff0c;引领着未来农业发展的新篇章。在这个充满变革的时代&#xff0c;我们需要更加关注农业的可持续发展&#xff0c;而生态农业正是实现这一目标的重要途径。 生态农业产业的王总说&#xff1a;生态农业强调生态平衡和可…

python基础-内置函数3-类与对象相关内置函数

文章目录 python基础-内置函数3类与对象getattr()hasattr()setattr()delattr()vars()dir()property()super()classmethod()staticmethod()isinstance()issubclass()callable()object()repr()ascii()id()hash()type() python基础-内置函数3 类与对象 getattr() getattr(objec…

以讲师能力提升,优路教育促学员拓宽职业原野

在建设教育强国的过程中&#xff0c;加强教师队伍建设被视为重要的基础工作。当前&#xff0c;我国正大力推进高素质“双师型”职业教育教师队伍建设&#xff0c;以培养更多既具备理论教学能力&#xff0c;又拥有实践教学经验的教师。在这一背景下&#xff0c;优路教育积极响应…

【文档+源码+调试讲解】古典舞在线交流平台的设计与实现

摘 要 随着互联网技术的发展&#xff0c;各类网站应运而生&#xff0c;网站具有新颖、展现全面的特点。因此&#xff0c;为了满足用户古典舞在线交流的需求&#xff0c;特开发了本古典舞在线交流平台。 本古典舞在线交流平台应用Java技术&#xff0c;MYSQL数据库存储数据&…

构建一个简单的情感分析器:使用Python和spaCy

构建一个简单的情感分析器&#xff1a;使用Python和spaCy 引言 情感分析是自然语言处理&#xff08;NLP&#xff09;中的一项重要技术&#xff0c;它可以帮助企业和研究人员理解公众对特定主题或产品的看法。 在本篇文章中&#xff0c;我们将使用Python编程语言和 spaCy 库来构…

FreeRTOS【7】队列使用

1.开发背景 操作系统提供了多线程并行的操作&#xff0c;为了方便代码的维护&#xff0c;各个线程都分配了专用的内存并处理对应的内容。但是线程间也是需要协助操作的&#xff0c;例如一个主线程接收信息&#xff0c;会把接收的信息并发到其他线程&#xff0c;即主线程不阻塞&…

[LitCTF 2023]yafu (中级) (素数分解)

题目&#xff1a; from Crypto.Util.number import * from secret import flagm bytes_to_long(flag) n 1 for i in range(15):n *getPrime(32) e 65537 c pow(m,e,n) print(fn {n}) print(fc {c})n 152412082177688498871800101395902107678314310182046454156816957…

3D模型展示适合哪些类型的产品?

3D模型展示特别适合那些需要全面展示产品细节和特性的商品&#xff0c;产品3D交互展示具有直观性、动态性、交互性等显著优势&#xff0c;可以通过51建模网一站式完成商品3D建模、3D展示、3D定制、AR试穿等功能&#xff0c;以下是一些适合使用3D模型展示的产品类型&#xff1a;…

Compose第三弹 列表

目标&#xff1a; 1.根据列表数据&#xff0c;生成列表 一、列表控件 使用 Compose 的 LazyColumn 和 LazyRow。这些可组合项只会呈现屏幕上显示的元素&#xff0c;因此&#xff0c;对于较长的列表&#xff0c;使用它们会非常高效。 LazyListScope.items会延迟创建列表视图&…

npm镜像源管理、nvm安装多版本node异常处理

查看当前使用的镜像源 npm config get registry --locationglobal 设置使用官方源 npm config set registry https://registry.npmjs.org/ --locationglobal 设置淘宝镜像源 npm config set registry https://registry.npm.taobao.org/ --locationglobal 需要更改淘宝镜像源地址…

Java培训后找不到工作,现在去培训嵌入式可行吗?

最近java 工作还是比较好找&#xff0c;不知道你是对薪资要求太高&#xff0c;还是因为其他原因&#xff0c;如果你真的面试了很多都还找不到工作&#xff0c;那么一定要知道找不到工作的原因是啥&#xff0c;一定不是因为java 太卷&#xff0c;你说那个行业&#xff0c;那个职…

多用户协作与实时渲染:3D开发工具HOOPS助力汽车行业CAD可视化

在当今汽车行业&#xff0c;计算机辅助设计&#xff08;CAD&#xff09;可视化是产品设计和开发过程中不可或缺的一环。随着汽车设计复杂性的增加&#xff0c;CAD可视化不仅仅是一个设计工具&#xff0c;更是一个沟通和协作的平台。然而&#xff0c;尽管技术不断进步&#xff0…

Vue3实战笔记(51)—Vue 3封装带均线的k线图

文章目录 前言带均线的k线图总结 前言 继续封装一个封装带均线的k线图 带均线的k线图 EChartsCandlestickSh.vue&#xff1a; <template><div ref"chartContainer" style"width: 100%; height: 500px"></div></template><scr…

西湖大学提出AIGC检测框架,精准识别AI撰写的文稿

近年来人工智能技术突飞猛进&#xff0c;尤其是大语言模型的出现&#xff0c;让AI具备了创作文章、小说、剧本等内容的能力。 AI代写&#xff0c;已经逃不过老师、编辑、审稿人的火眼金睛了。但让AI仅改写部分片段&#xff0c;就安全了么&#xff1f; 针对检测AI改写的片段&a…