[PATCH] GADGET : allow to build multi udc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I seems there no work on UDC Class anymore. So I repost my multi udc patch.


Even if the usb gadget framework is limited to work with one driver, it
could be useful to have a kernel build with more than driver.
This allow to make generic kernel that work with different udc controller.

After looking at the current code, the only blocker to do that is
usb_gadget_register_driver/usb_gadget_unregister_driver function that
are declared in each driver.

For avoiding that I propose to do a redirection for these functions.
At probe time the driver register them (usb_gadget_register/usb_gadget_unregister), and
usb_gadget_register_driver/usb_gadget_unregister_driver call these callback.
We pass pass struct *usb_gadget in usb_gadget_register/usb_gadget_unregister for flexibility (we can latter do a more complex dispatcher).

the implementation is provided in multi_udc_core.diff.

The driver conversion is quite easy and we don't need to port all the driver at one time.

multi_udc_driver.diff is port for ci13xxx_udc.c and net2280.c


Happy new year.

Matthieu CASTET

---


Signed-off-by: Matthieu CASTET <matthieu.castet@xxxxxxxxxx>


diff --git a/drivers/Makefile b/drivers/Makefile
index f3ebb30..a7450af 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -68,7 +68,7 @@ obj-$(CONFIG_USB_OTG_UTILS)	+= usb/otg/
 obj-$(CONFIG_USB)		+= usb/
 obj-$(CONFIG_USB_MUSB_HDRC)	+= usb/musb/
 obj-$(CONFIG_PCI)		+= usb/
-obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
+obj-y					+= usb/gadget/
 obj-$(CONFIG_SERIO)		+= input/serio/
 obj-$(CONFIG_GAMEPORT)		+= input/gameport/
 obj-$(CONFIG_INPUT)		+= input/
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 607d0db..66f17b6 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -495,6 +495,11 @@ config USB_LANGWELL
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_MULTIUDC
+	boolean "multi USB Device Port"
+	select USB_GADGET_SELECTED
+	help
+		Allow to build more than one udc.
 
 #
 # LAST -- dummy/emulated controller
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5780db4..3ab4de4 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -25,6 +25,8 @@ obj-$(CONFIG_USB_CI13XXX)	+= ci13xxx_udc.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 
+obj-$(CONFIG_USB_GADGET_MULTIUDC) += core_udc.o
+
 #
 # USB gadget drivers
 #
diff --git a/drivers/usb/gadget/core_udc.c b/drivers/usb/gadget/core_udc.c
new file mode 100644
index 0000000..a29af82
--- /dev/null
+++ b/drivers/usb/gadget/core_udc.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Matthieu CASTET <matthieu.castet@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+static struct usb_gadget *usb_gadget_udc;
+
+int usb_gadget_register(struct usb_gadget *gadget)
+{
+	if (!gadget->udc ||
+		!gadget->udc->register_driver || !gadget->udc->unregister_driver)
+		return -EINVAL;
+
+	if (usb_gadget_udc)
+		return -EBUSY;
+
+	usb_gadget_udc = gadget;
+
+	return device_register(&gadget->dev);
+}
+EXPORT_SYMBOL(usb_gadget_register);
+
+void usb_gadget_unregister(struct usb_gadget *gadget)
+{
+	if (!usb_gadget_udc || usb_gadget_udc != gadget)
+		return;
+
+	usb_gadget_udc = NULL;
+
+	device_unregister(&gadget->dev);
+}
+EXPORT_SYMBOL(usb_gadget_unregister);
+
+int usb_gadget_register_driver (struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *))
+{
+	if (!usb_gadget_udc)
+		return -ENODEV;
+	return usb_gadget_udc->udc->register_driver(driver, bind, usb_gadget_udc);
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	if (!usb_gadget_udc)
+		return -ENODEV;
+	return usb_gadget_udc->udc->unregister_driver(driver, usb_gadget_udc);
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 006412c..5979f01 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -432,6 +432,33 @@ struct usb_gadget_ops {
 				unsigned code, unsigned long param);
 };
 
