ALSA: Allow to pass the device object to snd_register_device*()

This is a preliminary patch for the further work on embedding struct
device into each sound device instance.  It changes
snd_register_device*() helpers to receive the device object directly
for skipping creating a device there.

Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/include/sound/core.h b/include/sound/core.h
index 1df3f2f..39d1423 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -186,6 +186,7 @@
 	int type;			/* SNDRV_DEVICE_TYPE_XXX */
 	int card;			/* card number */
 	int device;			/* device number */
+	bool created;
 	const struct file_operations *f_ops;	/* file operations */
 	void *private_data;		/* private data for f_ops->open */
 	struct device *dev;		/* device for sysfs */
@@ -206,12 +207,10 @@
 
 void snd_request_card(int card);
 
-int snd_register_device_for_dev(int type, struct snd_card *card,
-				int dev,
+int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 				const struct file_operations *f_ops,
-				void *private_data,
-				const char *name,
-				struct device *device);
+				void *private_data, struct device *device,
+				struct device *parent, const char *name);
 
 /**
  * snd_register_device - Register the ALSA device file for the card
@@ -236,8 +235,9 @@
 				      const char *name)
 {
 	return snd_register_device_for_dev(type, card, dev, f_ops,
-					   private_data, name,
-					   snd_card_get_device_link(card));
+					   private_data, NULL,
+					   snd_card_get_device_link(card),
+					   name);
 }
 
 int snd_unregister_device(int type, struct snd_card *card, int dev);
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 69459e5..85096a1 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -433,7 +433,8 @@
 		dev = snd_card_get_device_link(hwdep->card);
 	err = snd_register_device_for_dev(SNDRV_DEVICE_TYPE_HWDEP,
 					  hwdep->card, hwdep->device,
-					  &snd_hwdep_f_ops, hwdep, name, dev);
+					  &snd_hwdep_f_ops, hwdep,
+					  NULL, dev, name);
 	if (err < 0) {
 		dev_err(dev,
 			"unable to register hardware dependent device %i:%i\n",
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index cfc56c8..dba5180 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1115,7 +1115,7 @@
 		err = snd_register_device_for_dev(devtype, pcm->card,
 						  pcm->device,
 						  &snd_pcm_f_ops[cidx],
-						  pcm, str, dev);
+						  pcm, NULL, dev, str);
 		if (err < 0) {
 			list_del(&pcm->list);
 			mutex_unlock(&register_mutex);
diff --git a/sound/core/sound.c b/sound/core/sound.c
index f133306..ea1af1a 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -248,8 +248,9 @@
  * @dev: the device index
  * @f_ops: the file operations
  * @private_data: user pointer for f_ops->open()
- * @name: the device file name
- * @device: the &struct device to link this new device to
+ * @device: the device to register, NULL to create a new one
+ * @parent: the &struct device to link this new device to (only for device=NULL)
+ * @name: the device file name (only for device=NULL)
  *
  * Registers an ALSA device file for the given card.
  * The operators have to be set in reg parameter.
@@ -258,14 +259,13 @@
  */
 int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
 				const struct file_operations *f_ops,
-				void *private_data,
-				const char *name, struct device *device)
+				void *private_data, struct device *device,
+				struct device *parent, const char *name)
 {
 	int minor;
+	int err = 0;
 	struct snd_minor *preg;
 
-	if (snd_BUG_ON(!name))
-		return -EINVAL;
 	preg = kmalloc(sizeof *preg, GFP_KERNEL);
 	if (preg == NULL)
 		return -ENOMEM;
@@ -284,23 +284,32 @@
 		minor = -EBUSY;
 #endif
 	if (minor < 0) {
-		mutex_unlock(&sound_mutex);
-		kfree(preg);
-		return minor;
-	}
-	snd_minors[minor] = preg;
-	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
-				  private_data, "%s", name);
-	if (IS_ERR(preg->dev)) {
-		snd_minors[minor] = NULL;
-		mutex_unlock(&sound_mutex);
-		minor = PTR_ERR(preg->dev);
-		kfree(preg);
-		return minor;
+		err = minor;
+		goto error;
 	}
 
+	if (device) {
+		preg->created = false;
+		preg->dev = device;
+		device->devt = MKDEV(major, minor);
+		err = device_add(device);
+	} else {
+		preg->created = true;
+		preg->dev = device_create(sound_class, parent,
+					  MKDEV(major, minor), private_data,
+					  "%s", name);
+		if (IS_ERR(preg->dev))
+			err = PTR_ERR(preg->dev);
+	}
+	if (err < 0)
+		goto error;
+
+	snd_minors[minor] = preg;
+ error:
 	mutex_unlock(&sound_mutex);
-	return 0;
+	if (err < 0)
+		kfree(preg);
+	return err;
 }
 
 EXPORT_SYMBOL(snd_register_device_for_dev);
@@ -337,6 +346,7 @@
 int snd_unregister_device(int type, struct snd_card *card, int dev)
 {
 	int minor;
+	struct snd_minor *preg;
 
 	mutex_lock(&sound_mutex);
 	minor = find_snd_minor(type, card, dev);
@@ -345,7 +355,11 @@
 		return -EINVAL;
 	}
 
-	device_destroy(sound_class, MKDEV(major, minor));
+	preg = snd_minors[minor];
+	if (preg && !preg->created)
+		device_del(preg->dev);
+	else
+		device_destroy(sound_class, MKDEV(major, minor));
 
 	kfree(snd_minors[minor]);
 	snd_minors[minor] = NULL;