Merge branch 'linux-3.4.57' into rel-17
[linux-2.6.git] / sound / core / sound_oss.c
1 /*
2  *  Advanced Linux Sound Architecture
3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4  *
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19  *
20  */
21
22 #ifdef CONFIG_SND_OSSEMUL
23
24 #if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
25 #error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
26 #endif
27
28 #include <linux/init.h>
29 #include <linux/export.h>
30 #include <linux/slab.h>
31 #include <linux/time.h>
32 #include <sound/core.h>
33 #include <sound/minors.h>
34 #include <sound/info.h>
35 #include <linux/sound.h>
36 #include <linux/mutex.h>
37
38 #define SNDRV_OSS_MINORS 128
39
40 static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
41 static DEFINE_MUTEX(sound_oss_mutex);
42
43 /* NOTE: This function increments the refcount of the associated card like
44  * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
45  */
46 void *snd_lookup_oss_minor_data(unsigned int minor, int type)
47 {
48         struct snd_minor *mreg;
49         void *private_data;
50
51         if (minor >= ARRAY_SIZE(snd_oss_minors))
52                 return NULL;
53         mutex_lock(&sound_oss_mutex);
54         mreg = snd_oss_minors[minor];
55         if (mreg && mreg->type == type) {
56                 private_data = mreg->private_data;
57                 if (private_data && mreg->card_ptr)
58                         atomic_inc(&mreg->card_ptr->refcount);
59         } else
60                 private_data = NULL;
61         mutex_unlock(&sound_oss_mutex);
62         return private_data;
63 }
64
65 EXPORT_SYMBOL(snd_lookup_oss_minor_data);
66
67 static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
68 {
69         int minor;
70
71         switch (type) {
72         case SNDRV_OSS_DEVICE_TYPE_MIXER:
73                 if (snd_BUG_ON(!card || dev < 0 || dev > 1))
74                         return -EINVAL;
75                 minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
76                 break;
77         case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
78                 minor = SNDRV_MINOR_OSS_SEQUENCER;
79                 break;
80         case SNDRV_OSS_DEVICE_TYPE_MUSIC:
81                 minor = SNDRV_MINOR_OSS_MUSIC;
82                 break;
83         case SNDRV_OSS_DEVICE_TYPE_PCM:
84                 if (snd_BUG_ON(!card || dev < 0 || dev > 1))
85                         return -EINVAL;
86                 minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
87                 break;
88         case SNDRV_OSS_DEVICE_TYPE_MIDI:
89                 if (snd_BUG_ON(!card || dev < 0 || dev > 1))
90                         return -EINVAL;
91                 minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
92                 break;
93         case SNDRV_OSS_DEVICE_TYPE_DMFM:
94                 minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
95                 break;
96         case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
97                 minor = SNDRV_MINOR_OSS_SNDSTAT;
98                 break;
99         default:
100                 return -EINVAL;
101         }
102         if (minor < 0 || minor >= SNDRV_OSS_MINORS)
103                 return -EINVAL;
104         return minor;
105 }
106
107 int snd_register_oss_device(int type, struct snd_card *card, int dev,
108                             const struct file_operations *f_ops, void *private_data,
109                             const char *name)
110 {
111         int minor = snd_oss_kernel_minor(type, card, dev);
112         int minor_unit;
113         struct snd_minor *preg;
114         int cidx = SNDRV_MINOR_OSS_CARD(minor);
115         int track2 = -1;
116         int register1 = -1, register2 = -1;
117         struct device *carddev = snd_card_get_device_link(card);
118
119         if (card && card->number >= 8)
120                 return 0; /* ignore silently */
121         if (minor < 0)
122                 return minor;
123         preg = kmalloc(sizeof(struct snd_minor), GFP_KERNEL);
124         if (preg == NULL)
125                 return -ENOMEM;
126         preg->type = type;
127         preg->card = card ? card->number : -1;
128         preg->device = dev;
129         preg->f_ops = f_ops;
130         preg->private_data = private_data;
131         preg->card_ptr = card;
132         mutex_lock(&sound_oss_mutex);
133         snd_oss_minors[minor] = preg;
134         minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
135         switch (minor_unit) {
136         case SNDRV_MINOR_OSS_PCM:
137                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
138                 break;
139         case SNDRV_MINOR_OSS_MIDI:
140                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
141                 break;
142         case SNDRV_MINOR_OSS_MIDI1:
143                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
144                 break;
145         }
146         register1 = register_sound_special_device(f_ops, minor, carddev);
147         if (register1 != minor)
148                 goto __end;
149         if (track2 >= 0) {
150                 register2 = register_sound_special_device(f_ops, track2,
151                                                           carddev);
152                 if (register2 != track2)
153                         goto __end;
154                 snd_oss_minors[track2] = preg;
155         }
156         mutex_unlock(&sound_oss_mutex);
157         return 0;
158
159       __end:
160         if (register2 >= 0)
161                 unregister_sound_special(register2);
162         if (register1 >= 0)
163                 unregister_sound_special(register1);
164         snd_oss_minors[minor] = NULL;
165         mutex_unlock(&sound_oss_mutex);
166         kfree(preg);
167         return -EBUSY;
168 }
169
170 EXPORT_SYMBOL(snd_register_oss_device);
171
172 int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
173 {
174         int minor = snd_oss_kernel_minor(type, card, dev);
175         int cidx = SNDRV_MINOR_OSS_CARD(minor);
176         int track2 = -1;
177         struct snd_minor *mptr;
178
179         if (card && card->number >= 8)
180                 return 0;
181         if (minor < 0)
182                 return minor;
183         mutex_lock(&sound_oss_mutex);
184         mptr = snd_oss_minors[minor];
185         if (mptr == NULL) {
186                 mutex_unlock(&sound_oss_mutex);
187                 return -ENOENT;
188         }
189         unregister_sound_special(minor);
190         switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
191         case SNDRV_MINOR_OSS_PCM:
192                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
193                 break;
194         case SNDRV_MINOR_OSS_MIDI:
195                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
196                 break;
197         case SNDRV_MINOR_OSS_MIDI1:
198                 track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
199                 break;
200         }
201         if (track2 >= 0) {
202                 unregister_sound_special(track2);
203                 snd_oss_minors[track2] = NULL;
204         }
205         snd_oss_minors[minor] = NULL;
206         mutex_unlock(&sound_oss_mutex);
207         kfree(mptr);
208         return 0;
209 }
210
211 EXPORT_SYMBOL(snd_unregister_oss_device);
212
213 /*
214  *  INFO PART
215  */
216
217 #ifdef CONFIG_PROC_FS
218
219 static struct snd_info_entry *snd_minor_info_oss_entry;
220
221 static const char *snd_oss_device_type_name(int type)
222 {
223         switch (type) {
224         case SNDRV_OSS_DEVICE_TYPE_MIXER:
225                 return "mixer";
226         case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
227         case SNDRV_OSS_DEVICE_TYPE_MUSIC:
228                 return "sequencer";
229         case SNDRV_OSS_DEVICE_TYPE_PCM:
230                 return "digital audio";
231         case SNDRV_OSS_DEVICE_TYPE_MIDI:
232                 return "raw midi";
233         case SNDRV_OSS_DEVICE_TYPE_DMFM:
234                 return "hardware dependent";
235         default:
236                 return "?";
237         }
238 }
239
240 static void snd_minor_info_oss_read(struct snd_info_entry *entry,
241                                     struct snd_info_buffer *buffer)
242 {
243         int minor;
244         struct snd_minor *mptr;
245
246         mutex_lock(&sound_oss_mutex);
247         for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
248                 if (!(mptr = snd_oss_minors[minor]))
249                         continue;
250                 if (mptr->card >= 0)
251                         snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
252                                     mptr->card, mptr->device,
253                                     snd_oss_device_type_name(mptr->type));
254                 else
255                         snd_iprintf(buffer, "%3i:       : %s\n", minor,
256                                     snd_oss_device_type_name(mptr->type));
257         }
258         mutex_unlock(&sound_oss_mutex);
259 }
260
261
262 int __init snd_minor_info_oss_init(void)
263 {
264         struct snd_info_entry *entry;
265
266         entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
267         if (entry) {
268                 entry->c.text.read = snd_minor_info_oss_read;
269                 if (snd_info_register(entry) < 0) {
270                         snd_info_free_entry(entry);
271                         entry = NULL;
272                 }
273         }
274         snd_minor_info_oss_entry = entry;
275         return 0;
276 }
277
278 int __exit snd_minor_info_oss_done(void)
279 {
280         snd_info_free_entry(snd_minor_info_oss_entry);
281         return 0;
282 }
283 #endif /* CONFIG_PROC_FS */
284
285 #endif /* CONFIG_SND_OSSEMUL */