usb: gadget: Allow u_qc_ether to support multiple functions

u_qc_ether was originally planned to support only single function
driver instance, either f_qc_ecm or f_qc_rndis.
Android gadget can support composite devices with multiple
configurations, for example dual configuration of qc_rndis & qc_ecm
functions.

This change fixes u_qc_ether to support multiple functions.

CRs-Fixed:  408702
Change-Id: I62fa4ee77a349af7bcf8fa08719d20aa941881d9
Signed-off-by: Amit Blay <ablay@codeaurora.org>
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index f596411..52c19cf 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -636,7 +636,7 @@
 static void ecm_qc_function_unbind_config(struct android_usb_function *f,
 						struct usb_configuration *c)
 {
-	gether_qc_cleanup();
+	gether_qc_cleanup_name("ecm0");
 }
 
 static ssize_t ecm_ethaddr_show(struct device *dev,
@@ -1181,7 +1181,7 @@
 static void rndis_qc_function_unbind_config(struct android_usb_function *f,
 						struct usb_configuration *c)
 {
-	gether_qc_cleanup();
+	gether_qc_cleanup_name("rndis0");
 }
 
 static ssize_t rndis_manufacturer_show(struct device *dev,
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 1c64955..0b41197 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -518,7 +518,7 @@
 
 		if (ecm->port.in_ep->driver_data) {
 			DBG(cdev, "reset ecm\n");
-			gether_qc_disconnect(&ecm->port);
+			gether_qc_disconnect_name(&ecm->port, "ecm0");
 			ecm_qc_bam_disconnect(ecm);
 		}
 
@@ -548,7 +548,7 @@
 				);
 			ecm->port.cdc_filter = DEFAULT_FILTER;
 			DBG(cdev, "activate ecm\n");
-			net = gether_qc_connect(&ecm->port);
+			net = gether_qc_connect_name(&ecm->port, "ecm0");
 			if (IS_ERR(net))
 				return PTR_ERR(net);
 
@@ -591,7 +591,7 @@
 	DBG(cdev, "ecm deactivated\n");
 
 	if (ecm->port.in_ep->driver_data) {
-		gether_qc_disconnect(&ecm->port);
+		gether_qc_disconnect_name(&ecm->port, "ecm0");
 		ecm_qc_bam_disconnect(ecm);
 	}
 
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 7a181eb..f86bf12 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -661,7 +661,7 @@
 
 		if (rndis->port.in_ep->driver_data) {
 			DBG(cdev, "reset rndis\n");
-			gether_qc_disconnect(&rndis->port);
+			gether_qc_disconnect_name(&rndis->port, "rndis0");
 			rndis_qc_bam_disconnect(rndis);
 		}
 
@@ -695,7 +695,7 @@
 		rndis->port.cdc_filter = 0;
 
 		DBG(cdev, "RNDIS RX/TX early activation ...\n");
-		net = gether_qc_connect(&rndis->port);
+		net = gether_qc_connect_name(&rndis->port, "rndis0");
 		if (IS_ERR(net))
 			return PTR_ERR(net);
 
@@ -722,7 +722,7 @@
 	pr_info("rndis deactivated\n");
 
 	rndis_uninit(rndis->config);
-	gether_qc_disconnect(&rndis->port);
+	gether_qc_disconnect_name(&rndis->port, "rndis0");
 	rndis_qc_bam_disconnect(rndis);
 
 	usb_ep_disable(rndis->notify);
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index 20933b6..4931c1e 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -222,8 +222,6 @@
 	return 1;
 }
 
-static struct eth_qc_dev *qc_dev;
-
 static const struct net_device_ops eth_qc_netdev_ops = {
 	.ndo_open		= eth_qc_open,
 	.ndo_stop		= eth_qc_stop,
@@ -276,9 +274,6 @@
 	struct net_device	*net;
 	int			status;
 
-	if (qc_dev)
-		return -EBUSY;
-
 	net = alloc_etherdev(sizeof *dev);
 	if (!net)
 		return -ENOMEM;
@@ -318,32 +313,33 @@
 		INFO(dev, "MAC %pM\n", net->dev_addr);
 		INFO(dev, "HOST MAC %pM\n", dev->host_mac);
 
-		qc_dev = dev;
 	}
 
 	return status;
 }
 
 /**
- * gether_qc_cleanup - remove Ethernet-over-USB device
+ * gether_qc_cleanup_name - remove Ethernet-over-USB device
  * Context: may sleep
  *
  * This is called to free all resources allocated by @gether_qc_setup().
  */
-void gether_qc_cleanup(void)
+void gether_qc_cleanup_name(const char *netname)
 {
-	if (!qc_dev)
-		return;
+	struct net_device *net_dev;
 
-	unregister_netdev(qc_dev->net);
-	free_netdev(qc_dev->net);
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
 
-	qc_dev = NULL;
+	if (net_dev) {
+		unregister_netdev(net_dev);
+		free_netdev(net_dev);
+	}
 }
 
-
 /**
- * gether_qc_connect - notify network layer that USB link is active
+ * gether_qc_connect_name - notify network layer that USB link
+ * is active
  * @link: the USB link, set up with endpoints, descriptors matching
  *	current device speed, and any framing wrapper(s) set up.
  * Context: irqs blocked
@@ -351,9 +347,15 @@
  * This is called to let the network layer know the connection
  * is active ("carrier detect").
  */
-struct net_device *gether_qc_connect(struct qc_gether *link)
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+		const char *netname)
 {
-	struct eth_qc_dev		*dev = qc_dev;
+	struct net_device *net_dev;
+	struct eth_qc_dev *dev;
+
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
+	dev = netdev_priv(net_dev);
 
 	if (!dev)
 		return ERR_PTR(-EINVAL);
@@ -381,7 +383,8 @@
 }
 
 /**
- * gether_qc_disconnect - notify network layer that USB link is inactive
+ * gether_qc_disconnect_name - notify network layer that USB
+ * link is inactive
  * @link: the USB link, on which gether_connect() was called
  * Context: irqs blocked
  *
@@ -390,9 +393,14 @@
  *
  * On return, the state is as if gether_connect() had never been called.
  */
-void gether_qc_disconnect(struct qc_gether *link)
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname)
 {
-	struct eth_qc_dev		*dev = link->ioport;
+	struct net_device *net_dev;
+	struct eth_qc_dev *dev;
+
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
+	dev = netdev_priv(net_dev);
 
 	if (!dev)
 		return;
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index b3c281b..d91e805 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -78,14 +78,15 @@
 
 /* netdev setup/teardown as directed by the gadget driver */
 int gether_qc_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
-void gether_qc_cleanup(void);
+void gether_qc_cleanup_name(const char *netname);
 /* variant of gether_setup that allows customizing network device name */
 int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
 		const char *netname);
 
 /* connect/disconnect is handled by individual functions */
-struct net_device *gether_qc_connect(struct qc_gether *);
-void gether_qc_disconnect(struct qc_gether *);
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+		const char *netname);
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
 
 /* each configuration may bind one instance of an ethernet link */
 int ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);