[PATCH/RFC 1/2] USB: gadget: f_mass_storage: split fsg_common_init() into two phases

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

 



This patch splits the fsg_common_init() into two phases.  The first
phase is a “early init” (carried by fsg_common_early_init() function)
and the second is binding to cdev (carried by
fsg_common_bind_cdev() function).

The second phase can be undone (by fsg_common_unbind_cdev()
function) and carried multiple times.

The “early init” needs only the configuration object (the fsg_config
structure) and does not depend on the composite device (the
usb_composite_dev structure).  The second stage binds the instance
of the fsg_common structure with the composite device.

This division allows for binding the MSF to several composite
devices which are created when you first usb_composite_unregister()
one and usb_composite_register() another.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx>
Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
---
 drivers/usb/gadget/f_mass_storage.c |  302 ++++++++++++++++++++++-------------
 1 files changed, 187 insertions(+), 115 deletions(-)

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 32cce02..2b5cb67 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -300,7 +300,7 @@
 /*------------------------------------------------------------------------*/
 
 #define FSG_DRIVER_DESC		"Mass Storage Function"
-#define FSG_DRIVER_VERSION	"2009/09/11"
+#define FSG_DRIVER_VERSION	"2010/07/01"
 
 static const char fsg_string_interface[] = "Mass Storage";
 
@@ -366,6 +366,7 @@ struct fsg_common {
 	unsigned int		lun;
 	struct fsg_lun		*luns;
 	struct fsg_lun		*curlun;
+	const char		*lun_name_format;
 
 	unsigned int		bulk_out_maxpacket;
 	enum fsg_state		state;		/* For exception handling */
@@ -379,6 +380,7 @@ struct fsg_common {
 	u32			usb_amount_left;
 
 	unsigned int		can_stall:1;
+	unsigned int		cfg_can_stall:1;
 	unsigned int		free_storage_on_release:1;
 	unsigned int		phase_error:1;
 	unsigned int		short_packet_received:1;
@@ -397,6 +399,7 @@ struct fsg_common {
 	/* Vendor (8 chars), product (16 chars), release (4
 	 * hexadecimal digits) and NUL byte */
 	char inquiry_string[8 + 16 + 4 + 1];
+	u16 cfg_release;
 
 	struct kref		ref;
 };
@@ -445,17 +448,7 @@ struct fsg_dev {
 };
 
 
-static inline int __fsg_is_set(struct fsg_common *common,
-			       const char *func, unsigned line)
-{
-	if (common->fsg)
-		return 1;
-	ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
-	WARN_ON(1);
-	return 0;
-}
-
-#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+#define fsg_is_set(common) (!WARN_ON(!(common)->fsg))
 
 
 static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
@@ -2685,21 +2678,29 @@ static inline void fsg_common_put(struct fsg_common *common)
 }
 
 
-static struct fsg_common *fsg_common_init(struct fsg_common *common,
-					  struct usb_composite_dev *cdev,
-					  struct fsg_config *cfg)
+static struct fsg_common *fsg_common_early_init(struct fsg_common *common,
+						struct fsg_config *cfg)
 {
-	struct usb_gadget *gadget = cdev->gadget;
 	struct fsg_buffhd *bh;
 	struct fsg_lun *curlun;
 	struct fsg_lun_config *lcfg;
 	int nluns, i, rc;
 	char *pathbuf;
 
+	/*
+	 * Do not use ERROR, WARNING, INFO, DBG and VDBG macros in
+	 * this function.  common->gadget is unset and usage of those
+	 * macros here will cause a NULL pointer dereference.  Instead
+	 * fallback to plain pr_*.
+	 *
+	 * The L*() macros (for luns) are fine.
+	 */
+
 	/* Find out how many LUNs there should be */
 	nluns = cfg->nluns;
 	if (nluns < 1 || nluns > FSG_MAX_LUNS) {
-		dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
+		pr_err("mass storage: invalid number of LUNs: %u\n",
+		       nluns);
 		return ERR_PTR(-EINVAL);
 	}
 
@@ -2717,70 +2718,39 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
 	common->ops = cfg->ops;
 	common->private_data = cfg->private_data;
 
-	common->gadget = gadget;
-	common->ep0 = gadget->ep0;
-	common->ep0req = cdev->req;
-
-	/* Maybe allocate device-global string IDs, and patch descriptors */
-	if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
-		rc = usb_string_id(cdev);
-		if (unlikely(rc < 0))
-			goto error_release;
-		fsg_strings[FSG_STRING_INTERFACE].id = rc;
-		fsg_intf_desc.iInterface = rc;
-	}
-
-	/* Create the LUNs, open their backing files, and register the
-	 * LUN devices in sysfs. */
+	/* Create the LUNs, and open their backing files */
 	curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL);
 	if (unlikely(!curlun)) {
 		rc = -ENOMEM;
-		goto error_release;
+		goto error;
 	}
 	common->luns = curlun;
