IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> usb peripheral 驱动 - configfs -> 正文阅读

[移动开发]usb peripheral 驱动 - configfs

1. configfs init

1.1 configfs usage

参考文档:msm-kernel\Documentation\usb\gadget_configfs.rst
(1) Creating the gadgets:

mkdir /config/usb_gadget/g3		// mkdir $CONFIGFS_HOME/usb_gadget/<gadget name> 

底层调用gadget_make()

(2) Creating the configurations:

cd /config/usb_gadget/g3
mkdir configs/c.1			// mkdir configs/<name>.<number> 

(3) Creating the functions:

mkdir functions/ncm.usb0	// mkdir functions/<name>.<instance name>

底层调用function_make()

(4) Associating the functions with their configurations:

ln -s functions/ncm.usb0 configs/c.1	// ln -s functions/<name>.<instance name> configs/<name>.<number>

底层调用:config_usb_cfg_link()

(5) Enabling the gadget:

echo <udc name> > UDC
echo "a600000.dwc3" > UDC

底层调用:gadget_dev_desc_UDC_store()

1.2 configfs init file

== init.rc		// 源码路径\system\core\rootdir\init.rc
	== import /vendor/etc/init/hw/init.${ro.hardware}.rc
	== init.qcom.rc		// 源码路径\device\qcom\common\rootdir\etc
		== import init.qcom.usb.rc		// filesystem路径/vendor/etc/init/hw/
		== import init.msm.usb.configfs.rc
		== import init.target.rc

1.2.1 init.qcom.usb.rc

该配置文件用于:
(1)创建设备 gadget;
(2)创建配置 config;
(3)创建接口 function。

on boot
    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
    mount configfs none /config			// 挂载configfs
    mkdir /config/usb_gadget/g1 0770	// (1)创建设备gadget
    mkdir /config/usb_gadget/g2 0770
    mkdir /config/usb_gadget/g1/strings/0x409 0770
    mkdir /config/usb_gadget/g2/strings/0x409 0770
    write /config/usb_gadget/g1/bcdUSB 0x0200
    write /config/usb_gadget/g2/bcdUSB 0x0200
    write /config/usb_gadget/g1/os_desc/use 1
    write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno}
    write /config/usb_gadget/g2/strings/0x409/serialnumber ${ro.serialno}
    write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer}
    write /config/usb_gadget/g2/strings/0x409/manufacturer ${ro.product.manufacturer}
    write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model}
    write /config/usb_gadget/g2/strings/0x409/product ${ro.product.model}
    mkdir /config/usb_gadget/g1/functions/mass_storage.0	// (3)创建接口 function
    mkdir /config/usb_gadget/g1/functions/mtp.gs0
    mkdir /config/usb_gadget/g1/functions/ptp.gs1
    mkdir /config/usb_gadget/g1/functions/accessory.gs2
    mkdir /config/usb_gadget/g1/functions/audio_source.gs3
    mkdir /config/usb_gadget/g1/functions/midi.gs5
    mkdir /config/usb_gadget/g1/functions/ffs.adb
    mkdir /config/usb_gadget/g1/functions/diag.diag
    mkdir /config/usb_gadget/g1/functions/diag.diag_mdm
    mkdir /config/usb_gadget/g1/functions/cser.dun.0
    mkdir /config/usb_gadget/g1/functions/cser.nmea.1
    mkdir /config/usb_gadget/g1/functions/cser.dun.2
    mkdir /config/usb_gadget/g1/functions/gsi.rmnet
    mkdir /config/usb_gadget/g1/functions/gsi.rndis
    mkdir /config/usb_gadget/g1/functions/gsi.dpl
    mkdir /config/usb_gadget/g1/functions/qdss.qdss
    mkdir /config/usb_gadget/g1/functions/qdss.qdss_mdm
    mkdir /config/usb_gadget/g1/functions/rndis_bam.rndis
    mkdir /config/usb_gadget/g1/functions/rndis.rndis
    mkdir /config/usb_gadget/g1/functions/rmnet_bam.rmnet
    mkdir /config/usb_gadget/g1/functions/rmnet_bam.dpl
    mkdir /config/usb_gadget/g1/functions/rmnet_bam.rmnet_bam_dmux
    mkdir /config/usb_gadget/g1/functions/rmnet_bam.dpl_bam_dmux
    mkdir /config/usb_gadget/g1/functions/ncm.0
    mkdir /config/usb_gadget/g1/functions/ccid.ccid
    mkdir /config/usb_gadget/g1/functions/uac2.0
    mkdir /config/usb_gadget/g1/functions/uvc.0
    mkdir /config/usb_gadget/g1/functions/uvc.1
    mkdir /config/usb_gadget/g1/functions/hid.0
    mkdir /config/usb_gadget/g1/functions/hid.1
    mkdir /config/usb_gadget/g1/functions/hid.2
    mkdir /config/usb_gadget/g1/functions/hid.3
    mkdir /config/usb_gadget/g1/functions/hid.4
    mkdir /config/usb_gadget/g1/functions/hid.5
    mkdir /config/usb_gadget/g1/configs/b.1 0770	//(2)创建配置config
    mkdir /config/usb_gadget/g2/configs/b.1 0770
    mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770
    mkdir /config/usb_gadget/g2/configs/b.1/strings/0x409 0770
    write /config/usb_gadget/g1/os_desc/b_vendor_code 0x1
    write /config/usb_gadget/g1/os_desc/qw_sign "MSFT100"
    symlink /config/usb_gadget/g1/configs/b.1 /config/usb_gadget/g1/os_desc/b.1
    mkdir /dev/usb-ffs 0775 shell system
    mkdir /dev/usb-ffs/adb 0770 shell system
    mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=1000,rmode=0770,fmode=0660
    write /sys/class/android_usb/android0/f_ffs/aliases adb
    setprop sys.usb.mtp.device_type 2
    setprop vendor.usb.controller ${sys.usb.controller}
    enable vendor.qcom-usb-sh