+#ifdef CONFIG_USB_GADGET_MULTIUDC
+struct usb_gadget_driver;
+/**
+ * struct usb_gadget_udc - driver for udc device
+ * @register_driver:register a gadget driver
+ * @unregister_driver:unregister a gadget driver
+ *
+ * @see usb_gadget_register_driver and usb_gadget_unregister_driver
+ */
+struct usb_gadget_udc {
+    int (*register_driver) (struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *), struct usb_gadget *gadget);
+    int (*unregister_driver) (struct usb_gadget_driver *driver, struct usb_gadget *gadget);
+};
+
+/**
+ * usb_gadget_register - register a udc driver
+ * @gadget:the driver being registered
+ */
+int usb_gadget_register(struct usb_gadget *gadget);
+
+/**
+ * usb_gadget_unregister - unregister a udc driver
+ * @gadget:the driver being registered
+ */
+void usb_gadget_unregister(struct usb_gadget *gadget);
+#endif
+
 /**
  * struct usb_gadget - represents a usb slave device
  * @ops: Function pointers used to access hardware-specific operations.
@@ -487,6 +514,9 @@ struct usb_gadget {
 	unsigned			a_hnp_support:1;
 	unsigned			a_alt_hnp_support:1;
 	const char			*name;
+#ifdef CONFIG_USB_GADGET_MULTIUDC
+	struct usb_gadget_udc *udc;
+#endif
 	struct device			dev;
 };
 
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 66f17b6..f8cf72f 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -414,46 +414,6 @@ config USB_FSL_QE
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
-config USB_GADGET_CI13XXX
-	boolean "MIPS USB CI13xxx"
-	depends on PCI
-	select USB_GADGET_DUALSPEED
-	help
-	  MIPS USB IP core family device controller
-	  Currently it only supports IP part number CI13412
-
-	  Say "y" to link the driver statically, or "m" to build a
-	  dynamically linked module called "ci13xxx_udc" and force all
-	  gadget drivers to also be dynamically linked.
-
-config USB_CI13XXX
-	tristate
-	depends on USB_GADGET_CI13XXX
-	default USB_GADGET
-	select USB_GADGET_SELECTED
-
-config USB_GADGET_NET2280
-	boolean "NetChip 228x"
-	depends on PCI
-	select USB_GADGET_DUALSPEED
-	help
-	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which
-	   supports both full and high speed USB 2.0 data transfers.
-
-	   It has six configurable endpoints, as well as endpoint zero
-	   (for control transfers) and several endpoints with dedicated
-	   functions.
-
-	   Say "y" to link the driver statically, or "m" to build a
-	   dynamically linked module called "net2280" and force all
-	   gadget drivers to also be dynamically linked.
-
-config USB_NET2280
-	tristate
-	depends on USB_GADGET_NET2280
-	default USB_GADGET
-	select USB_GADGET_SELECTED
-
 config USB_GADGET_GOKU
 	boolean "Toshiba TC86C001 'Goku-S'"
 	depends on PCI
@@ -539,6 +499,50 @@ config USB_DUMMY_HCD
 
 endchoice
 
+menu "multi USB Peripheral Controller"
+	depends on USB_GADGET_MULTIUDC
+
+config USB_GADGET_CI13XXX
+	boolean "MIPS USB CI13xxx"
+	depends on PCI
+	select USB_GADGET_DUALSPEED
+	help
+	  MIPS USB IP core family device controller
+	  Currently it only supports IP part number CI13412
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "ci13xxx_udc" and force all
+	  gadget drivers to also be dynamically linked.
+
+config USB_CI13XXX
+	tristate
+	depends on USB_GADGET_CI13XXX
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
+config USB_GADGET_NET2280
+	boolean "NetChip 228x"
+	depends on PCI
+	select USB_GADGET_DUALSPEED
+	help
+	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which
+	   supports both full and high speed USB 2.0 data transfers.
+
+	   It has six configurable endpoints, as well as endpoint zero
+	   (for control transfers) and several endpoints with dedicated
+	   functions.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "net2280" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_NET2280
+	tristate
+	depends on USB_GADGET_NET2280
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+endmenu
+
 config USB_GADGET_DUALSPEED
 	bool
 	depends on USB_GADGET
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 98b36fc..1074382 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -2339,18 +2339,20 @@ static const struct usb_ep_ops usb_ep_ops = {
  */
 static const struct usb_gadget_ops usb_gadget_ops;
 
+static int cil_usb_gadget_unregister_driver(struct usb_gadget_driver *driver, struct usb_gadget *gadget);
 /**
- * usb_gadget_probe_driver: register a gadget driver
+ * cil_usb_gadget_register_driver: register a gadget driver
  * @driver: the driver being registered
  * @bind: the driver's bind callback
+ * @usb_gadget
  *
  * Check usb_gadget_probe_driver() at <linux/usb/gadget.h> for details.
  * Interrupts are enabled here.
  */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
-		int (*bind)(struct usb_gadget *))
+static int cil_usb_gadget_register_driver(struct usb_gadget_driver *driver, 
+		int (*bind)(struct usb_gadget *), struct usb_gadget *gadget)
 {
-	struct ci13xxx *udc = _udc;
+	struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
 	unsigned long i, k, flags;
 	int retval = -ENOMEM;
 
@@ -2447,19 +2449,18 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
  done:
 	spin_unlock_irqrestore(udc->lock, flags);
 	if (retval)
-		usb_gadget_unregister_driver(driver);
+		cil_usb_gadget_unregister_driver(driver, gadget);
 	return retval;
 }
