Qualcomm USB Code Analysis(一) 之 /core/usb.c

前面转载学习了一些在前辈写的文章,了解了相关的原理,
但代码是最好的老最师,为了能够深入的学习USB的工作原理,重点还是要深入到代码中去看,
因此,从本文开始,主要是跟踪代码学习。

We have learned some USB principles from some articles written by predecessors.
But the code aways is the best teacher.
In order to learn the working principle of USB In depth, the key point is to go deep into the code.
Therefore , This article is mainly about tracking code.



带着问题学习:
Learnning with Questions:

目前,领导给了一个任务,研究,如何实现在不断电的前提下切换USB Host/Device 模式:
该问题分解如下:

  1. 如何实现切换 USB Host / Device 模式
  2. 如何实现 当前机器无论是 USB Host 还是 USB Device 模式,均由来当前机器来给USB 设备供电。(Android 默认为Host给Device 供电)。

好,接下来,我们来看代码,看代码过程中,我们重点关注供电相关的代码,
而一些其他需要看的代码,我们可以标记 “ ### ** ”说明后续得补上的的知识。



一、usb_init(void)

USB 模块入口函数在 kernel\msm-3.18\drivers\usb\core\usb.c 的 usb_init(void)中。

/*
 * Init
 */
static int __init usb_init(void)
{
}
subsys_initcall(usb_init);

usb_init 函数详细如下:

// kernel\msm-3.18\drivers\usb\core\usb.c
/*
 * Init
 */
static int __init usb_init(void)
{
	// 1. 检测当前系统是否支持USB模块 ---------> 详见: **待完 1.1 nousb
	if (nousb) {
		pr_info("%s: USB support disabled\n", usbcore_name);
		return 0;
	}
	// 2. 初始化DMA Buffers ---------> **后续分析
	usb_init_pool_max();
	
 	// 3. 初始化调试文件系统 ---------> 详见:**待完 1.2 usb_debugfs_init()
	retval = usb_debugfs_init(); 

	usb_acpi_register();
	
	retval = bus_register(&usb_bus_type);
	===================>>>
	===================>>>
		//\kernel\msm-3.18\drivers\usb\core\driver.c
		struct bus_type usb_bus_type = {
			.name =		"usb",				// Conditions for success of match 
			.match =	usb_device_match,  	// this will be called, when insmod driver or device belong to this bus
			.uevent =	usb_uevent,			// this will be called, Once the device or driver on the bus changes
		};
		
		static int usb_device_match(struct device *dev, struct device_driver *drv)
		{
			//在匹配时,将 设备 和 接口 分离
			/* devices and interfaces are handled separately */
			if (is_usb_device(dev)) {
				//如果是设备
				/* interface drivers never match devices */
				if (!is_usb_device_driver(drv))
					return 0;
				/* TODO: Add real matching code */
				return 1;
			} else if (is_usb_interface(dev)) {
				//如果是接口
				struct usb_interface *intf;
				struct usb_driver *usb_drv;
				const struct usb_device_id *id;
		
				/* device drivers never match interfaces */
				if (is_usb_device_driver(drv))
					return 0;
		
				intf = to_usb_interface(dev); 	//获得 interface 数据
				usb_drv = to_usb_driver(drv);	//获得 USB Driver 数据
				id = usb_match_id(intf, usb_drv->id_table); //匹配 interface 和 usb driver 是否匹配
				if (id)
					return 1;  //如果匹配,则返回 1
		
				id = usb_match_dynamic_id(intf, usb_drv); //如果有多个usb id_table 则进行遍历
				if (id)
					return 1;
			}
			return 0;
		}
		
		// 检测到插入设备后,会调用该函数
		static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
		{
			if (is_usb_device(dev)) {
				usb_dev = to_usb_device(dev);
			} else if (is_usb_interface(dev)) {
				struct usb_interface *intf = to_usb_interface(dev);
				usb_dev = interface_to_usbdev(intf);
			} else {
				return 0;
			}
			
			if (usb_dev->devnum < 0) {
				/* driver is often null here; dev_dbg() would oops */
				pr_debug("usb %s: already deleted?\n", dev_name(dev));
				return -ENODEV;
			}
			if (!usb_dev->bus) {
				pr_debug("usb %s: bus removed?\n", dev_name(dev));
				return -ENODEV;
			}
			//解析 USB PID / VID / BCD 信息
			/* per-device configurations are common */
			if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
					   le16_to_cpu(usb_dev->descriptor.idVendor),
					   le16_to_cpu(usb_dev->descriptor.idProduct),
					   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
				return -ENOMEM;
		
			/* class-based driver binding models */
			if (add_uevent_var(env, "TYPE=%d/%d/%d",
					   usb_dev->descriptor.bDeviceClass,
					   usb_dev->descriptor.bDeviceSubClass,
					   usb_dev->descriptor.bDeviceProtocol))
				return -ENOMEM;
		
			return 0;
		}

		int bus_register(struct bus_type *bus)
		{
			priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);

			priv->bus = bus;
			bus->p = priv;
			BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
			
			// set bus name :  &priv->subsys.kobj->name = "usb"
			retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

			// set "bus" as the parent ,so the folder path is /sys/class/bus/usb/
			priv->subsys.kobj.kset = bus_kset;  
				=====>>> 
				+	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
				<<<=====
				
			priv->subsys.kobj.ktype = &bus_ktype;
				======>>>
				+	static struct kobj_type bus_ktype = {
				+		.sysfs_ops	= &bus_sysfs_ops,
				+		.release	= bus_release,
				+	};
				<<<======
			priv->drivers_autoprobe = 1;
		
			retval = kset_register(&priv->subsys); // register /sys/class/bus/usb path in sys
	
			retval = bus_create_file(bus, &bus_attr_uevent); // create uevent file , so we can store a uevent command at will
				=======>>>  // path with /sys/class/bus/usb/uevent
					static ssize_t bus_uevent_store(struct bus_type *bus, const char *buf, size_t count)
					{
						enum kobject_action action;
						if (kobject_action_type(buf, count, &action) == 0)
							kobject_uevent(&bus->p->subsys.kobj, action);
						return count;
					}
					static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
				<<<=======
			// create devices folder,  /sys/class/bus/usb/devices ,  kset is just like a folder in sys
			priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);

			// create devices folder,  /sys/class/bus/usb/drivers 
			priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);
	
			INIT_LIST_HEAD(&priv->interfaces);
			__mutex_init(&priv->mutex, "subsys mutex", key);
			klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
			klist_init(&priv->klist_drivers, NULL, NULL);
		
			retval = add_probe_files(bus);
				===========>>>
				+	retval = bus_create_file(bus, &bus_attr_drivers_probe);
				+	retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
				
				//step 1 : driver probe
				// when drivers_probe stored ,store_drivers_probe will be call ,to foreach drvier-device name
				static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
					------------->
						static ssize_t store_drivers_probe(struct bus_type *bus, const char *buf, size_t count)
						{
							dev = bus_find_device_by_name(bus, NULL, buf);  // foreach bus devices by name 
								--------->
									while ((dev = next_device(&i)))
										if (match(dev, data) && get_device(dev))
											break;
								<-----------
							if (bus_rescan_devices_helper(dev, NULL) == 0)
								err = count;
							put_device(dev);
							return err;
						}
					<-------------	

				// file  /sys/class/bus/usb/driver_autoprobe ,  get or set bus->p->drivers_autoprobe = 0 or 1
				static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO, show_drivers_autoprobe, store_drivers_autoprobe);		
					------------->
						static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
						{
							return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
						}
						
						static ssize_t store_drivers_autoprobe(struct bus_type *bus,
										       const char *buf, size_t count)
						{
							if (buf[0] == '0')
								bus->p->drivers_autoprobe = 0;
							else
								bus->p->drivers_autoprobe = 1;
							return count;
						}
					<------------		

				<<<===========
	
			retval = bus_add_groups(bus, bus->bus_groups);
		
			pr_debug("bus: '%s': registered\n", bus->name);
			return 0;
		}
	<<<===================
	<<<===================
	// after all bus_register(), /sys/class/bus/usb/drivers and /sys/class/bus/usb/devices is create. 

	// bus will notifier other , when device_add or device_del
	retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
	
	// create char devc  /dev/usb
	retval = usb_major_init();
		===========>>> 
			error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
			
			static const struct file_operations usb_fops = {
				.owner =	THIS_MODULE,
				.open =		usb_open,
				.llseek =	noop_llseek,
			};

			static int usb_open(struct inode *inode, struct file *file)
			{
				const struct file_operations *new_fops;
		
				new_fops = fops_get(usb_minors[iminor(inode)]);
			
				replace_fops(file, new_fops);
				/* Curiouser and curiouser... NULL ->open() as "no device" ? */
				if (file->f_op->open)
					err = file->f_op->open(inode, file);
			}
		<<<===========

	retval = usb_register(&usbfs_driver);
		============>>>
			struct usb_driver usbfs_driver = {
				.name =		"usbfs",
				.probe =	driver_probe,
				.disconnect =	driver_disconnect,
				.suspend =	driver_suspend,
				.resume =	driver_resume,
			};

			/* use a define to avoid include chaining to get THIS_MODULE & friends */
			#define usb_register(driver) \
				usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

			int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name)
			{
				new_driver->drvwrap.for_devices = 0;
				new_driver->drvwrap.driver.name = new_driver->name;   	// "usbfs"
				new_driver->drvwrap.driver.bus = &usb_bus_type;			// "usb" bus
				new_driver->drvwrap.driver.probe = usb_probe_interface;
					===========>>>
						// step 2: interface probe
						// this will be called ,when insmod a new interface to usb bus
						
						/* called from driver core with dev locked */
						static int usb_probe_interface(struct device *dev)
						{
							struct usb_driver *driver = to_usb_driver(dev->driver);
							struct usb_interface *intf = to_usb_interface(dev);
							struct usb_device *udev = interface_to_usbdev(intf);
						
							intf->needs_binding = 0;
						
							if (usb_device_is_owned(udev))
								return error;
						
							id = usb_match_dynamic_id(intf, driver);
							if (!id)
								id = usb_match_id(intf, driver->id_table);
							if (!id)
								return error;
								==============>>>
									/* returns 0 if no match, 1 if match */
									int usb_match_one_id(struct usb_interface *interface, const struct usb_device_id *id)
									{
										struct usb_host_interface *intf;
										struct usb_device *dev;
									
										/* proc_connectinfo in devio.c may call us with id == NULL. */
										if (id == NULL)
											return 0;
									
										intf = interface->cur_altsetting;
										dev = interface_to_usbdev(interface);
									
										if (!usb_match_device(dev, id))
											return 0;
									
										return usb_match_one_id_intf(dev, intf, id);
									}
								<<<===========
	
							dev_dbg(dev, "%s - got id\n", __func__);
						
							error = usb_autoresume_device(udev);
							intf->condition = USB_INTERFACE_BINDING;
						
							/* Probed interfaces are initially active.  They are
							 * runtime-PM-enabled only if the driver has autosuspend support.
							 * They are sensitive to their children's power states.
							 */
							pm_runtime_set_active(dev);
							pm_suspend_ignore_children(dev, false);
							if (driver->supports_autosuspend)
								pm_runtime_enable(dev);
						
							/* If the new driver doesn't allow hub-initiated LPM, and we can't
							 * disable hub-initiated LPM, then fail the probe.
							 *
							 * Otherwise, leaving LPM enabled should be harmless, because the
							 * endpoint intervals should remain the same, and the U1/U2 timeouts
							 * should remain the same.
							 *
							 * If we need to install alt setting 0 before probe, or another alt
							 * setting during probe, that should also be fine.  usb_set_interface()
							 * will attempt to disable LPM, and fail if it can't disable it.
							 */
							lpm_disable_error = usb_unlocked_disable_lpm(udev);
							if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
								dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
										__func__, driver->name);
								error = lpm_disable_error;
								goto err;
							}
						
							/* Carry out a deferred switch to altsetting 0 */
							if (intf->needs_altsetting0) {
								error = usb_set_interface(udev, intf->altsetting[0].
										desc.bInterfaceNumber, 0);
								if (error < 0)
									goto err;
								intf->needs_altsetting0 = 0;
							}
						
							error = driver->probe(intf, id);  // call   .probe =	driver_probe,
						
							intf->condition = USB_INTERFACE_BOUND;
						
							/* If the LPM disable succeeded, balance the ref counts. */
							if (!lpm_disable_error)
								usb_unlocked_enable_lpm(udev);
						
							usb_autosuspend_device(udev);
							return error;
						}
					<<<===========
					
				new_driver->drvwrap.driver.remove = usb_unbind_interface;
					=============>>>

						// step 3: unbind interface func
						/* called from driver core with dev locked */
						static int usb_unbind_interface(struct device *dev)
						{
							struct usb_driver *driver = to_usb_driver(dev->driver);
							struct usb_interface *intf = to_usb_interface(dev);
							struct usb_host_endpoint *ep, **eps = NULL;
							struct usb_device *udev;
						
							intf->condition = USB_INTERFACE_UNBINDING;
						
							/* Autoresume for set_interface call below */
							udev = interface_to_usbdev(intf);
							error = usb_autoresume_device(udev);
						
							/* Hub-initiated LPM policy may change, so attempt to disable LPM until
							 * the driver is unbound.  If LPM isn't disabled, that's fine because it
							 * wouldn't be enabled unless all the bound interfaces supported
							 * hub-initiated LPM.
							 */
							lpm_disable_error = usb_unlocked_disable_lpm(udev);
						
							/*
							 * Terminate all URBs for this interface unless the driver
							 * supports "soft" unbinding and the device is still present.
							 */
							if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED)
								usb_disable_interface(udev, intf, false);
						
							driver->disconnect(intf);
						
							/* Free streams */
							for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
								ep = &intf->cur_altsetting->endpoint[i];
								if (ep->streams == 0)
									continue;
								if (j == 0) {
									eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
										      GFP_KERNEL);
									if (!eps) {
										dev_warn(dev, "oom, leaking streams\n");
										break;
									}
								}
								eps[j++] = ep;
							}
							if (j) {
								usb_free_streams(intf, eps, j, GFP_KERNEL);
								kfree(eps);
							}
						
							/* Reset other interface state.
							 * We cannot do a Set-Interface if the device is suspended or
							 * if it is prepared for a system sleep (since installing a new
							 * altsetting means creating new endpoint device entries).
							 * When either of these happens, defer the Set-Interface.
							 */
							if (intf->cur_altsetting->desc.bAlternateSetting == 0) {
								/* Already in altsetting 0 so skip Set-Interface.
								 * Just re-enable it without affecting the endpoint toggles.
								 */
								usb_enable_interface(udev, intf, false);
							} else if (!error && !intf->dev.power.is_prepared) {
								r = usb_set_interface(udev, intf->altsetting[0].
										desc.bInterfaceNumber, 0);
								if (r < 0)
									intf->needs_altsetting0 = 1;
							} else {
								intf->needs_altsetting0 = 1;
							}
							usb_set_intfdata(intf, NULL);
						
							intf->condition = USB_INTERFACE_UNBOUND;
							intf->needs_remote_wakeup = 0;
						
							/* Attempt to re-enable USB3 LPM, if the disable succeeded. */
							if (!lpm_disable_error)
								usb_unlocked_enable_lpm(udev);
						
							/* Unbound interfaces are always runtime-PM-disabled and -suspended */
							if (driver->supports_autosuspend)
								pm_runtime_disable(dev);
							pm_runtime_set_suspended(dev);
						
							/* Undo any residual pm_autopm_get_interface_* calls */
							for (r = atomic_read(&intf->pm_usage_cnt); r > 0; --r)
								usb_autopm_put_interface_no_suspend(intf);
							atomic_set(&intf->pm_usage_cnt, 0);
						
							if (!error)
								usb_autosuspend_device(udev);
						
							return 0;
						}
					<<<=============
				new_driver->drvwrap.driver.owner = owner;				// THIS_MODULE
				new_driver->drvwrap.driver.mod_name = mod_name;			// KBUILD_MODNAME
				spin_lock_init(&new_driver->dynids.lock);
				INIT_LIST_HEAD(&new_driver->dynids.list);
			
				retval = driver_register(&new_driver->drvwrap.driver);
				=======================>>>
				int driver_register(struct device_driver *drv)
				{
					BUG_ON(!drv->bus->p);
				
					if ((drv->bus->probe && drv->probe) ||
					    (drv->bus->remove && drv->remove) ||
					    (drv->bus->shutdown && drv->shutdown))
						printk(KERN_WARNING "Driver '%s' needs updating - please use "
							"bus_type methods\n", drv->name);
				
					other = driver_find(drv->name, drv->bus);
						====================>>>
							struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
							struct driver_private *priv;
							
							if (k) {
								/* Drop reference added by kset_find_obj() */
								kobject_put(k);
								priv = to_driver(k);
								return priv->driver;  //if find success ,then return the object
							}
						<<<====================
				
					ret = bus_add_driver(drv);
						====================>>>
							/**
							 * bus_add_driver - Add a driver to the bus.
							 * @drv: driver.
							 */
							int bus_add_driver(struct device_driver *drv)
							{
								bus = bus_get(drv->bus);
								pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
							
								priv = kzalloc(sizeof(*priv), GFP_KERNEL);
								klist_init(&priv->klist_devices, NULL, NULL);
								priv->driver = drv;
								drv->p = priv;
								priv->kobj.kset = bus->p->drivers_kset;
								error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);
							
								klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
								if (drv->bus->p->drivers_autoprobe) {
									if (driver_allows_async_probing(drv)) {
										pr_debug("bus: '%s': probing driver %s asynchronously\n",
											drv->bus->name, drv->name);
										async_schedule(driver_attach_async, drv);
											======================>>>
												//trigger to bind driver to deices
												ret = driver_attach(drv);
											<<<======================
									} else {
										error = driver_attach(drv);
										if (error)
											goto out_unregister;
									}
								}
								module_add_driver(drv->owner, drv);
							
								error = driver_create_file(drv, &driver_attr_uevent);
								if (error) {
									printk(KERN_ERR "%s: uevent attr (%s) failed\n",
										__func__, drv->name);
								}
								error = driver_add_groups(drv, bus->drv_groups);
								if (error) {
									/* How the hell do we get out of this pickle? Give up */
									printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
										__func__, drv->name);
								}
							
								if (!drv->suppress_bind_attrs) {
									error = add_bind_files(drv);
									if (error) {
										/* Ditto */
										printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
											__func__, drv->name);
									}
								}
							
								return 0;		
						<<<====================
					
					ret = driver_add_groups(drv, drv->groups);
					if (ret) {
						bus_remove_driver(drv);
						return ret;
					}
					kobject_uevent(&drv->p->kobj, KOBJ_ADD);
				
					return ret;
				}
				EXPORT_SYMBOL_GPL(driver_register);
				<<<=======================

				retval = usb_create_newid_files(new_driver);



				pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name);
			
			out:
				return retval;	



		<<<============
	retval = usb_devio_init();
		============>>>
			int __init usb_devio_init(void)
			{
				retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX, "usb_device");
				if (retval) {
					printk(KERN_ERR "Unable to register minors for usb_device\n");
					goto out;
				}
				cdev_init(&usb_device_cdev, &usbdev_file_operations);
				retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
				if (retval) {
					printk(KERN_ERR "Unable to get usb_device major %d\n",  USB_DEVICE_MAJOR);
					goto error_cdev;
				}
				usb_register_notify(&usbdev_nb);
				return retval;
		<<<============
	
	retval = usb_hub_init();
		=============>>>
			// \kernel\msm-3.18\drivers\usb\core\hub.c
			static const struct usb_device_id hub_id_table[] = {
			    { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, 	// 0x0001 | 0x0080
			      .idVendor = USB_VENDOR_GENESYS_LOGIC,				// 0x05e3
			      .bInterfaceClass = USB_CLASS_HUB,					// 9
			      .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},	// 0x01
			    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,		// 0x0010
			      .bDeviceClass = USB_CLASS_HUB},					// 9
			    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,		// 0x0080
			      .bInterfaceClass = USB_CLASS_HUB},				// 9
			    { }						/* Terminating entry */
			};
						
			int usb_hub_init(void)
			{
				if (usb_register(&hub_driver) < 0) {
					printk(KERN_ERR "%s: can't register hub driver\n", usbcore_name);
					return -1;
				}
				/*
				 * The workqueue needs to be freezable to avoid interfering with
				 * USB-PERSIST port handover. Otherwise it might see that a full-speed
				 * device was gone before the EHCI controller had handed its port
				 * over to the companion full-speed controller.
				 */
				hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
				if (hub_wq)
					return 0;
			}
			// \kernel\msm-3.18\drivers\usb\core\hub.c
			static struct usb_driver hub_driver = {
				.name =		"hub",
				.probe =	hub_probe,
				.disconnect =	hub_disconnect,
				.suspend =	hub_suspend,
				.resume =	hub_resume,
				.reset_resume =	hub_reset_resume,
				.pre_reset =	hub_pre_reset,
				.post_reset =	hub_post_reset,
				.unlocked_ioctl = hub_ioctl,
				.id_table =	hub_id_table,
				.supports_autosuspend =	1,
			};
			
			// step 4: hub_probe
			static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
			{
				struct usb_host_interface *desc;
				struct usb_endpoint_descriptor *endpoint;
				struct usb_device *hdev;
				struct usb_hub *hub;
			
				desc = intf->cur_altsetting;
				hdev = interface_to_usbdev(intf);
			
				/*
				 * Set default autosuspend delay as 0 to speedup bus suspend,
				 * based on the below considerations:
				 *
				 * - Unlike other drivers, the hub driver does not rely on the
				 *   autosuspend delay to provide enough time to handle a wakeup
				 *   event, and the submitted status URB is just to check future
				 *   change on hub downstream ports, so it is safe to do it.
				 *
				 * - The patch might cause one or more auto supend/resume for
				 *   below very rare devices when they are plugged into hub
				 *   first time:
				 *
				 *   	devices having trouble initializing, and disconnect
				 *   	themselves from the bus and then reconnect a second
				 *   	or so later
				 *
				 *   	devices just for downloading firmware, and disconnects
				 *   	themselves after completing it
				 *
				 *   For these quite rare devices, their drivers may change the
				 *   autosuspend delay of their parent hub in the probe() to one
				 *   appropriate value to avoid the subtle problem if someone
				 *   does care it.
				 *
				 * - The patch may cause one or more auto suspend/resume on
				 *   hub during running 'lsusb', but it is probably too
				 *   infrequent to worry about.
				 *
				 * - Change autosuspend delay of hub can avoid unnecessary auto
				 *   suspend timer for hub, also may decrease power consumption
				 *   of USB bus.
				 *
				 * - If user has indicated to prevent autosuspend by passing
				 *   usbcore.autosuspend = -1 then keep autosuspend disabled.
				 */
			#ifdef CONFIG_PM_RUNTIME
				if (hdev->dev.power.autosuspend_delay >= 0)
					pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
			#endif
			
				/*
				 * Hubs have proper suspend/resume support, except for root hubs
				 * where the controller driver doesn't have bus_suspend and
				 * bus_resume methods.
				 */
				if (hdev->parent) {		/* normal device */
					usb_enable_autosuspend(hdev);
				} else {			/* root hub */
					const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
			
					if (drv->bus_suspend && drv->bus_resume)
						usb_enable_autosuspend(hdev);
				}
			
				if (hdev->level == MAX_TOPO_LEVEL) {
					dev_err(&intf->dev,
						"Unsupported bus topology: hub nested too deep\n");
					return -E2BIG;
				}
			
			#ifdef	CONFIG_USB_OTG_BLACKLIST_HUB
				if (hdev->parent) {
					dev_warn(&intf->dev, "ignoring external hub\n");
					return -ENODEV;
				}
			#endif
			
				/* Some hubs have a subclass of 1, which AFAICT according to the */
				/*  specs is not defined, but it works */
				if ((desc->desc.bInterfaceSubClass != 0) &&
				    (desc->desc.bInterfaceSubClass != 1)) {
			descriptor_error:
					dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
					return -EIO;
				}
			
				/* Multiple endpoints? What kind of mutant ninja-hub is this? */
				if (desc->desc.bNumEndpoints != 1)
					goto descriptor_error;
			
				endpoint = &desc->endpoint[0].desc;
			
				/* If it's not an interrupt in endpoint, we'd better punt! */
				if (!usb_endpoint_is_int_in(endpoint))
					goto descriptor_error;
			
				/* We found a hub */
				dev_info (&intf->dev, "USB hub found\n");
			
				hub = kzalloc(sizeof(*hub), GFP_KERNEL);
				if (!hub) {
					dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
					return -ENOMEM;
				}
			
				kref_init(&hub->kref);
				hub->intfdev = &intf->dev;
				hub->hdev = hdev;
				INIT_DELAYED_WORK(&hub->leds, led_work);
				INIT_DELAYED_WORK(&hub->init_work, NULL);
				INIT_WORK(&hub->events, hub_event);   // it is important
				usb_get_intf(intf);
				usb_get_dev(hdev);
			
				usb_set_intfdata (intf, hub);
				intf->needs_remote_wakeup = 1;
				pm_suspend_ignore_children(&intf->dev, true);
			
				if (hdev->speed == USB_SPEED_HIGH)
					highspeed_hubs++;
			
				if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
					hub->quirk_check_port_auto_suspend = 1;
			
				if (hub_configure(hub, endpoint) >= 0)
					return 0;
			
				hub_disconnect (intf);
				return -ENODEV;
			}

			// waitting update
			static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
			{
				struct usb_hcd *hcd;
				struct usb_device *hdev = hub->hdev;
				struct device *hub_dev = hub->intfdev;
				u16 hubstatus, hubchange;
				u16 wHubCharacteristics;
				unsigned int pipe;
				int maxp, ret, i;
				char *message = "out of memory";
				unsigned unit_load;
				unsigned full_load;
				unsigned maxchild;
			
				hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
				hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
				mutex_init(&hub->status_mutex);
			
				hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
			
				/* Request the entire hub descriptor.
				 * hub->descriptor can handle USB_MAXCHILDREN ports,
				 * but the hub can/will return fewer bytes here.
				 */
				ret = get_hub_descriptor(hdev, hub->descriptor);
				if (ret < 0) {
					message = "can't read hub descriptor";
					goto fail;
				} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {  // maximum with 31 ports per port
					message = "hub has too many ports!";
					ret = -ENODEV;
					goto fail;
				} else if (hub->descriptor->bNbrPorts == 0) {  
					message = "hub doesn't have any ports!";
					ret = -ENODEV;
					goto fail;
				}
			
				maxchild = hub->descriptor->bNbrPorts;
				dev_info(hub_dev, "%d port%s detected\n", maxchild,
						(maxchild == 1) ? "" : "s");
			
				hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
				if (!hub->ports) {
					ret = -ENOMEM;
					goto fail;
				}
			
				wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
				if (hub_is_superspeed(hdev)) {
					unit_load = 150;
					full_load = 900;
				} else {
					unit_load = 100;
					full_load = 500;
				}
			
				/* FIXME for USB 3.0, skip for now */
				if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
						!(hub_is_superspeed(hdev))) {
					int	i;
					char	portstr[USB_MAXCHILDREN + 1];
			
					for (i = 0; i < maxchild; i++)
						portstr[i] = hub->descriptor->u.hs.DeviceRemovable
							    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
							? 'F' : 'R';
					portstr[maxchild] = 0;
					dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
				} else
					dev_dbg(hub_dev, "standalone hub\n");
			
				switch (wHubCharacteristics & HUB_CHAR_LPSM) {
				case HUB_CHAR_COMMON_LPSM:
					dev_dbg(hub_dev, "ganged power switching\n");
					break;
				case HUB_CHAR_INDV_PORT_LPSM:
					dev_dbg(hub_dev, "individual port power switching\n");
					break;
				case HUB_CHAR_NO_LPSM:
				case HUB_CHAR_LPSM:
					dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
					break;
				}
			
				switch (wHubCharacteristics & HUB_CHAR_OCPM) {
				case HUB_CHAR_COMMON_OCPM:
					dev_dbg(hub_dev, "global over-current protection\n");
					break;
				case HUB_CHAR_INDV_PORT_OCPM:
					dev_dbg(hub_dev, "individual port over-current protection\n");
					break;
				case HUB_CHAR_NO_OCPM:
				case HUB_CHAR_OCPM:
					dev_dbg(hub_dev, "no over-current protection\n");
					break;
				}
			
				spin_lock_init (&hub->tt.lock);
				INIT_LIST_HEAD (&hub->tt.clear_list);
				INIT_WORK(&hub->tt.clear_work, hub_tt_work);
				switch (hdev->descriptor.bDeviceProtocol) {
				case USB_HUB_PR_FS:
					break;
				case USB_HUB_PR_HS_SINGLE_TT:
					dev_dbg(hub_dev, "Single TT\n");
					hub->tt.hub = hdev;
					break;
				case USB_HUB_PR_HS_MULTI_TT:
					ret = usb_set_interface(hdev, 0, 1);
					if (ret == 0) {
						dev_dbg(hub_dev, "TT per port\n");
						hub->tt.multi = 1;
					} else
						dev_err(hub_dev, "Using single TT (err %d)\n",
							ret);
					hub->tt.hub = hdev;
					break;
				case USB_HUB_PR_SS:
					/* USB 3.0 hubs don't have a TT */
					break;
				default:
					dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
						hdev->descriptor.bDeviceProtocol);
					break;
				}
			
				/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
				switch (wHubCharacteristics & HUB_CHAR_TTTT) {
				case HUB_TTTT_8_BITS:
					if (hdev->descriptor.bDeviceProtocol != 0) {
						hub->tt.think_time = 666;
						dev_dbg(hub_dev, "TT requires at most %d "
								"FS bit times (%d ns)\n",
							8, hub->tt.think_time);
					}
					break;
				case HUB_TTTT_16_BITS:
					hub->tt.think_time = 666 * 2;
					dev_dbg(hub_dev, "TT requires at most %d "
							"FS bit times (%d ns)\n",
						16, hub->tt.think_time);
					break;
				case HUB_TTTT_24_BITS:
					hub->tt.think_time = 666 * 3;
					dev_dbg(hub_dev, "TT requires at most %d "
							"FS bit times (%d ns)\n",
						24, hub->tt.think_time);
					break;
				case HUB_TTTT_32_BITS:
					hub->tt.think_time = 666 * 4;
					dev_dbg(hub_dev, "TT requires at most %d "
							"FS bit times (%d ns)\n",
						32, hub->tt.think_time);
					break;
				}
			
				/* probe() zeroes hub->indicator[] */
				if (wHubCharacteristics & HUB_CHAR_PORTIND) {
					hub->has_indicators = 1;
					dev_dbg(hub_dev, "Port indicators are supported\n");
				}
			
				dev_dbg(hub_dev, "power on to power good time: %dms\n",
					hub->descriptor->bPwrOn2PwrGood * 2);
			
				/* power budgeting mostly matters with bus-powered hubs,
				 * and battery-powered root hubs (may provide just 8 mA).
				 */
				ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
				if (ret) {
					message = "can't get hub status";
					goto fail;
				}
				hcd = bus_to_hcd(hdev->bus);
				if (hdev == hdev->bus->root_hub) {
					if (hcd->power_budget > 0)
						hdev->bus_mA = hcd->power_budget;
					else
						hdev->bus_mA = full_load * maxchild;
					if (hdev->bus_mA >= full_load)
						hub->mA_per_port = full_load;
					else {
						hub->mA_per_port = hdev->bus_mA;
						hub->limited_power = 1;
					}
				} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
					int remaining = hdev->bus_mA -
						hub->descriptor->bHubContrCurrent;
			
					dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
						hub->descriptor->bHubContrCurrent);
					hub->limited_power = 1;
			
					if (remaining < maxchild * unit_load)
						dev_warn(hub_dev,
								"insufficient power available "
								"to use all downstream ports\n");
					hub->mA_per_port = unit_load;	/* 7.2.1 */
			
				} else {	/* Self-powered external hub */
					/* FIXME: What about battery-powered external hubs that
					 * provide less current per port? */
					hub->mA_per_port = full_load;
				}
				if (hub->mA_per_port < full_load)
					dev_dbg(hub_dev, "%umA bus power budget for each child\n",
							hub->mA_per_port);
			
				ret = hub_hub_status(hub, &hubstatus, &hubchange);
				if (ret < 0) {
					message = "can't get hub status";
					goto fail;
				}
			
				/* local power status reports aren't always correct */
				if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)
					dev_dbg(hub_dev, "local power source is %s\n",
						(hubstatus & HUB_STATUS_LOCAL_POWER)
						? "lost (inactive)" : "good");
			
				if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
					dev_dbg(hub_dev, "%sover-current condition exists\n",
						(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
			
				/* set up the interrupt endpoint
				 * We use the EP's maxpacket size instead of (PORTS+1+7)/8
				 * bytes as USB2.0[11.12.3] says because some hubs are known
				 * to send more data (and thus cause overflow). For root hubs,
				 * maxpktsize is defined in hcd.c's fake endpoint descriptors
				 * to be big enough for at least USB_MAXCHILDREN ports. */
				pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
				maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
			
				if (maxp > sizeof(*hub->buffer))
					maxp = sizeof(*hub->buffer);
			
				hub->urb = usb_alloc_urb(0, GFP_KERNEL);
				if (!hub->urb) {
					ret = -ENOMEM;
					goto fail;
				}
			
				usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
					hub, endpoint->bInterval);
			
				/* maybe cycle the hub leds */
				if (hub->has_indicators && blinkenlights)
					hub->indicator[0] = INDICATOR_CYCLE;
			
				mutex_lock(&usb_port_peer_mutex);
				for (i = 0; i < maxchild; i++) {
					ret = usb_hub_create_port_device(hub, i + 1);
					if (ret < 0) {
						dev_err(hub->intfdev,
							"couldn't create port%d device.\n", i + 1);
						break;
					}
				}
				hdev->maxchild = i;
				for (i = 0; i < hdev->maxchild; i++) {
					struct usb_port *port_dev = hub->ports[i];
			
					pm_runtime_put(&port_dev->dev);
				}
			
				mutex_unlock(&usb_port_peer_mutex);
				if (ret < 0)
					goto fail;
			
				/* Update the HCD's internal representation of this hub before hub_wq
				 * starts getting port status changes for devices under the hub.
				 */
				if (hcd->driver->update_hub_device) {
					ret = hcd->driver->update_hub_device(hcd, hdev,
							&hub->tt, GFP_KERNEL);
					if (ret < 0) {
						message = "can't update HCD hub info";
						goto fail;
					}
				}
			
				usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
			
				hub_activate(hub, HUB_INIT);     // the most important function
				return 0;
			
			fail:
				dev_err (hub_dev, "config failed, %s (err %d)\n",
						message, ret);
				/* hub_disconnect() frees urb and descriptor */
				return ret;
			}


			static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
			{
				struct usb_device *hdev = hub->hdev;
				struct usb_hcd *hcd;
				int ret;
				int port1;
				int status;
				bool need_debounce_delay = false;
				unsigned delay;
			
				/* Continue a partial initialization */
				if (type == HUB_INIT2 || type == HUB_INIT3) {
					device_lock(hub->intfdev);
			
					/* Was the hub disconnected while we were waiting? */
					if (hub->disconnected) {
						device_unlock(hub->intfdev);
						kref_put(&hub->kref, hub_release);
						return;
					}
					if (type == HUB_INIT2)
						goto init2;
					goto init3;
				}
				kref_get(&hub->kref);
			
				/* The superspeed hub except for root hub has to use Hub Depth
				 * value as an offset into the route string to locate the bits
				 * it uses to determine the downstream port number. So hub driver
				 * should send a set hub depth request to superspeed hub after
				 * the superspeed hub is set configuration in initialization or
				 * reset procedure.
				 *
				 * After a resume, port power should still be on.
				 * For any other type of activation, turn it on.
				 */
				if (type != HUB_RESUME) {
					if (hdev->parent && hub_is_superspeed(hdev)) {
						ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
								HUB_SET_DEPTH, USB_RT_HUB,
								hdev->level - 1, 0, NULL, 0,
								USB_CTRL_SET_TIMEOUT);
						if (ret < 0)
							dev_err(hub->intfdev,
									"set hub depth failed\n");
					}
			
					/* Speed up system boot by using a delayed_work for the
					 * hub's initial power-up delays.  This is pretty awkward
					 * and the implementation looks like a home-brewed sort of
					 * setjmp/longjmp, but it saves at least 100 ms for each
					 * root hub (assuming usbcore is compiled into the kernel
					 * rather than as a module).  It adds up.
					 *
					 * This can't be done for HUB_RESUME or HUB_RESET_RESUME
					 * because for those activation types the ports have to be
					 * operational when we return.  In theory this could be done
					 * for HUB_POST_RESET, but it's easier not to.
					 */
					if (type == HUB_INIT) {
						unsigned delay = hub_power_on_good_delay(hub);
			
						hub_power_on(hub, false);    // power step 1 
						INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
						queue_delayed_work(system_power_efficient_wq,
								&hub->init_work,
								msecs_to_jiffies(delay));
			
						/* Suppress autosuspend until init is done */
						usb_autopm_get_interface_no_resume(
								to_usb_interface(hub->intfdev));
						return;		/* Continues at init2: below */
					} else if (type == HUB_RESET_RESUME) {
						/* The internal host controller state for the hub device
						 * may be gone after a host power loss on system resume.
						 * Update the device's info so the HW knows it's a hub.
						 */
						hcd = bus_to_hcd(hdev->bus);
						if (hcd->driver->update_hub_device) {
							ret = hcd->driver->update_hub_device(hcd, hdev,
									&hub->tt, GFP_NOIO);
							if (ret < 0) {
								dev_err(hub->intfdev, "Host not "
										"accepting hub info "
										"update.\n");
								dev_err(hub->intfdev, "LS/FS devices "
										"and hubs may not work "
										"under this hub\n.");
							}
						}
						hub_power_on(hub, true);
					} else {
						hub_power_on(hub, true);
					}
				}
			 init2:
			
				/*
				 * Check each port and set hub->change_bits to let hub_wq know
				 * which ports need attention.
				 */
				for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
					struct usb_port *port_dev = hub->ports[port1 - 1];
					struct usb_device *udev = port_dev->child;
					u16 portstatus, portchange;
			
					portstatus = portchange = 0;
					status = hub_port_status(hub, port1, &portstatus, &portchange);
					if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
						dev_dbg(&port_dev->dev, "status %04x change %04x\n",
								portstatus, portchange);
			
					/*
					 * After anything other than HUB_RESUME (i.e., initialization
					 * or any sort of reset), every port should be disabled.
					 * Unconnected ports should likewise be disabled (paranoia),
					 * and so should ports for which we have no usb_device.
					 */
					if ((portstatus & USB_PORT_STAT_ENABLE) && (
							type != HUB_RESUME ||
							!(portstatus & USB_PORT_STAT_CONNECTION) ||
							!udev ||
							udev->state == USB_STATE_NOTATTACHED)) {
						/*
						 * USB3 protocol ports will automatically transition
						 * to Enabled state when detect an USB3.0 device attach.
						 * Do not disable USB3 protocol ports, just pretend
						 * power was lost
						 */
						portstatus &= ~USB_PORT_STAT_ENABLE;
						if (!hub_is_superspeed(hdev))
							usb_clear_port_feature(hdev, port1,
									   USB_PORT_FEAT_ENABLE);
					}
			
					/* Clear status-change flags; we'll debounce later */
					if (portchange & USB_PORT_STAT_C_CONNECTION) {
						need_debounce_delay = true;
						usb_clear_port_feature(hub->hdev, port1,
								USB_PORT_FEAT_C_CONNECTION);
					}
					if (portchange & USB_PORT_STAT_C_ENABLE) {
						need_debounce_delay = true;
						usb_clear_port_feature(hub->hdev, port1,
								USB_PORT_FEAT_C_ENABLE);
					}
					if (portchange & USB_PORT_STAT_C_RESET) {
						need_debounce_delay = true;
						usb_clear_port_feature(hub->hdev, port1,
								USB_PORT_FEAT_C_RESET);
					}
					if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
							hub_is_superspeed(hub->hdev)) {
						need_debounce_delay = true;
						usb_clear_port_feature(hub->hdev, port1,
								USB_PORT_FEAT_C_BH_PORT_RESET);
					}
					/* We can forget about a "removed" device when there's a
					 * physical disconnect or the connect status changes.
					 */
					if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
							(portchange & USB_PORT_STAT_C_CONNECTION))
						clear_bit(port1, hub->removed_bits);
			
					if (!udev || udev->state == USB_STATE_NOTATTACHED) {
						/* Tell hub_wq to disconnect the device or
						 * check for a new connection
						 */
						if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
						    (portstatus & USB_PORT_STAT_OVERCURRENT))
							set_bit(port1, hub->change_bits);
			
					} else if (portstatus & USB_PORT_STAT_ENABLE) {
						bool port_resumed = (portstatus &
								USB_PORT_STAT_LINK_STATE) ==
							USB_SS_PORT_LS_U0;
						/* The power session apparently survived the resume.
						 * If there was an overcurrent or suspend change
						 * (i.e., remote wakeup request), have hub_wq
						 * take care of it.  Look at the port link state
						 * for USB 3.0 hubs, since they don't have a suspend
						 * change bit, and they don't set the port link change
						 * bit on device-initiated resume.
						 */
						if (portchange || (hub_is_superspeed(hub->hdev) &&
									port_resumed))
							set_bit(port1, hub->change_bits);
			
					} else if (udev->persist_enabled) {
			#ifdef CONFIG_PM
						udev->reset_resume = 1;
			#endif
						/* Don't set the change_bits when the device
						 * was powered off.
						 */
						if (test_bit(port1, hub->power_bits))
							set_bit(port1, hub->change_bits);
			
					} else {
						/* The power session is gone; tell hub_wq */
						usb_set_device_state(udev, USB_STATE_NOTATTACHED);
						set_bit(port1, hub->change_bits);
					}
				}
			
				/* If no port-status-change flags were set, we don't need any
				 * debouncing.  If flags were set we can try to debounce the
				 * ports all at once right now, instead of letting hub_wq do them
				 * one at a time later on.
				 *
				 * If any port-status changes do occur during this delay, hub_wq
				 * will see them later and handle them normally.
				 */
				if (need_debounce_delay) {
					delay = HUB_DEBOUNCE_STABLE;
			
					/* Don't do a long sleep inside a workqueue routine */
					if (type == HUB_INIT2) {
						INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
						queue_delayed_work(system_power_efficient_wq,
								&hub->init_work,
								msecs_to_jiffies(delay));
						device_unlock(hub->intfdev);
						return;		/* Continues at init3: below */
					} else {
						msleep(delay);
					}
				}
			 init3:
				hub->quiescing = 0;
			
				status = usb_submit_urb(hub->urb, GFP_NOIO);
				if (status < 0)
					dev_err(hub->intfdev, "activate --> %d\n", status);
				if (hub->has_indicators && blinkenlights)
					queue_delayed_work(system_power_efficient_wq,
							&hub->leds, LED_CYCLE_PERIOD);
			
				/* Scan all ports that need attention */
				kick_hub_wq(hub);
			
				/* Allow autosuspend if it was suppressed */
				if (type <= HUB_INIT3)
					usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
			
				if (type == HUB_INIT2 || type == HUB_INIT3)
					device_unlock(hub->intfdev);
			
				kref_put(&hub->kref, hub_release);
			}
		}
	









			// usb hub event 
			// this function will autosupend until the usb event is proceed.
		

			static void kick_hub_wq(struct usb_hub *hub)
			{
				struct usb_interface *intf;
			
				if (hub->disconnected || work_pending(&hub->events))
					return;
			
				/*
				 * Suppress autosuspend until the event is proceed.
				 *
				 * Be careful and make sure that the symmetric operation is
				 * always called. We are here only when there is no pending
				 * work for this hub. Therefore put the interface either when
				 * the new work is called or when it is canceled.
				 */
				intf = to_usb_interface(hub->intfdev);   //get ups interface
				usb_autopm_get_interface_no_resume(intf);
				kref_get(&hub->kref);
			
				if (queue_work(hub_wq, &hub->events))  //call hubevent
					return;
			
				/* the work has already been scheduled */
				usb_autopm_put_interface_async(intf);
				kref_put(&hub->kref, hub_release);
			}




		<<<=============

	
	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
		===============>>>
			struct usb_device_driver usb_generic_driver = {
				.name =	"usb",
				.probe = generic_probe,
				.disconnect = generic_disconnect,
			#ifdef	CONFIG_PM
				.suspend = generic_suspend,
				.resume = generic_resume,
			#endif
				.supports_autosuspend = 1,
			};

			// \kernel\msm-3.18\drivers\usb\core\driver.c
			int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner)
			{
				new_udriver->drvwrap.for_devices = 1;
				new_udriver->drvwrap.driver.name = new_udriver->name;  // usb
				new_udriver->drvwrap.driver.bus = &usb_bus_type;
				new_udriver->drvwrap.driver.probe = usb_probe_device;
					===========>>>
						/* called from driver core with dev locked */
						static int usb_probe_device(struct device *dev)
						{
							struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
							struct usb_device *udev = to_usb_device(dev);
						
							dev_dbg(dev, "%s\n", __func__);
						
							/* TODO: Add real matching code */
							/* The device should always appear to be in use
							 * unless the driver supports autosuspend.
							 */
							if (!udriver->supports_autosuspend)
								error = usb_autoresume_device(udev);
							error = udriver->probe(udev);
								===========>>>
									static int generic_probe(struct usb_device *udev)
									{
										/* Choose and set the configuration.  This registers the interfaces
										 * with the driver core and lets interface drivers bind to them. */
										c = usb_choose_configuration(udev);
										err = usb_set_configuration(udev, c);
										
										/* USB device state == configured ... usable */
										usb_notify_add_device(udev);
										return 0;
									}
								<<<==========
							return error;
						}
					<<<==========
				new_udriver->drvwrap.driver.remove = usb_unbind_device;
					============>>>
						/* called from driver core with dev locked */
						static int usb_unbind_device(struct device *dev)
						{
							struct usb_device *udev = to_usb_device(dev);
							struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
						
							udriver->disconnect(udev);
							if (!udriver->supports_autosuspend)
								usb_autosuspend_device(udev);
							return 0;
						}
					<<<===========
				new_udriver->drvwrap.driver.owner = owner;
			
				retval = driver_register(&new_udriver->drvwrap.driver);
			
				pr_info("%s: registered new device driver %s\n", usbcore_name, new_udriver->name);
			}

		<<<==============



	usb_hub_cleanup();
}


**待完 1.1 nousb

// 如果代码中不需要支持USB模块,
	// 则可在 kernel command line 中传入 "nousb" 或者 "usbcore.nousb"
	=========>>>
		/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
		#ifdef MODULE
		module_param(nousb, bool, 0444);
		#else
		core_param(nousb, nousb, bool, 0444);
		#endif
	<<<=========


**待完 1.2 usb_debugfs_init()

static int usb_debugfs_init(void)
{
	usb_debug_root = debugfs_create_dir("usb", NULL);
	if (!usb_debug_root)
		return -ENOENT;

	usb_debug_devices = debugfs_create_file("devices", 0444,
						usb_debug_root, NULL,
						&usbfs_devices_fops);
	if (!usb_debug_devices) {
		debugfs_remove(usb_debug_root);
		usb_debug_root = NULL;
		return -ENOENT;
	}

	return 0;
}


**待完 1.2 usb_debugfs_init()





本文学自:
一定要让你彻底明白什么是USB子系统
mtk-usb代码分析之枚举过程
mtk-usb代码分析之usb gadget
usb热插拔实现机制

Linux USB - Uevent

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"小夜猫&小懒虫&小财迷"的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值