+	common->nluns = nluns;
 
 	init_rwsem(&common->filesem);
 
-	for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
+	common->lun_name_format = cfg->lun_name_format ?: "lun%d";
+
+	lcfg = cfg->luns;
+	i = 0;
+	do {
 		curlun->cdrom = !!lcfg->cdrom;
 		curlun->ro = lcfg->cdrom || lcfg->ro;
 		curlun->removable = lcfg->removable;
-		curlun->dev.release = fsg_lun_release;
-		curlun->dev.parent = &gadget->dev;
-		/* curlun->dev.driver = &fsg_driver.driver; XXX */
-		dev_set_drvdata(&curlun->dev, &common->filesem);
-		dev_set_name(&curlun->dev,
-			     cfg->lun_name_format
-			   ? cfg->lun_name_format
-			   : "lun%d",
-			     i);
 
-		rc = device_register(&curlun->dev);
-		if (rc) {
-			INFO(common, "failed to register LUN%d: %d\n", i, rc);
-			common->nluns = i;
-			goto error_release;
-		}
-
-		rc = device_create_file(&curlun->dev, &dev_attr_ro);
-		if (rc)
-			goto error_luns;
-		rc = device_create_file(&curlun->dev, &dev_attr_file);
-		if (rc)
-			goto error_luns;
+		dev_set_name(&curlun->dev, common->lun_name_format, i);
+		dev_set_drvdata(&curlun->dev, &common->filesem);
 
 		if (lcfg->filename) {
 			rc = fsg_lun_open(curlun, lcfg->filename);
 			if (rc)
-				goto error_luns;
+				goto error;
 		} else if (!curlun->removable) {
-			ERROR(common, "no file given for LUN%d\n", i);
+			pr_err("mass storage: no file given for LUN%d\n", i);
 			rc = -EINVAL;
-			goto error_luns;
+			goto error;
 		}
-	}
-	common->nluns = nluns;
-
+	} while (++curlun, ++lcfg, ++i < nluns);
 
 	/* Data buffers cyclic list */
 	bh = common->buffhds;
@@ -2793,44 +2763,12 @@ buffhds_first_it:
 		bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
 		if (unlikely(!bh->buf)) {
 			rc = -ENOMEM;
-			goto error_release;
+			goto error;
 		}
 	} while (--i);
 	bh->next = common->buffhds;
 
 
-	/* Prepare inquiryString */
-	if (cfg->release != 0xffff) {
-		i = cfg->release;
-	} else {
-		i = usb_gadget_controller_number(gadget);
-		if (i >= 0) {
-			i = 0x0300 + i;
-		} else {
-			WARNING(common, "controller '%s' not recognized\n",
-				gadget->name);
-			i = 0x0399;
-		}
-	}
-#define OR(x, y) ((x) ? (x) : (y))
-	snprintf(common->inquiry_string, sizeof common->inquiry_string,
-		 "%-8s%-16s%04x",
-		 OR(cfg->vendor_name, "Linux   "),
-		 /* Assume product name dependent on the first LUN */
-		 OR(cfg->product_name, common->luns->cdrom
-				     ? "File-Stor Gadget"
-				     : "File-CD Gadget  "),
-		 i);
-
-
-	/* Some peripheral controllers are known not to be able to
-	 * halt bulk endpoints correctly.  If one of them is present,
-	 * disable stalls.
-	 */
-	common->can_stall = cfg->can_stall &&
-		!(gadget_is_at91(common->gadget));
-
-
 	spin_lock_init(&common->lock);
 	kref_init(&common->ref);
 
@@ -2838,19 +2776,18 @@ buffhds_first_it:
 	/* Tell the thread to start working */
 	common->thread_task =
 		kthread_create(fsg_main_thread, common,
-			       OR(cfg->thread_name, "file-storage"));
+			       cfg->thread_name ?: "file-storage");
 	if (IS_ERR(common->thread_task)) {
 		rc = PTR_ERR(common->thread_task);
-		goto error_release;
+		goto error;
 	}
 	init_completion(&common->thread_notifier);
 	init_waitqueue_head(&common->fsg_wait);
-#undef OR
 
 
 	/* Information */
-	INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
-	INFO(common, "Number of LUNs=%d\n", common->nluns);
+	pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION
+		"\nNumber of LUNs=%d\n", common->nluns);
 
 	pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
 	for (i = 0, nluns = common->nluns, curlun = common->luns;
@@ -2874,28 +2811,167 @@ buffhds_first_it:
 	}
 	kfree(pathbuf);
 
-	DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+	pr_debug(KERN_DEBUG "mass storage: I/O thread pid: %d\n", task_pid_nr(common->thread_task));
 
 	wake_up_process(common->thread_task);
 
-	return common;
+	/* Prepare inquiryString */
+	/* Release will be later added in fsg_common_bind_cdev() */
+	snprintf(common->inquiry_string, sizeof common->inquiry_string,
+		 "%-8s%-16s",
+		 cfg->vendor_name ?: "Linux   ",
+		 /* Assume product name dependent on the first LUN */
+		 cfg->product_name ?: (common->luns->cdrom
+				     ? "File-Stor Gadget"
+				     : "File-CD Gadget  "));
+
+	common->cfg_release = cfg->release;
+	common->cfg_can_stall = cfg->can_stall;
 
+	return common;
 
-error_luns:
-	common->nluns = i + 1;
-error_release:
+error:
 	common->state = FSG_STATE_TERMINATED;	/* The thread is dead */
-	/* Call fsg_common_release() directly, ref might be not
-	 * initialised */
+	/*
+	 * Call fsg_common_release() directly, ref might be not
+	 * initialised
+	 */
 	fsg_common_release(&common->ref);
 	return ERR_PTR(rc);
 }
 
 