1.2.2 init.msm.usb.configfs.rc

该配置文件用于读取上层 property,然后 enable 对应 config 的所有 function。

// sys.usb.config=diag,adb 表示该配置拥有diag 和adb 两个接口功能;
// 可使用getprop sys.usb.config 进行查询,setprop sys.usb.config **进行配置切换
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=diag,adb && property:sys.usb.configfs=1
    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "diag_adb"
    rm /config/usb_gadget/g1/configs/b.1/f1
    rm /config/usb_gadget/g1/configs/b.1/f2
    rm /config/usb_gadget/g1/configs/b.1/f3
    rm /config/usb_gadget/g1/configs/b.1/f4
    rm /config/usb_gadget/g1/configs/b.1/f5
    rm /config/usb_gadget/g1/configs/b.1/f6
    rm /config/usb_gadget/g1/configs/b.1/f7
    rm /config/usb_gadget/g1/configs/b.1/f8
    rm /config/usb_gadget/g1/configs/b.1/f9
    rm /config/usb_gadget/g1/configs/b.1/f10
    rm /config/usb_gadget/g1/configs/b.1/f11
    write /config/usb_gadget/g1/idVendor 0x05C6
    write /config/usb_gadget/g1/idProduct 0x901D
    symlink /config/usb_gadget/g1/functions/diag.diag /config/usb_gadget/g1/configs/b.1/f1
    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
    setprop sys.usb.state ${sys.usb.config}

2. 驱动模块初始化

2.1 function 驱动初始化

所有的 function 驱动都注册在一个链表中 func_list,当 configfs 驱动启动时,读取上层配置的 function 去与链表中的匹配。
\drivers\usb\gadget\function\f_fs.c 中,用如下宏去注册 function 驱动(将 usb_function_driver 加入func_list链表),该 init 操作在系统初始化的时候就会被执行。

#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)

2.2 configfs 驱动初始化

\drivers\usb\gadget\configfs.c

在系统加载驱动模块的时候初始化gadget configfs,包括提供给上层的一些操作接口(mkdir write…

== module_init(gadget_cfs_init);
	== configfs_register_subsystem(&gadget_subsys);
		gadget_subsys == gadgets_type == gadgets_ops
		== gadgets_make();		//gadget_make会在上层执行mkdir 的时候调用
			== struct gadget_info *gi;		//构建gadget_info
			== gi->composite.gadget_driver = configfs_driver_template;		//构建usb_gadget_driver

struct gadget_info {
	struct usb_composite_driver composite;
	struct usb_composite_dev cdev;
};

static const struct usb_gadget_driver configfs_driver_template = {	//usb_gadget_driver
	.bind           = configfs_composite_bind,
	.unbind         = configfs_composite_unbind,

#ifdef CONFIG_USB_CONFIGFS_UEVENT
	.setup          = android_setup,
#else
	.setup          = configfs_composite_setup,
#endif
	.reset          = configfs_composite_reset,
	.disconnect     = configfs_composite_disconnect,
	.suspend	= configfs_composite_suspend,
	.resume		= configfs_composite_resume,
};

2.3 dwc3 初始化

在dwc3 初始化过程中,会构建 usb_gadget 设备。

== dwc3_probe(struct platform_device *pdev);
	== dwc3_core_init_mode(struct dwc3 *dwc);
		== dwc3_gadget_init(dwc);

int dwc3_gadget_init(struct dwc3 *dwc)
{	// 初始化usb_gadget
	...
	dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);

	usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
	dev				= &dwc->gadget->dev;
	dev->platform_data		= dwc;
	dwc->gadget->ops		= &dwc3_gadget_ops;
	dwc->gadget->speed		= USB_SPEED_UNKNOWN;
	dwc->gadget->sg_supported	= true;
	dwc->gadget->name		= "dwc3-gadget";
	dwc->gadget->lpm_capable	= true;

	ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);

	ret = usb_add_gadget(dwc->gadget);
}	

2.3.1 struct usb_gadget

struct usb_gadget {
	struct work_struct		work;
	struct usb_udc			*udc;			// udc
	/* readonly to gadget driver */
	const struct usb_gadget_ops	*ops;		// gadget的操作函数
	struct usb_ep			*ep0;			// 端点0
	struct list_head		ep_list;		/* of usb_ep */
	enum usb_device_speed		speed;
	enum usb_device_speed		max_speed;
	enum usb_device_state		state;
	const char			*name;
	struct device			dev;
	...
}

3. 底层对应上层的接口

3.1 gadget_make

mkdir /config/usb_gadget/g3 底层调用gadget_make()

static struct config_group *gadgets_make(struct config_group *group, const char *name)
{
	struct gadget_info *gi;
	...
	config_group_init_type_name(&gi->group, name, &gadget_root_type);	// 根group

	config_group_init_type_name(&gi->functions_group, "functions", &functions_type);
	configfs_add_default_group(&gi->functions_group, &gi->group); 		// function group

	config_group_init_type_name(&gi->configs_group, "configs", &config_desc_type);
	configfs_add_default_group(&gi->configs_group, &gi->group);			// configs group
	...
	gi->composite.max_speed = USB_SPEED_SUPER;

	gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
	gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
	gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());

	gi->composite.gadget_driver = configfs_driver_template;		// init usb_gadget_driver 

	gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
	gi->composite.name = gi->composite.gadget_driver.function;
	...
}

3.1.1 struct gadget_info

struct gadget_info {
	struct config_group group;				// 一些group
	struct config_group functions_group;
	struct config_group configs_group;
	...
	struct usb_composite_driver composite;		// 复合设备驱动
	struct usb_composite_dev cdev;				// 复合设备
	...
};

3.1.2 struc usb_composite_driver

struct usb_composite_driver {
	const char				*name;
	const struct usb_device_descriptor	*dev;				// 设备描述符
	enum usb_device_speed			max_speed;

	int			(*bind)(struct usb_composite_dev *cdev);	// configfs_do_nothing
	int			(*unbind)(struct usb_composite_dev *);		// configfs_do_nothing

	void			(*disconnect)(struct usb_composite_dev *);

	void			(*suspend)(struct usb_composite_dev *);		// NULL
	void			(*resume)(struct usb_composite_dev *);		// NULL
	struct usb_gadget_driver		gadget_driver;				// 重要
};

3.1.3 struct usb_composite_dev

struct usb_composite_dev {  // 复合设备
	struct usb_gadget		*gadget;  		// 重要,usb设备控制器的抽象,指向dwc3结构体中的usb_gadget
	struct usb_request		*req;     		// 用于响应控制请求,缓冲区提前分配好
	struct usb_request		*os_desc_req;  	// 用于响应OS描述符,缓冲区提前分配
	struct usb_configuration	*config;  	// 当前使用配置
	struct usb_configuration *os_desc_config;  	// OS描述符使用的配置
	struct usb_device_descriptor desc; 			// 设备描述符

	struct usb_composite_driver	*driver;  	// 指向Composite驱动
    ......
};

3.1.4 struct usb_gadget_driver

