Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sound_oss.c
Go to the documentation of this file.
1 /*
2  * Advanced Linux Sound Architecture
3  * Copyright (c) by Jaroslav Kysela <[email protected]>
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 256
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 >= SNDRV_MINOR_OSS_DEVICES)
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 >= SNDRV_MINOR_OSS_DEVICES)
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  }
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 */