[ALSA] dynamic minors (3/6): store device-specific object pointers dynamically

Instead of storing the pointers to the device-specific structures in an
array, put them into the struct snd_minor, and look them up dynamically.

This makes the device type modules independent of the minor number
encoding.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 2bc5f69..95036c8 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -33,7 +33,7 @@
 MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
 MODULE_LICENSE("GPL");
 
-struct snd_pcm *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES];
+static LIST_HEAD(snd_pcm_devices);
 static LIST_HEAD(snd_pcm_notify_list);
 static DECLARE_MUTEX(register_mutex);
 
@@ -43,13 +43,23 @@
 static int snd_pcm_dev_disconnect(struct snd_device *device);
 static int snd_pcm_dev_unregister(struct snd_device *device);
 
+static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
+{
+	struct list_head *p;
+	struct snd_pcm *pcm;
+
+	list_for_each(p, &snd_pcm_devices) {
+		pcm = list_entry(p, struct snd_pcm, list);
+		if (pcm->card == card && pcm->device == device)
+			return pcm;
+	}
+	return NULL;
+}
+
 static int snd_pcm_control_ioctl(struct snd_card *card,
 				 struct snd_ctl_file *control,
 				 unsigned int cmd, unsigned long arg)
 {
-	unsigned int tmp;
-
-	tmp = card->number * SNDRV_PCM_DEVICES;
 	switch (cmd) {
 	case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
 		{
@@ -57,14 +67,16 @@
 
 			if (get_user(device, (int __user *)arg))
 				return -EFAULT;
+			down(&register_mutex);
 			device = device < 0 ? 0 : device + 1;
 			while (device < SNDRV_PCM_DEVICES) {
-				if (snd_pcm_devices[tmp + device])
+				if (snd_pcm_search(card, device))
 					break;
 				device++;
 			}
 			if (device == SNDRV_PCM_DEVICES)
 				device = -1;
+			up(&register_mutex);
 			if (put_user(device, (int __user *)arg))
 				return -EFAULT;
 			return 0;
@@ -77,31 +89,44 @@
 			struct snd_pcm *pcm;
 			struct snd_pcm_str *pstr;
 			struct snd_pcm_substream *substream;
+			int err;
+
 			info = (struct snd_pcm_info __user *)arg;
 			if (get_user(device, &info->device))
 				return -EFAULT;
-			if (device >= SNDRV_PCM_DEVICES)
-				return -ENXIO;
-			pcm = snd_pcm_devices[tmp + device];
-			if (pcm == NULL)
-				return -ENXIO;
 			if (get_user(stream, &info->stream))
 				return -EFAULT;
 			if (stream < 0 || stream > 1)
 				return -EINVAL;
-			pstr = &pcm->streams[stream];
-			if (pstr->substream_count == 0)
-				return -ENOENT;
 			if (get_user(subdevice, &info->subdevice))
 				return -EFAULT;
-			if (subdevice >= pstr->substream_count)
-				return -ENXIO;
-			for (substream = pstr->substream; substream; substream = substream->next)
+			down(&register_mutex);
+			pcm = snd_pcm_search(card, device);
+			if (pcm == NULL) {
+				err = -ENXIO;
+				goto _error;
+			}
+			pstr = &pcm->streams[stream];
+			if (pstr->substream_count == 0) {
+				err = -ENOENT;
+				goto _error;
+			}
+			if (subdevice >= pstr->substream_count) {
+				err = -ENXIO;
+				goto _error;
+			}
+			for (substream = pstr->substream; substream;
+			     substream = substream->next)
 				if (substream->number == (int)subdevice)
 					break;
-			if (substream == NULL)
-				return -ENXIO;
-			return snd_pcm_info_user(substream, info);
+			if (substream == NULL) {
+				err = -ENXIO;
+				goto _error;
+			}
+			err = snd_pcm_info_user(substream, info);
+		_error:
+			up(&register_mutex);
+			return err;
 		}
 	case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
 		{
@@ -865,8 +890,7 @@
 
 static int snd_pcm_dev_register(struct snd_device *device)
 {
-	int idx, cidx, err;
-	unsigned short minor;
+	int cidx, err;
 	struct snd_pcm_substream *substream;
 	struct list_head *list;
 	char str[16];
@@ -874,12 +898,11 @@
 
 	snd_assert(pcm != NULL && device != NULL, return -ENXIO);
 	down(&register_mutex);
-	idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
-	if (snd_pcm_devices[idx]) {
+	if (snd_pcm_search(pcm->card, pcm->device)) {
 		up(&register_mutex);
 		return -EBUSY;
 	}
-	snd_pcm_devices[idx] = pcm;
+	list_add_tail(&pcm->list, &snd_pcm_devices);
 	for (cidx = 0; cidx < 2; cidx++) {
 		int devtype = -1;
 		if (pcm->streams[cidx].substream == NULL)
@@ -887,20 +910,19 @@
 		switch (cidx) {
 		case SNDRV_PCM_STREAM_PLAYBACK:
 			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
-			minor = SNDRV_MINOR_PCM_PLAYBACK + idx;
 			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
 			break;
 		case SNDRV_PCM_STREAM_CAPTURE:
 			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
-			minor = SNDRV_MINOR_PCM_CAPTURE + idx;
 			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
 			break;
 		}
 		if ((err = snd_register_device(devtype, pcm->card,
 					       pcm->device,
-					       &snd_pcm_f_ops[cidx], str)) < 0)
+					       &snd_pcm_f_ops[cidx],
+					       pcm, str)) < 0)
 		{
-			snd_pcm_devices[idx] = NULL;
+			list_del(&pcm->list);
 			up(&register_mutex);
 			return err;
 		}
@@ -921,11 +943,10 @@
 	struct snd_pcm *pcm = device->device_data;
 	struct list_head *list;
 	struct snd_pcm_substream *substream;
-	int idx, cidx;
+	int cidx;
 
 	down(&register_mutex);
-	idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
-	snd_pcm_devices[idx] = NULL;
+	list_del_init(&pcm->list);
 	for (cidx = 0; cidx < 2; cidx++)
 		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
 			if (substream->runtime)
@@ -941,15 +962,14 @@
 
 static int snd_pcm_dev_unregister(struct snd_device *device)
 {
-	int idx, cidx, devtype;
+	int cidx, devtype;
 	struct snd_pcm_substream *substream;
 	struct list_head *list;
 	struct snd_pcm *pcm = device->device_data;
 
 	snd_assert(pcm != NULL, return -ENXIO);
 	down(&register_mutex);
-	idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
-	snd_pcm_devices[idx] = NULL;
+	list_del(&pcm->list);
 	for (cidx = 0; cidx < 2; cidx++) {
 		devtype = -1;
 		switch (cidx) {
@@ -975,24 +995,19 @@
 
 int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
 {
-	int idx;
+	struct list_head *p;
 
 	snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
 	down(&register_mutex);
 	if (nfree) {
 		list_del(&notify->list);
-		for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
-			if (snd_pcm_devices[idx] == NULL)
-				continue;
-			notify->n_unregister(snd_pcm_devices[idx]);
-		}
+		list_for_each(p, &snd_pcm_devices)
+			notify->n_unregister(list_entry(p,
+							struct snd_pcm, list));
 	} else {
 		list_add_tail(&notify->list, &snd_pcm_notify_list);
-		for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
-			if (snd_pcm_devices[idx] == NULL)
-				continue;
-			notify->n_register(snd_pcm_devices[idx]);
-		}
+		list_for_each(p, &snd_pcm_devices)
+			notify->n_register(list_entry(p, struct snd_pcm, list));
 	}
 	up(&register_mutex);
 	return 0;
@@ -1005,16 +1020,14 @@
 static void snd_pcm_proc_read(struct snd_info_entry *entry,
 			      struct snd_info_buffer *buffer)
 {
-	int idx;
+	struct list_head *p;
 	struct snd_pcm *pcm;
 
 	down(&register_mutex);
-	for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
-		pcm = snd_pcm_devices[idx];
-		if (pcm == NULL)
-			continue;
-		snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES,
-			    idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name);
+	list_for_each(p, &snd_pcm_devices) {
+		pcm = list_entry(p, struct snd_pcm, list);
+		snd_iprintf(buffer, "%02i-%02i: %s : %s",
+			    pcm->card->number, pcm->device, pcm->id, pcm->name);
 		if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
 			snd_iprintf(buffer, " : playback %i",
 				    pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
@@ -1063,7 +1076,6 @@
 module_init(alsa_pcm_init)
 module_exit(alsa_pcm_exit)
 
-EXPORT_SYMBOL(snd_pcm_devices);
 EXPORT_SYMBOL(snd_pcm_new);
 EXPORT_SYMBOL(snd_pcm_new_stream);
 EXPORT_SYMBOL(snd_pcm_notify);