struct usb_gadget_driver {
	char			*function;
	enum 			usb_device_speed	max_speed;
	int				(*bind)(struct usb_gadget *gadget,
					struct usb_gadget_driver *driver);
	void			(*unbind)(struct usb_gadget *);
	int				(*setup)(struct usb_gadget *,
					const struct usb_ctrlrequest *);
	void			(*disconnect)(struct usb_gadget *);
	void			(*suspend)(struct usb_gadget *);
	void			(*resume)(struct usb_gadget *);
	void			(*reset)(struct usb_gadget *);

	/* FIXME support safe rmmod */
	struct device_driver	driver;		// 继承device_driver

	char			*udc_name;
};

3.2 function_make

上层:mkdir /config/usb_gadget/g1/functions/diag.diag
底层:function_make()

 static struct config_group *function_make(
		struct config_group *group,
		const char *name)

function_make 调用过程:

== configfs_mkdir();
	== function_make(struct config_group *group, const char *name); 
		== usb_get_function_instance(); 
			== try_get_usb_function_instance(const char *name);		// 从func_list 中找到fd
				fd->alloc_inst(); 
				== ncm_alloc_inst(); 
					== create_function_device(); 
						== device_create(); 
							== device_add();

3.3 gadget connect

上层:echo “a600000.dwc3” > UDC
底层:gadget_dev_desc_UDC_store();
(1)绑定usb_gadgetusb_gadget_driver
(2)dwc3_gadget_start,注册中断,接收host 的消息;
(3)dwc3_gadget_pullup,enable 端点传输。
kernel 5.4:

== gadget_dev_desc_UDC_store();		// \drivers\usb\gadget\configfs.c
	== usb_gadget_probe_driver(struct usb_gadget_driver *driver)	 // \drivers\usb\gadget\udc\core.c
		== udc_bind_to_driver();	//绑定usb_gadget_driver 与usb_udc
			== driver->bind(udc->gadget, driver);
				== configfs_composite_bind();
					== usb_add_function(c, f);		//添加function,执行function 的函数
			== usb_gadget_udc_start(udc);
				== udc->gadget->ops->udc_start(udc->gadget, udc->driver);
					== dwc3_gadget_start();
						== request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, IRQF_SHARED, "dwc3", dwc->ev_buf);
						//注册dwc3中断,当host发消息过来时,触发中断函数
			== usb_udc_connect_control(struct usb_udc *udc)
				== usb_gadget_connect(struct usb_gadget *gadget)
					== gadget->ops->pullup(gadget, 1);
					== dwc3_gadget_pullup(struct usb_gadget *g, int is_on);
						== dwc3_gadget_run_stop_util();
							== dwc3_gadget_run_stop(dwc, true, false);
								== __dwc3_gadget_start(dwc);		//enable端点传输
									== __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)		// initializes a hw endpoint

3.4 gadget disconnect

上层:echo “none” > UDC

== gadget_dev_desc_UDC_store
	== unregister_gadget(struct gadget_info *gi)
		== usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
			== usb_gadget_remove_driver(struct usb_udc *udc)
			
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
	dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
			udc->driver->function);

	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);	//产生一个uevent 上报

	usb_gadget_disconnect(udc->gadget);				//gadget 驱动断开
	if (udc->gadget->irq)
		synchronize_irq(udc->gadget->irq);
	udc->driver->unbind(udc->gadget);				//取消req(gadget 的端点0 usb_ep)
	usb_gadget_udc_stop(udc);						//udc 停止

	udc->driver = NULL;
	udc->dev.driver = NULL;
	udc->gadget->dev.driver = NULL;
}

3.4.1 disable 控制器的端点dwc3_ep(32个)

== usb_gadget_disconnect(struct usb_gadget *gadget)       
	gadget->ops->pullup(gadget, 0);
	.pullup			= dwc3_gadget_pullup,
	== dwc3_gadget_pullup(struct usb_gadget *g, int is_on)    	// 打开/关闭端点、写寄存器
		== dwc3_stop_active_transfers(dwc);						// 非0 端点
			== dwc3_remove_requests(dwc, dep);
		== __dwc3_gadget_stop(struct dwc3 *dwc)					// disable 端点0(控制端点)传输
			== __dwc3_gadget_ep_disable(struct dwc3_ep *dep)  	// disables a hw endpoint
				== dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)   

3.4.2 取消req(gadget 的端点0 usb_ep)

== udc->driver->unbind(udc->gadget);
	.unbind		= composite_unbind,
	.unbind     = configfs_composite_unbind,
	== configfs_composite_unbind(struct usb_gadget *gadget)
		== composite_dev_cleanup(struct usb_composite_dev *cdev)    		// 清除composite dev上的req
			== usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)	// dequeues (cancels, unlinks) an I/O request from an endpoint
				== ret = ep->ops->dequeue(ep, req);					
					.dequeue	= dwc3_gadget_ep_dequeue, 					//usb_ep转化为dwc3_ep
					==dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request)

4. 一次完整的 Gadget 数据流

Configfs子系统与控制器之间的数据交换是通过struct usb_request *request结构体,一次完整的流程如下:

4.1 function 申请req

\drivers\usb\gadget\function\f_fs.c
(1) 申请 req:

== ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);	trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM);
	== ep->ops->alloc_request(ep, gfp_flags);
	== dwc3_gadget_ep_alloc_request();									trace_dwc3_alloc_request(req);
== ffs->ep0req->complete = ffs_ep0_complete;	// req的完成函数

释放 req:

== ffs_func_unbind();
	== usb_ep_free_request();					trace_usb_ep_free_request(ep, req, 0);
		== ep->ops->free_request(ep, req);
		== dwc3_gadget_ep_free_request();		trace_dwc3_free_request();	

(2) 将req提交到控制器:

== usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);		trace_usb_ep_queue(ep, req, ret);
	== ret = ep->ops->queue(ep, req, gfp_flags);
	==  dwc3_gadget_ep0_queue();
		== __dwc3_gadget_ep0_queue();
			== __dwc3_ep0_do_control_data();
				== dwc3_ep0_start_trans();
					== dwc3_send_gadget_ep_cmd();		trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
						== dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);

4.2 控制器处理req (写寄存器)

== dwc3_gadget_ep_queue();
	== __dwc3_gadget_ep_queue();
		== __dwc3_gadget_start_isoc(struct dwc3_ep *dep);
			==__dwc3_gadget_kick_transfer(struct dwc3_ep *dep);
				== dwc3_send_gadget_ep_cmd(); 	// This function will issue @cmd with given @params to @dep and wait for its completion.
					== dwc3_writel(void __iomem *base, u32 offset, u32 value)

4.3 控制器返回req (中断回调函数)

== dwc3_gadget_start();  						//注册中断
	== request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, IRQF_SHARED, "dwc3", dwc->ev_buf);
	== dwc3_thread_interrupt();					//中断产生(host发消息过来),调用回调函数
		== dwc3_process_event_buf();			//处理中断事件buf(dwc3_event_buffer )
			== dwc3_process_event_entry();		trace_dwc3_event(event->raw, dwc);
				== dwc3_endpoint_interrupt();	//对端点处理的中断
					== dwc3_ep0_interrupt();	//控制传输if (epnum == 0 || epnum == 1) 
						== dwc3_ep0_xfer_complete();		//event->endpoint_event = DWC3_DEPEVT_XFERCOMPLETE
							== dwc3_ep0_inspect_setup();	//dwc->ep0state = EP0_SETUP_PHASE
								//dwc3_ep0_std_request() == dwc3_ep0_set_config()
								== dwc3_ep0_delegate_req();
									== dwc->gadget_driver->setup(&dwc->gadget, ctrl);
									== configfs_composite_setup();	
										== composite_setup();		//setup 包括获取描述符,配置,地址等操作
											== set_config(cdev, ctrl, w_value);	ctrl->bRequest = USB_REQ_SET_CONFIGURATION
					== dwc3_gadget_endpoint_transfer_complete();		//传输完成
						== dwc3_gadget_endpoint_trbs_complete();
							== dwc3_gadget_ep_cleanup_completed_requests()
								== dwc3_gadget_giveback();				//数据返回
									== dwc3_gadget_del_and_unmap_request();		trace_dwc3_gadget_giveback(req);
									== usb_gadget_giveback_request()			trace_usb_gadget_giveback_request(ep, req, 0);// give the request back to the gadget layer
										== req->complete(ep, req);
										== ffs_ep0_complete()	// function的完成函数
				== dwc3_gadget_interrupt(dwc, &event->devt);	//对gadget设备的中断
					== usb_gadget_vbus_draw(&dwc->gadget, 2);	trace_usb_gadget_vbus_draw(gadget, ret);
						== gadget->ops->vbus_draw(gadget, mA);
						== dwc3_gadget_vbus_draw();	
							== dwc3_notify_event(dwc, DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT, 0);		
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:16:18  更:2022-06-25 18:17:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 3:47:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码