-EXPORT_SYMBOL(usb_gadget_probe_driver);
 
 /**
- * usb_gadget_unregister_driver: unregister a gadget driver
+ * cil_usb_gadget_unregister_driver: unregister a gadget driver
  *
  * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details
  */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int cil_usb_gadget_unregister_driver(struct usb_gadget_driver *driver, struct usb_gadget *gadget)
 {
-	struct ci13xxx *udc = _udc;
+	struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
 	unsigned long i, k, flags;
 
 	trace("%p", driver);
@@ -2519,7 +2520,11 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 
 	return 0;
 }
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+struct usb_gadget_udc cil_driver = {
+	.register_driver = cil_usb_gadget_register_driver,
+	.unregister_driver = cil_usb_gadget_unregister_driver,
+};
 
 /******************************************************************************
  * BUS block
@@ -2627,6 +2632,7 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
 	udc->gadget.is_dualspeed = 1;
 	udc->gadget.is_otg       = 0;
 	udc->gadget.name         = name;
+	udc->gadget.udc          = &cil_driver;
 
 	INIT_LIST_HEAD(&udc->gadget.ep_list);
 	udc->gadget.ep0 = NULL;
@@ -2636,7 +2642,8 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
 	udc->gadget.dev.parent   = dev;
 	udc->gadget.dev.release  = udc_release;
 
-	retval = device_register(&udc->gadget.dev);
+	_udc = udc;
+	retval = usb_gadget_register(&udc->gadget);
 	if (retval)
 		goto done;
 
@@ -2644,11 +2651,10 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
 	retval = dbg_create_files(&udc->gadget.dev);
 #endif
 	if (retval) {
-		device_unregister(&udc->gadget.dev);
+		usb_gadget_unregister(&udc->gadget);
 		goto done;
 	}
 
-	_udc = udc;
 	return retval;
 
  done:
@@ -2675,7 +2681,7 @@ static void udc_remove(void)
 #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 	dbg_remove_files(&udc->gadget.dev);
 #endif
-	device_unregister(&udc->gadget.dev);
+	usb_gadget_unregister(&udc->gadget);
 
 	kfree(udc);
 	_udc = NULL;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index d09155b..68072a7 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -1802,8 +1802,6 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
  * perhaps to bind specific drivers to specific devices.
  */
 
-static struct net2280	*the_controller;
-
 static void usb_reset (struct net2280 *dev)
 {
 	u32	tmp;
@@ -1929,10 +1927,10 @@ static void ep0_start (struct net2280 *dev)
  * disconnect is reported.  then a host may connect again, or
  * the driver might get unbound.
  */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
-		int (*bind)(struct usb_gadget *))
+static int net2280_usb_gadget_register_driver (struct usb_gadget_driver *driver,
+	   int (*bind)(struct usb_gadget *), struct usb_gadget *gadget)
 {
-	struct net2280		*dev = the_controller;
+	struct net2280		*dev = container_of (gadget, struct net2280, gadget);
 	int			retval;
 	unsigned		i;
 
@@ -1993,7 +1991,6 @@ err_unbind:
 	dev->driver = NULL;
 	return retval;
 }
-EXPORT_SYMBOL(usb_gadget_probe_driver);
 
 static void
 stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
@@ -2021,9 +2018,9 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
 	usb_reinit (dev);
 }
 
-int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+static int net2280_usb_gadget_unregister_driver (struct usb_gadget_driver *driver, struct usb_gadget *gadget)
 {
-	struct net2280	*dev = the_controller;
+	struct net2280	*dev = container_of (gadget, struct net2280, gadget);
 	unsigned long	flags;
 
 	if (!dev)
@@ -2048,7 +2045,11 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
 	DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
 	return 0;
 }
-EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+struct usb_gadget_udc net2280_driver = {
+	.register_driver = net2280_usb_gadget_register_driver,
+	.unregister_driver = net2280_usb_gadget_unregister_driver,
+};
 
 
 /*-------------------------------------------------------------------------*/
@@ -2754,13 +2755,11 @@ static void net2280_remove (struct pci_dev *pdev)
 				pci_resource_len (pdev, 0));
 	if (dev->enabled)
 		pci_disable_device (pdev);
-	device_unregister (&dev->gadget.dev);
+	usb_gadget_unregister(&dev->gadget);
 	device_remove_file (&pdev->dev, &dev_attr_registers);
 	pci_set_drvdata (pdev, NULL);
 
 	INFO (dev, "unbind\n");
-
-	the_controller = NULL;
 }
 
 /* wrap this driver around the specified device, but
@@ -2774,14 +2773,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 	void			__iomem *base = NULL;
 	int			retval, i;
 
-	/* if you want to support more than one controller in a system,
-	 * usb_gadget_driver_{register,unregister}() must change.
-	 */
-	if (the_controller) {
-		dev_warn (&pdev->dev, "ignoring\n");
-		return -EBUSY;
-	}
-
 	/* alloc, and start init */
 	dev = kzalloc (sizeof *dev, GFP_KERNEL);
 	if (dev == NULL){
@@ -2908,9 +2899,8 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 			use_dma
 				? (use_dma_chaining ? "chaining" : "enabled")
 				: "disabled");
-	the_controller = dev;
-
-	retval = device_register (&dev->gadget.dev);
+	dev->gadget.udc = &net2280_driver;
+	retval = usb_gadget_register(&dev->gadget);
 	if (retval) goto done;
 	retval = device_create_file (&pdev->dev, &dev_attr_registers);
 	if (retval) goto done;

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux