USB OTG(Host) 、 USB ADB(Device)、DWC3 Charge

USB ADB: Corresponding to USB Device Mode
USB OTG: Corresponding to USB Host Mode
USB EAP: Corresponding to USB Network Mode



一、USB OTG / USB Host

Related keyword to search article : USB dwc3,dwc2

enable USB OTG in .dts file:

&usbotg1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_usbotg1>;
	srp-disable;
	hnp-disable;
	adp-disable;
	power-polarity-active-high;
	disable-over-current;
	dr_mode = "otg";
	status = "okay";
};


// kernel\msm-3.18\Documentation\devicetree\bindings\usb\generic.txt
Generic USB Properties
Optional properties:
 - maximum-speed: tells USB controllers we want to work up to a certain speed. 
 			Valid arguments are "super-speed", "high-speed","full-speed" and "low-speed". 
 			In case this isn't passed via DT, USB controllers should default to their maximum HW capability.
 - dr_mode: tells Dual-Role USB controllers that we want to work on a particular mode. Valid arguments are "host",
			"peripheral" and "otg". In case this attribute isn't passed via DT, USB DRD controllers should default to OTG.
			
This is an attribute to a USB controller such as:
dwc3@4a030000 { compatible = "synopsys,dwc3";
	reg = <0x4a030000 0xcfff>;
	interrupts = <0 92 4>
	usb-phy = <&usb2_phy>, <&usb3,phy>;
	maximum-speed = "super-speed";
	dr_mode = "otg"; }; 



//\kernel\msm-3.18\drivers\usb\core\hub.c
--0-->static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
	--1-->static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)
		--2-->static void hub_irq(struct urb *urb)
		--2-->static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
	
			--3-->static void kick_hub_wq(struct usb_hub *hub)
			--3-->queue_work(hub_wq, &hub->events)
					--4-->INIT_WORK(&hub->events, hub_event);
					--4-->static void hub_event(struct work_struct *work)
						--5-->static void port_event(struct usb_hub *hub, int port1)__must_hold(&port_dev->status_lock)
							--6-->static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)__must_hold(&port_dev->status_lock)
								--7-->static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,u16 portchange)
									--8-->int usb_new_device(struct usb_device *udev)
										--9-->static int usb_enumerate_device(struct usb_device *udev)
												--10--> static int usb_enumerate_device_otg(struct usb_device *udev)
	

1.0 hub_probe

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	desc = intf->cur_altsetting;
	hdev = interface_to_usbdev(intf);
	
	/*
	 * 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);
	}
	endpoint = &desc->endpoint[0].desc;
	/* We found a hub */
	dev_info (&intf->dev, "USB hub found\n");
	INIT_DELAYED_WORK(&hub->leds, led_work);
	INIT_DELAYED_WORK(&hub->init_work, NULL);
	INIT_WORK(&hub->events, hub_event);
	if (hub_configure(hub, endpoint) >= 0)
		return 0;
}

1.1 hub_configure

static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
{
	hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
	hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
	hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
	ret = get_hub_descriptor(hdev, hub->descriptor);
	hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
	
	INIT_WORK(&hub->tt.clear_work, hub_tt_work);
	ret = hub_hub_status(hub, &hubstatus, &hubchange);

	hub->urb = usb_alloc_urb(0, GFP_KERNEL);
	usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
	
	usb_hub_adjust_deviceremovable(hdev, hub->descriptor);

	hub_activate(hub, HUB_INIT);
}

1.2 hub_irq

/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
	bits = 0;
	for (i = 0; i < urb->actual_length; ++i)
		bits |= ((unsigned long) ((*hub->buffer)[i]))<< (i*8);
		
	hub->event_bits[0] = bits;
	/* Something happened, let hub_wq figure it out */
	kick_hub_wq(hub);
}

1.3 hub_activate

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
HUB_INIT:
	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 (type == HUB_INIT) {
			hub_power_on(hub, false); // 上电, 不需要延时
			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:
	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 */
	}

 init3:
	hub->quiescing = 0;
	status = usb_submit_urb(hub->urb, GFP_NOIO);
	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);
}

1.4 kick_hub_wq

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);
	usb_autopm_get_interface_no_resume(intf);
	kref_get(&hub->kref);

	if (queue_work(hub_wq, &hub->events))
		return;

	/* the work has already been scheduled */
	usb_autopm_put_interface_async(intf);
	kref_put(&hub->kref, hub_release);
}

1.5 hub_event

static void hub_event(struct work_struct *work)
{
	hub = container_of(work, struct usb_hub, events);
	hdev = hub->hdev;
	hub_dev = hub->intfdev;
	intf = to_usb_interface(hub_dev);

	/* Autoresume */
	ret = usb_autopm_get_interface(intf);

	/* deal with port status changes */
	for (i = 1; i <= hdev->maxchild; i++) {
		struct usb_port *port_dev = hub->ports[i - 1];

		if (test_bit(i, hub->event_bits)|| test_bit(i, hub->change_bits) || test_bit(i, hub->wakeup_bits)) {
			/*
			 * The get_noresume and barrier ensure that if the port was in the process of resuming, we
			 * flush that work and keep the port active for the duration of the port_event().  However,
			 * if the port is runtime pm suspended (powered-off), we leave it in that state, run
			 * an abbreviated port_event(), and move on. */
			pm_runtime_get_noresume(&port_dev->dev);
			pm_runtime_barrier(&port_dev->dev);
			usb_lock_port(port_dev);
			port_event(hub, i);
			usb_unlock_port(port_dev);
			pm_runtime_put_sync(&port_dev->dev);
		}
	}
}

1.6 port_event

static void port_event(struct usb_hub *hub, int port1) __must_hold(&port_dev->status_lock)
{
	connect_change = test_bit(port1, hub->change_bits);

	if (portchange & USB_PORT_STAT_C_CONNECTION) {
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
		connect_change = 1;
	}
	if (portchange & USB_PORT_STAT_C_ENABLE) {
		if (!connect_change)
			dev_dbg(&port_dev->dev, "enable change, status %08x\n", portstatus);
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

		/*
		 * EM interference sometimes causes badly shielded USB devices
		 * to be shutdown by the hub, this hack enables them again.
		 * Works at least with mouse driver.
		 */
		if (!(portstatus & USB_PORT_STAT_ENABLE)  && !connect_change && udev) {
			dev_err(&port_dev->dev, "disabled by hub (EMI?), re-enabling...\n");
			connect_change = 1;
		}	
	}
	if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
		u16 status = 0, unused;

		dev_dbg(&port_dev->dev, "over-current change\n");
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_OVER_CURRENT);
		msleep(100);	/* Cool down */
		hub_power_on(hub, true);
		hub_port_status(hub, port1, &status, &unused);
		if (status & USB_PORT_STAT_OVERCURRENT)
			dev_err(&port_dev->dev, "over-current condition\n");
	}

	if (portchange & USB_PORT_STAT_C_RESET) {
		dev_dbg(&port_dev->dev, "reset change\n");
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET);
	}
	if ((portchange & USB_PORT_STAT_C_BH_RESET)
	    && hub_is_superspeed(hdev)) {
		dev_dbg(&port_dev->dev, "warm reset change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_BH_PORT_RESET);
	}
	if (portchange & USB_PORT_STAT_C_LINK_STATE) {
		dev_dbg(&port_dev->dev, "link state change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_PORT_LINK_STATE);
	}
	if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
		connect_change = 1;
		
	/*
	 * Warm reset a USB3 protocol port if it's in
	 * SS.Inactive state.
	 */
	if (hub_port_warm_reset_required(hub, port1, portstatus)) {
		dev_dbg(&port_dev->dev, "do warm reset\n");
		if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
				|| udev->state == USB_STATE_NOTATTACHED) {
			if (hub_port_reset(hub, port1, NULL,
					HUB_BH_RESET_TIME, true) < 0)
				hub_port_disable(hub, port1, 1);
		} else
			reset_device = 1;
	}

	if (connect_change)
		hub_port_connect_change(hub, port1, portstatus, portchange);
}

1.7 hub_port_connect_change

static void hub_port_connect_change(struct usb_hub *hub, int port1,
					u16 portstatus, u16 portchange)
		__must_hold(&port_dev->status_lock)
{
	if (hub->has_indicators) {
		set_port_led(hub, port1, HUB_LED_AUTO);
		hub->indicator[port1-1] = INDICATOR_AUTO;
	}
	#ifdef	CONFIG_USB_OTG
	/* during HNP, don't repeat the debounce */
	if (hub->hdev->bus->is_b_host)
		portchange &= ~(USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE);
	#endif

	hub_port_connect(hub, port1, portstatus, portchange);  // start to connect the port
}

1.8 hub_port_connect

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
		u16 portchange)
{
	/* Disconnect any existing devices under this port */
	if (udev) {
		if (hcd->usb_phy && !hdev->parent)
			usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
		usb_disconnect(&port_dev->child);
	}
	if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) {
		status = hub_port_debounce_be_stable(hub, port1);
		portstatus = status;
	}
	/* Return now if debouncing failed or nothing is connected or the device was "removed". */
	if (!(portstatus & USB_PORT_STAT_CONNECTION) || test_bit(port1, hub->removed_bits)) {

		/* maybe switch power back on (e.g. root hub was reset) */
		if (hub_is_port_power_switchable(hub) && !port_is_power_on(hub, portstatus))
			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
		return;
	}
	if (hub_is_superspeed(hub->hdev))
		unit_load = 150;
	else
		unit_load = 100;

	for (i = 0; i < SET_CONFIG_TRIES; i++) {
		/* reallocate for each attempt, since references to the previous one can escape in various ways */
		udev = usb_alloc_dev(hdev, hdev->bus, port1);
		usb_set_device_state(udev, USB_STATE_POWERED);   // set usb state powered
		udev->bus_mA = hub->mA_per_port;
		udev->level = hdev->level + 1;   // level + 1
		udev->wusb = hub_is_wusb(hub);

		/* Only USB 3.0 devices are connected to SuperSpeed hubs. */
		if (hub_is_superspeed(hub->hdev))
			udev->speed = USB_SPEED_SUPER;

		choose_devnum(udev);
		status = hub_port_init(hub, udev, port1, i);
		usb_detect_quirks(udev);

		/* check for devices running slower than they could */
		if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
				&& udev->speed == USB_SPEED_FULL && highspeed_hubs != 0)
			check_highspeed (hub, udev, port1);
		}
		/* Run it through the hoops (find a driver, etc) */
		if (!status) {
			status = usb_new_device(udev);
			if (status) {
				port_dev->child = NULL;
			} else {
				if (hcd->usb_phy && !hdev->parent)
					usb_phy_notify_connect(hcd->usb_phy, udev->speed);
			}
		}
		status = hub_power_remaining(hub);
		if (status)
			dev_dbg(hub->intfdev, "%dmA power budget left\n", status);

		return;
	}
}

1.9 usb_new_device

int usb_new_device(struct usb_device *udev)
{	
	/* Tell the runtime-PM framework the device is active */
	pm_runtime_set_active(&udev->dev);
	pm_runtime_get_noresume(&udev->dev);
	pm_runtime_use_autosuspend(&udev->dev);
	pm_runtime_enable(&udev->dev);

	/* By default, forbid autosuspend for all devices.  It will be allowed for hubs during binding.
	 */
	usb_disable_autosuspend(udev);

	err = usb_enumerate_device(udev);	/* Read descriptors */

	dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n", udev->devnum, udev->bus->busnum, (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
	
	/* export the usbdev device-node for libusb */
	udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

	/* Tell the world! */
	announce_device(udev);
	if (udev->serial)
		add_device_randomness(udev->serial, strlen(udev->serial));
	if (udev->product)
		add_device_randomness(udev->product, strlen(udev->product));
	if (udev->manufacturer)
		add_device_randomness(udev->manufacturer, strlen(udev->manufacturer));
	device_enable_async_suspend(&udev->dev);
	
	/* check whether the hub or firmware marks this port as non-removable */
	if (udev->parent)
		set_usb_port_removable(udev);
	
	err = device_add(&udev->dev);
		========>>>
			int device_add(struct device *dev)
			{
				kobject_uevent(&dev->kobj, KOBJ_ADD);
				bus_probe_device(dev);
				/* tie the class to the device */
				klist_add_tail(&dev->knode_class, &dev->class->p->klist_devices);
			}
		<<<========
	(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);    //create endpoint dev
	usb_mark_last_busy(udev);
	pm_runtime_put_sync_autosuspend(&udev->dev);
	return err;
}

1.10 usb_enumerate_device

static int usb_enumerate_device(struct usb_device *udev)
{
	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
	err = usb_get_configuration(udev);
	
	/* read the standard strings and cache them if present */
	udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
	udev->manufacturer = usb_cache_string(udev, udev->descriptor.iManufacturer);
	udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);

	err = usb_enumerate_device_otg(udev);

	usb_detect_interface_quirks(udev);
	===========>>>
		static u32 __usb_detect_quirks(struct usb_device *udev, const struct usb_device_id *id)
		{
			u32 quirks = 0;
			for (; id->match_flags; id++) {
				if (!usb_match_device(udev, id))
					continue;
				if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
				    !usb_match_any_interface(udev, id))     ---------------// from here to go 
					continue;
				quirks |= (u32)(id->driver_info);
			}
			return quirks;
		}
	<<<============
}

1.11 usb_enumerate_device_otg

static int usb_enumerate_device_otg(struct usb_device *udev)
{
	#ifdef	CONFIG_USB_OTG
	if (desc->bmAttributes & USB_OTG_HNP) {
		/* enable HNP before suspend, it's simpler */
		if (port1 == bus->otg_port)
			bus->b_hnp_enable = 1;
		err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, 0, 
					bus->b_hnp_enable ? USB_DEVICE_B_HNP_ENABLE : USB_DEVICE_A_ALT_HNP_SUPPORT,
					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
	}
}		

1.12 usb_control_msg

// usb_control_msg - Builds a control urb, sends it off and waits for completion
// This function sends a simple control message to a specified endpoint and waits for the message to complete, or timeout

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
		    __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)
{
	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);

	dr->bRequestType = requesttype;
	dr->bRequest = request;
	dr->wValue = cpu_to_le16(value);
	dr->wIndex = cpu_to_le16(index);
	dr->wLength = cpu_to_le16(size);

	ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
		==========>>>
			urb = usb_alloc_urb(0, GFP_NOIO);
			usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data, len, usb_api_blocking_completion, NULL);
			retv = usb_start_wait_urb(urb, timeout, &length);
			======>>>
				retval = usb_submit_urb(urb, GFP_NOIO);
				wait_event_timeout(awd.wqh, awd.done, timeout)
			<<<======
		<<<==========
	return ret;
}



1.13 usb_match_device(udev, id)

int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
	if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
	    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
		return 0;

	if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
	    id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
		return 0;

	/* No need to test id->bcdDevice_lo != 0, since 0 is never
	   greater than any unsigned number. */
	if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
	    (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
		return 0;

	if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
	    (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
		return 0;

	if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
	    (id->bDeviceClass != dev->descriptor.bDeviceClass))
		return 0;

	if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
	    (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
		return 0;

	if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
	    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
		return 0;

	return 1;
}



1.14 usb_match_any_interface(udev, id)

static bool usb_match_any_interface(struct usb_device *udev,
				    const struct usb_device_id *id)
{
	unsigned int i;

	for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
		struct usb_host_config *cfg = &udev->config[i];
		unsigned int j;

		for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
			struct usb_interface_cache *cache;
			struct usb_host_interface *intf;

			cache = cfg->intf_cache[j];
			if (cache->num_altsetting == 0)
				continue;

			intf = &cache->altsetting[0];
			if (usb_match_one_id_intf(udev, intf, id))
				return true;
		}
	}

	return false;
}



usb_gadget_set_state

composite_setup



二、USB ADB / USB Device

related keyword: USB Device

三、DWC3 Charge



Linux下USB驱动详解(HOST)
https://blog.csdn.net/feng85016578/article/details/52808434
ljzcom的专栏
https://blog.csdn.net/ljzcom/article/category/1067200
USB OTG学习笔记
https://blog.csdn.net/ljzcom/article/details/8843597

【精】USB具体通讯过程(含枚举过程)

从零开始学USB(十七、USB的枚举)

你可能感兴趣的:(Android,USB)