diff options
Diffstat (limited to 'sound/core/pcm.c')
-rw-r--r-- | sound/core/pcm.c | 118 |
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 | |||
33 | MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); | 33 | MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); |
34 | MODULE_LICENSE("GPL"); | 34 | MODULE_LICENSE("GPL"); |
35 | 35 | ||
36 | struct snd_pcm *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; | 36 | static LIST_HEAD(snd_pcm_devices); |
37 | static LIST_HEAD(snd_pcm_notify_list); | 37 | static LIST_HEAD(snd_pcm_notify_list); |
38 | static DECLARE_MUTEX(register_mutex); | 38 | static DECLARE_MUTEX(register_mutex); |
39 | 39 | ||
@@ -43,13 +43,23 @@ static int snd_pcm_dev_register(struct snd_device *device); | |||
43 | static int snd_pcm_dev_disconnect(struct snd_device *device); | 43 | static int snd_pcm_dev_disconnect(struct snd_device *device); |
44 | static int snd_pcm_dev_unregister(struct snd_device *device); | 44 | static int snd_pcm_dev_unregister(struct snd_device *device); |
45 | 45 | ||
46 | static 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 | |||
46 | static int snd_pcm_control_ioctl(struct snd_card *card, | 59 | static 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(®ister_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(®ister_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(®ister_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(®ister_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 | ||
866 | static int snd_pcm_dev_register(struct snd_device *device) | 891 | static 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(®ister_mutex); | 900 | down(®ister_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(®ister_mutex); | 902 | up(®ister_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(®ister_mutex); | 926 | up(®ister_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(®ister_mutex); | 948 | down(®ister_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 | ||
942 | static int snd_pcm_dev_unregister(struct snd_device *device) | 963 | static 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(®ister_mutex); | 971 | down(®ister_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 | ||
976 | int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) | 996 | int 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(®ister_mutex); | 1001 | down(®ister_mutex); |
982 | if (nfree) { | 1002 | if (nfree) { |
983 | list_del(¬ify->list); | 1003 | list_del(¬ify->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(¬ify->list, &snd_pcm_notify_list); | 1008 | list_add_tail(¬ify->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(®ister_mutex); | 1012 | up(®ister_mutex); |
998 | return 0; | 1013 | return 0; |
@@ -1005,16 +1020,14 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) | |||
1005 | static void snd_pcm_proc_read(struct snd_info_entry *entry, | 1020 | static 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(®ister_mutex); | 1026 | down(®ister_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) | |||
1063 | module_init(alsa_pcm_init) | 1076 | module_init(alsa_pcm_init) |
1064 | module_exit(alsa_pcm_exit) | 1077 | module_exit(alsa_pcm_exit) |
1065 | 1078 | ||
1066 | EXPORT_SYMBOL(snd_pcm_devices); | ||
1067 | EXPORT_SYMBOL(snd_pcm_new); | 1079 | EXPORT_SYMBOL(snd_pcm_new); |
1068 | EXPORT_SYMBOL(snd_pcm_new_stream); | 1080 | EXPORT_SYMBOL(snd_pcm_new_stream); |
1069 | EXPORT_SYMBOL(snd_pcm_notify); | 1081 | EXPORT_SYMBOL(snd_pcm_notify); |