usb: add device connection/disconnection detection

Provide a function to detect USB device insertion/removal in order to
avoid having to do USB enumeration in a tight loop when trying to detect
peripheral hotplugging.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>

Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
Tested-by: Stefan Reinauer <reinauer@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/common/usb.c b/common/usb.c
index 20c614c..6283f39 100644
--- a/common/usb.c
+++ b/common/usb.c
@@ -148,6 +148,32 @@
 	return 0;
 }
 
+/******************************************************************************
+ * Detect if a USB device has been plugged or unplugged.
+ */
+int usb_detect_change(void)
+{
+	int i, j;
+	int change = 0;
+
+	for (j = 0; j < USB_MAX_DEVICE; j++) {
+		for (i = 0; i < usb_dev[j].maxchild; i++) {
+			struct usb_port_status status;
+
+			if (usb_get_port_status(&usb_dev[j], i + 1,
+						&status) < 0)
+				/* USB request failed */
+				continue;
+
+			if (le16_to_cpu(status.wPortChange) &
+			    USB_PORT_STAT_C_CONNECTION)
+				change++;
+		}
+	}
+
+	return change;
+}
+
 /*
  * disables the asynch behaviour of the control message. This is used for data
  * transfers that uses the exclusiv access to the control and bulk messages.
diff --git a/common/usb_hub.c b/common/usb_hub.c
index af39c76..be01f4f 100644
--- a/common/usb_hub.c
+++ b/common/usb_hub.c
@@ -79,7 +79,7 @@
 			data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
 }
 
-static int usb_get_port_status(struct usb_device *dev, int port, void *data)
+int usb_get_port_status(struct usb_device *dev, int port, void *data)
 {
 	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
diff --git a/include/usb.h b/include/usb.h
index 5043bc3..c709ce2 100644
--- a/include/usb.h
+++ b/include/usb.h
@@ -265,6 +265,7 @@
 /* routines */
 int usb_init(void); /* initialize the USB Controller */
 int usb_stop(void); /* stop the USB Controller */
+int usb_detect_change(void); /* detect if a USB device has been (un)plugged */
 
 
 int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol);
@@ -290,6 +291,7 @@
 int usb_clear_halt(struct usb_device *dev, int pipe);
 int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
 int usb_set_interface(struct usb_device *dev, int interface, int alternate);
+int usb_get_port_status(struct usb_device *dev, int port, void *data);
 
 /* big endian -> little endian conversion */
 /* some CPUs are already little endian e.g. the ARM920T */