aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/pcm.c')
-rw-r--r--sound/core/pcm.c118
1 files changed, 65 insertions, 53 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 2bc5f69ec2a8..95036c83de43 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -33,7 +33,7 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-proj
33MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 33MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
34MODULE_LICENSE("GPL"); 34MODULE_LICENSE("GPL");
35 35
36struct snd_pcm *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; 36static LIST_HEAD(snd_pcm_devices);
37static LIST_HEAD(snd_pcm_notify_list); 37static LIST_HEAD(snd_pcm_notify_list);
38static DECLARE_MUTEX(register_mutex); 38static DECLARE_MUTEX(register_mutex);
39 39
@@ -43,13 +43,23 @@ static int snd_pcm_dev_register(struct snd_device *device);
43static int snd_pcm_dev_disconnect(struct snd_device *device); 43static int snd_pcm_dev_disconnect(struct snd_device *device);
44static int snd_pcm_dev_unregister(struct snd_device *device); 44static int snd_pcm_dev_unregister(struct snd_device *device);
45 45
46static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
47{
48 struct list_head *p;
49 struct snd_pcm *pcm;
50
51 list_for_each(p, &snd_pcm_devices) {
52 pcm = list_entry(p, struct snd_pcm, list);
53 if (pcm->card == card && pcm->device == device)
54 return pcm;
55 }
56 return NULL;
57}
58
46static int snd_pcm_control_ioctl(struct snd_card *card, 59static int snd_pcm_control_ioctl(struct snd_card *card,
47 struct snd_ctl_file *control, 60 struct snd_ctl_file *control,
48 unsigned int cmd, unsigned long arg) 61 unsigned int cmd, unsigned long arg)
49{ 62{
50 unsigned int tmp;
51
52 tmp = card->number * SNDRV_PCM_DEVICES;
53 switch (cmd) { 63 switch (cmd) {
54 case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 64 case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
55 { 65 {
@@ -57,14 +67,16 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
57 67
58 if (get_user(device, (int __user *)arg)) 68 if (get_user(device, (int __user *)arg))
59 return -EFAULT; 69 return -EFAULT;
70 down(&register_mutex);
60 device = device < 0 ? 0 : device + 1; 71 device = device < 0 ? 0 : device + 1;
61 while (device < SNDRV_PCM_DEVICES) { 72 while (device < SNDRV_PCM_DEVICES) {
62 if (snd_pcm_devices[tmp + device]) 73 if (snd_pcm_search(card, device))
63 break; 74 break;
64 device++; 75 device++;
65 } 76 }
66 if (device == SNDRV_PCM_DEVICES) 77 if (device == SNDRV_PCM_DEVICES)
67 device = -1; 78 device = -1;
79 up(&register_mutex);
68 if (put_user(device, (int __user *)arg)) 80 if (put_user(device, (int __user *)arg))
69 return -EFAULT; 81 return -EFAULT;
70 return 0; 82 return 0;
@@ -77,31 +89,44 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
77 struct snd_pcm *pcm; 89 struct snd_pcm *pcm;
78 struct snd_pcm_str *pstr; 90 struct snd_pcm_str *pstr;
79 struct snd_pcm_substream *substream; 91 struct snd_pcm_substream *substream;
92 int err;
93
80 info = (struct snd_pcm_info __user *)arg; 94 info = (struct snd_pcm_info __user *)arg;
81 if (get_user(device, &info->device)) 95 if (get_user(device, &info->device))
82 return -EFAULT; 96 return -EFAULT;
83 if (device >= SNDRV_PCM_DEVICES)
84 return -ENXIO;
85 pcm = snd_pcm_devices[tmp + device];
86 if (pcm == NULL)
87 return -ENXIO;
88 if (get_user(stream, &info->stream)) 97 if (get_user(stream, &info->stream))
89 return -EFAULT; 98 return -EFAULT;
90 if (stream < 0 || stream > 1) 99 if (stream < 0 || stream > 1)
91 return -EINVAL; 100 return -EINVAL;
92 pstr = &pcm->streams[stream];
93 if (pstr->substream_count == 0)
94 return -ENOENT;
95 if (get_user(subdevice, &info->subdevice)) 101 if (get_user(subdevice, &info->subdevice))
96 return -EFAULT; 102 return -EFAULT;
97 if (subdevice >= pstr->substream_count) 103 down(&register_mutex);
98 return -ENXIO; 104 pcm = snd_pcm_search(card, device);
99 for (substream = pstr->substream; substream; substream = substream->next) 105 if (pcm == NULL) {
106 err = -ENXIO;
107 goto _error;
108 }
109 pstr = &pcm->streams[stream];
110 if (pstr->substream_count == 0) {
111 err = -ENOENT;
112 goto _error;
113 }
114 if (subdevice >= pstr->substream_count) {
115 err = -ENXIO;
116 goto _error;
117 }
118 for (substream = pstr->substream; substream;
119 substream = substream->next)
100 if (substream->number == (int)subdevice) 120 if (substream->number == (int)subdevice)
101 break; 121 break;
102 if (substream == NULL) 122 if (substream == NULL) {
103 return -ENXIO; 123 err = -ENXIO;
104 return snd_pcm_info_user(substream, info); 124 goto _error;
125 }
126 err = snd_pcm_info_user(substream, info);
127 _error:
128 up(&register_mutex);
129 return err;
105 } 130 }
106 case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 131 case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
107 { 132 {
@@ -865,8 +890,7 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
865 890
866static int snd_pcm_dev_register(struct snd_device *device) 891static int snd_pcm_dev_register(struct snd_device *device)
867{ 892{
868 int idx, cidx, err; 893 int cidx, err;
869 unsigned short minor;
870 struct snd_pcm_substream *substream; 894 struct snd_pcm_substream *substream;
871 struct list_head *list; 895 struct list_head *list;
872 char str[16]; 896 char str[16];
@@ -874,12 +898,11 @@ static int snd_pcm_dev_register(struct snd_device *device)
874 898
875 snd_assert(pcm != NULL && device != NULL, return -ENXIO); 899 snd_assert(pcm != NULL && device != NULL, return -ENXIO);
876 down(&register_mutex); 900 down(&register_mutex);
877 idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; 901 if (snd_pcm_search(pcm->card, pcm->device)) {
878 if (snd_pcm_devices[idx]) {
879 up(&register_mutex); 902 up(&register_mutex);
880 return -EBUSY; 903 return -EBUSY;
881 } 904 }
882 snd_pcm_devices[idx] = pcm; 905 list_add_tail(&pcm->list, &snd_pcm_devices);
883 for (cidx = 0; cidx < 2; cidx++) { 906 for (cidx = 0; cidx < 2; cidx++) {
884 int devtype = -1; 907 int devtype = -1;
885 if (pcm->streams[cidx].substream == NULL) 908 if (pcm->streams[cidx].substream == NULL)
@@ -887,20 +910,19 @@ static int snd_pcm_dev_register(struct snd_device *device)
887 switch (cidx) { 910 switch (cidx) {
888 case SNDRV_PCM_STREAM_PLAYBACK: 911 case SNDRV_PCM_STREAM_PLAYBACK:
889 sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); 912 sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
890 minor = SNDRV_MINOR_PCM_PLAYBACK + idx;
891 devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 913 devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
892 break; 914 break;
893 case SNDRV_PCM_STREAM_CAPTURE: 915 case SNDRV_PCM_STREAM_CAPTURE:
894 sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); 916 sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
895 minor = SNDRV_MINOR_PCM_CAPTURE + idx;
896 devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 917 devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
897 break; 918 break;
898 } 919 }
899 if ((err = snd_register_device(devtype, pcm->card, 920 if ((err = snd_register_device(devtype, pcm->card,
900 pcm->device, 921 pcm->device,
901 &snd_pcm_f_ops[cidx], str)) < 0) 922 &snd_pcm_f_ops[cidx],
923 pcm, str)) < 0)
902 { 924 {
903 snd_pcm_devices[idx] = NULL; 925 list_del(&pcm->list);
904 up(&register_mutex); 926 up(&register_mutex);
905 return err; 927 return err;
906 } 928 }
@@ -921,11 +943,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
921 struct snd_pcm *pcm = device->device_data; 943 struct snd_pcm *pcm = device->device_data;
922 struct list_head *list; 944 struct list_head *list;
923 struct snd_pcm_substream *substream; 945 struct snd_pcm_substream *substream;
924 int idx, cidx; 946 int cidx;
925 947
926 down(&register_mutex); 948 down(&register_mutex);
927 idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; 949 list_del_init(&pcm->list);
928 snd_pcm_devices[idx] = NULL;
929 for (cidx = 0; cidx < 2; cidx++) 950 for (cidx = 0; cidx < 2; cidx++)
930 for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 951 for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
931 if (substream->runtime) 952 if (substream->runtime)
@@ -941,15 +962,14 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
941 962
942static int snd_pcm_dev_unregister(struct snd_device *device) 963static int snd_pcm_dev_unregister(struct snd_device *device)
943{ 964{
944 int idx, cidx, devtype; 965 int cidx, devtype;
945 struct snd_pcm_substream *substream; 966 struct snd_pcm_substream *substream;
946 struct list_head *list; 967 struct list_head *list;
947 struct snd_pcm *pcm = device->device_data; 968 struct snd_pcm *pcm = device->device_data;
948 969
949 snd_assert(pcm != NULL, return -ENXIO); 970 snd_assert(pcm != NULL, return -ENXIO);
950 down(&register_mutex); 971 down(&register_mutex);
951 idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; 972 list_del(&pcm->list);
952 snd_pcm_devices[idx] = NULL;
953 for (cidx = 0; cidx < 2; cidx++) { 973 for (cidx = 0; cidx < 2; cidx++) {
954 devtype = -1; 974 devtype = -1;
955 switch (cidx) { 975 switch (cidx) {
@@ -975,24 +995,19 @@ static int snd_pcm_dev_unregister(struct snd_device *device)
975 995
976int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 996int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
977{ 997{
978 int idx; 998 struct list_head *p;
979 999
980 snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); 1000 snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
981 down(&register_mutex); 1001 down(&register_mutex);
982 if (nfree) { 1002 if (nfree) {
983 list_del(&notify->list); 1003 list_del(&notify->list);
984 for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { 1004 list_for_each(p, &snd_pcm_devices)
985 if (snd_pcm_devices[idx] == NULL) 1005 notify->n_unregister(list_entry(p,
986 continue; 1006 struct snd_pcm, list));
987 notify->n_unregister(snd_pcm_devices[idx]);
988 }
989 } else { 1007 } else {
990 list_add_tail(&notify->list, &snd_pcm_notify_list); 1008 list_add_tail(&notify->list, &snd_pcm_notify_list);
991 for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { 1009 list_for_each(p, &snd_pcm_devices)
992 if (snd_pcm_devices[idx] == NULL) 1010 notify->n_register(list_entry(p, struct snd_pcm, list));
993 continue;
994 notify->n_register(snd_pcm_devices[idx]);
995 }
996 } 1011 }
997 up(&register_mutex); 1012 up(&register_mutex);
998 return 0; 1013 return 0;
@@ -1005,16 +1020,14 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
1005static void snd_pcm_proc_read(struct snd_info_entry *entry, 1020static void snd_pcm_proc_read(struct snd_info_entry *entry,
1006 struct snd_info_buffer *buffer) 1021 struct snd_info_buffer *buffer)
1007{ 1022{
1008 int idx; 1023 struct list_head *p;
1009 struct snd_pcm *pcm; 1024 struct snd_pcm *pcm;
1010 1025
1011 down(&register_mutex); 1026 down(&register_mutex);
1012 for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { 1027 list_for_each(p, &snd_pcm_devices) {
1013 pcm = snd_pcm_devices[idx]; 1028 pcm = list_entry(p, struct snd_pcm, list);
1014 if (pcm == NULL) 1029 snd_iprintf(buffer, "%02i-%02i: %s : %s",
1015 continue; 1030 pcm->card->number, pcm->device, pcm->id, pcm->name);
1016 snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES,
1017 idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name);
1018 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1031 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
1019 snd_iprintf(buffer, " : playback %i", 1032 snd_iprintf(buffer, " : playback %i",
1020 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 1033 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
@@ -1063,7 +1076,6 @@ static void __exit alsa_pcm_exit(void)
1063module_init(alsa_pcm_init) 1076module_init(alsa_pcm_init)
1064module_exit(alsa_pcm_exit) 1077module_exit(alsa_pcm_exit)
1065 1078
1066EXPORT_SYMBOL(snd_pcm_devices);
1067EXPORT_SYMBOL(snd_pcm_new); 1079EXPORT_SYMBOL(snd_pcm_new);
1068EXPORT_SYMBOL(snd_pcm_new_stream); 1080EXPORT_SYMBOL(snd_pcm_new_stream);
1069EXPORT_SYMBOL(snd_pcm_notify); 1081EXPORT_SYMBOL(snd_pcm_notify);