USB ADB: Corresponding to USB Device Mode
USB OTG: Corresponding to USB Host Mode
USB EAP: Corresponding to USB Network Mode
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)
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;
}
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);
}
/* 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);
}
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);
}
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);
}
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);
}
}
}
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);
}
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
}
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;
}
}
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;
}
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;
}
<<<============
}
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);
}
}
// 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;
}
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;
}
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
related keyword: USB Device
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的枚举)