+static void fsg_common_unbind_cdev(struct fsg_common *common)
+{
+	struct fsg_lun *curlun;
+	unsigned i, nluns;
+
+	if (!common->gadget)
+		return;
+
+	common->gadget = NULL;
+	common->ep0 = NULL;
+	common->ep0req = NULL;
+
+	curlun = common->luns;
+	if (unlikely(!curlun))
+		return;
+
+	nluns = common->nluns;
+	i = 0;
+	do {
+		if (!curlun->dev.parent)
+			continue;
+
+		device_remove_file(&curlun->dev, &dev_attr_ro);
+		device_remove_file(&curlun->dev, &dev_attr_file);
+		device_unregister(&curlun->dev);
+		memset(&curlun->dev, 0, sizeof curlun->dev);
+
+		dev_set_name(&curlun->dev, common->lun_name_format, i);
+		dev_set_drvdata(&curlun->dev, &common->filesem);
+	} while (++curlun, ++i < nluns);
+}
+
+static int fsg_common_bind_cdev(struct fsg_common *common,
+				struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+	struct fsg_lun *curlun;
+	int i, rc;
+
+	/* Register string */
+	i = usb_string_id(cdev);
+	if (unlikely(i < 0))
+		return i;
+	fsg_strings[FSG_STRING_INTERFACE].id = i;
+	fsg_intf_desc.iInterface = i;
+
+	/* Save pointers */
+	common->gadget = gadget;
+	common->ep0 = gadget->ep0;
+	common->ep0req = cdev->req;
+
+	/* Register the LUN devices in sysfs. */
+	curlun = common->luns;
+	i = common->nluns;
+	do {
+		curlun->dev.parent = &gadget->dev;
+		curlun->dev.release = fsg_lun_release;
+		rc = device_register(&curlun->dev);
+		if (unlikely(rc)) {
+			curlun->dev.parent = NULL;
+			goto error;
+		}
+
+		rc = device_create_file(&curlun->dev, &dev_attr_ro);
+		if (unlikely(rc))
+			goto error;
+		rc = device_create_file(&curlun->dev, &dev_attr_file);
+		if (unlikely(rc))
+			goto error;
+	} while (++curlun, --i);
+
+	/* Save release in inquiryString */
+	/*
+	 * The rest of the inquiryString is prepared in
+	 * fsg_common_early_init().
+	 */
+	if (common->cfg_release != 0xffff) {
+		i = common->cfg_release;
+	} else {
+		i = usb_gadget_controller_number(gadget);
+		i = i >= 0 ? 0x0300 + i : 0x0399;
+	}
+
+	snprintf(common->inquiry_string + 8 + 16,
+		 sizeof common->inquiry_string - 8 - 16,
+		 "%04x", i);
+
+	/*
+	 * Some peripheral controllers are known not to be able to
+	 * halt bulk endpoints correctly.  If one of them is present,
+	 * disable stalls.
+	 */
+	common->can_stall = common->cfg_can_stall &&
+		!(gadget_is_at91(common->gadget));
+
+	return 0;
+
+error:
+	fsg_common_unbind_cdev(common);
+	return rc;
+}
+
+
+static struct fsg_common *fsg_common_init(struct fsg_common *common,
+					  struct usb_composite_dev *cdev,
+					  struct fsg_config *cfg)
+{
+	int ret;
+
+	common = fsg_common_early_init(common, cfg);
+	if (unlikely(IS_ERR(common)))
+		return common;
+
+	ret = fsg_common_bind_cdev(common, cdev);
+	if (unlikely(ret))
+		goto error;
+	return common;
+
+error:
+	fsg_common_release(&common->ref);
+	return ERR_PTR(ret);
+}
+
+
 static void fsg_common_release(struct kref *ref)
 {
 	struct fsg_common *common = container_of(ref, struct fsg_common, ref);
 
+	/* Unbind from cdev */
+	fsg_common_unbind_cdev(common);
+
 	/* If the thread isn't already dead, tell it to exit now */
 	if (common->state != FSG_STATE_TERMINATED) {
 		raise_exception(common, FSG_STATE_EXIT);
@@ -2906,16 +2982,12 @@ static void fsg_common_release(struct kref *ref)
 	}
 
 	if (likely(common->luns)) {
-		struct fsg_lun *lun = common->luns;
+		struct fsg_lun *curlun = common->luns;
 		unsigned i = common->nluns;
 
-		/* In error recovery common->nluns may be zero. */
-		for (; i; --i, ++lun) {
-			device_remove_file(&lun->dev, &dev_attr_ro);
-			device_remove_file(&lun->dev, &dev_attr_file);
-			fsg_lun_close(lun);
-			device_unregister(&lun->dev);
-		}
+		do {
+			fsg_lun_close(curlun);
+		} while (++curlun, --i);
 
 		kfree(common->luns);
 	}
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux