~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/sound_core.c

Version: ~ [ linux-5.10-rc1 ] ~ [ linux-5.9.1 ] ~ [ linux-5.8.16 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.72 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.152 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.202 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.240 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.240 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  *      Sound core.  This file is composed of two parts.  sound_class
  4  *      which is common to both OSS and ALSA and OSS sound core which
  5  *      is used OSS or emulation of it.
  6  */
  7 
  8 /*
  9  * First, the common part.
 10  */
 11 #include <linux/module.h>
 12 #include <linux/device.h>
 13 #include <linux/err.h>
 14 #include <linux/kdev_t.h>
 15 #include <linux/major.h>
 16 #include <sound/core.h>
 17 
 18 #ifdef CONFIG_SOUND_OSS_CORE
 19 static int __init init_oss_soundcore(void);
 20 static void cleanup_oss_soundcore(void);
 21 #else
 22 static inline int init_oss_soundcore(void)      { return 0; }
 23 static inline void cleanup_oss_soundcore(void)  { }
 24 #endif
 25 
 26 struct class *sound_class;
 27 EXPORT_SYMBOL(sound_class);
 28 
 29 MODULE_DESCRIPTION("Core sound module");
 30 MODULE_AUTHOR("Alan Cox");
 31 MODULE_LICENSE("GPL");
 32 
 33 static char *sound_devnode(struct device *dev, umode_t *mode)
 34 {
 35         if (MAJOR(dev->devt) == SOUND_MAJOR)
 36                 return NULL;
 37         return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
 38 }
 39 
 40 static int __init init_soundcore(void)
 41 {
 42         int rc;
 43 
 44         rc = init_oss_soundcore();
 45         if (rc)
 46                 return rc;
 47 
 48         sound_class = class_create(THIS_MODULE, "sound");
 49         if (IS_ERR(sound_class)) {
 50                 cleanup_oss_soundcore();
 51                 return PTR_ERR(sound_class);
 52         }
 53 
 54         sound_class->devnode = sound_devnode;
 55 
 56         return 0;
 57 }
 58 
 59 static void __exit cleanup_soundcore(void)
 60 {
 61         cleanup_oss_soundcore();
 62         class_destroy(sound_class);
 63 }
 64 
 65 subsys_initcall(init_soundcore);
 66 module_exit(cleanup_soundcore);
 67 
 68 
 69 #ifdef CONFIG_SOUND_OSS_CORE
 70 /*
 71  *      OSS sound core handling. Breaks out sound functions to submodules
 72  *      
 73  *      Author:         Alan Cox <alan@lxorguk.ukuu.org.uk>
 74  *
 75  *      Fixes:
 76  *
 77  *                         --------------------
 78  * 
 79  *      Top level handler for the sound subsystem. Various devices can
 80  *      plug into this. The fact they don't all go via OSS doesn't mean 
 81  *      they don't have to implement the OSS API. There is a lot of logic
 82  *      to keeping much of the OSS weight out of the code in a compatibility
 83  *      module, but it's up to the driver to rember to load it...
 84  *
 85  *      The code provides a set of functions for registration of devices
 86  *      by type. This is done rather than providing a single call so that
 87  *      we can hide any future changes in the internals (eg when we go to
 88  *      32bit dev_t) from the modules and their interface.
 89  *
 90  *      Secondly we need to allocate the dsp, dsp16 and audio devices as
 91  *      one. Thus we misuse the chains a bit to simplify this.
 92  *
 93  *      Thirdly to make it more fun and for 2.3.x and above we do all
 94  *      of this using fine grained locking.
 95  *
 96  *      FIXME: we have to resolve modules and fine grained load/unload
 97  *      locking at some point in 2.3.x.
 98  */
 99 
100 #include <linux/init.h>
101 #include <linux/slab.h>
102 #include <linux/types.h>
103 #include <linux/kernel.h>
104 #include <linux/sound.h>
105 #include <linux/kmod.h>
106 
107 #define SOUND_STEP 16
108 
109 struct sound_unit
110 {
111         int unit_minor;
112         const struct file_operations *unit_fops;
113         struct sound_unit *next;
114         char name[32];
115 };
116 
117 /*
118  * By default, OSS sound_core claims full legacy minor range (0-255)
119  * of SOUND_MAJOR to trap open attempts to any sound minor and
120  * requests modules using custom sound-slot/service-* module aliases.
121  * The only benefit of doing this is allowing use of custom module
122  * aliases instead of the standard char-major-* ones.  This behavior
123  * prevents alternative OSS implementation and is scheduled to be
124  * removed.
125  *
126  * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel
127  * parameter are added to allow distros and developers to try and
128  * switch to alternative implementations without needing to rebuild
129  * the kernel in the meantime.  If preclaim_oss is non-zero, the
130  * kernel will behave the same as before.  All SOUND_MAJOR minors are
131  * preclaimed and the custom module aliases along with standard chrdev
132  * ones are emitted if a missing device is opened.  If preclaim_oss is
133  * zero, sound_core only grabs what's actually in use and for missing
134  * devices only the standard chrdev aliases are requested.
135  *
136  * All these clutters are scheduled to be removed along with
137  * sound-slot/service-* module aliases.
138  */
139 #ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM
140 static int preclaim_oss = 1;
141 #else
142 static int preclaim_oss = 0;
143 #endif
144 
145 module_param(preclaim_oss, int, 0444);
146 
147 static int soundcore_open(struct inode *, struct file *);
148 
149 static const struct file_operations soundcore_fops =
150 {
151         /* We must have an owner or the module locking fails */
152         .owner  = THIS_MODULE,
153         .open   = soundcore_open,
154         .llseek = noop_llseek,
155 };
156 
157 /*
158  *      Low level list operator. Scan the ordered list, find a hole and
159  *      join into it. Called with the lock asserted
160  */
161 
162 static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top)
163 {
164         int n=low;
165 
166         if (index < 0) {        /* first free */
167 
168                 while (*list && (*list)->unit_minor<n)
169                         list=&((*list)->next);
170 
171                 while(n<top)
172                 {
173                         /* Found a hole ? */
174                         if(*list==NULL || (*list)->unit_minor>n)
175                                 break;
176                         list=&((*list)->next);
177                         n+=SOUND_STEP;
178                 }
179 
180                 if(n>=top)
181                         return -ENOENT;
182         } else {
183                 n = low+(index*16);
184                 while (*list) {
185                         if ((*list)->unit_minor==n)
186                                 return -EBUSY;
187                         if ((*list)->unit_minor>n)
188                                 break;
189                         list=&((*list)->next);
190                 }
191         }       
192                 
193         /*
194          *      Fill it in
195          */
196          
197         s->unit_minor=n;
198         s->unit_fops=fops;
199         
200         /*
201          *      Link it
202          */
203          
204         s->next=*list;
205         *list=s;
206         
207         
208         return n;
209 }
210 
211 /*
212  *      Remove a node from the chain. Called with the lock asserted
213  */
214  
215 static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)
216 {
217         while(*list)
218         {
219                 struct sound_unit *p=*list;
220                 if(p->unit_minor==unit)
221                 {
222                         *list=p->next;
223                         return p;
224                 }
225                 list=&(p->next);
226         }
227         printk(KERN_ERR "Sound device %d went missing!\n", unit);
228         return NULL;
229 }
230 
231 /*
232  *      This lock guards the sound loader list.
233  */
234 
235 static DEFINE_SPINLOCK(sound_loader_lock);
236 
237 /*
238  *      Allocate the controlling structure and add it to the sound driver
239  *      list. Acquires locks as needed
240  */
241 
242 static int sound_insert_unit(struct sound_unit **list, const struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode, struct device *dev)
243 {
244         struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);
245         int r;
246 
247         if (!s)
248                 return -ENOMEM;
249 
250         spin_lock(&sound_loader_lock);
251 retry:
252         r = __sound_insert_unit(s, list, fops, index, low, top);
253         spin_unlock(&sound_loader_lock);
254         
255         if (r < 0)
256                 goto fail;
257         else if (r < SOUND_STEP)
258                 sprintf(s->name, "sound/%s", name);
259         else
260                 sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
261 
262         if (!preclaim_oss) {
263                 /*
264                  * Something else might have grabbed the minor.  If
265                  * first free slot is requested, rescan with @low set
266                  * to the next unit; otherwise, -EBUSY.
267                  */
268                 r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,
269                                       &soundcore_fops);
270                 if (r < 0) {
271                         spin_lock(&sound_loader_lock);
272                         __sound_remove_unit(list, s->unit_minor);
273                         if (index < 0) {
274                                 low = s->unit_minor + SOUND_STEP;
275                                 goto retry;
276                         }
277                         spin_unlock(&sound_loader_lock);
278                         r = -EBUSY;
279                         goto fail;
280                 }
281         }
282 
283         device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
284                       NULL, "%s", s->name+6);
285         return s->unit_minor;
286 
287 fail:
288         kfree(s);
289         return r;
290 }
291 
292 /*
293  *      Remove a unit. Acquires locks as needed. The drivers MUST have
294  *      completed the removal before their file operations become
295  *      invalid.
296  */
297         
298 static void sound_remove_unit(struct sound_unit **list, int unit)
299 {
300         struct sound_unit *p;
301 
302         spin_lock(&sound_loader_lock);
303         p = __sound_remove_unit(list, unit);
304         spin_unlock(&sound_loader_lock);
305         if (p) {
306                 if (!preclaim_oss)
307                         __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,
308                                             p->name);
309                 device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
310                 kfree(p);
311         }
312 }
313 
314 /*
315  *      Allocations
316  *
317  *      0       *16             Mixers
318  *      1       *8              Sequencers
319  *      2       *16             Midi
320  *      3       *16             DSP
321  *      4       *16             SunDSP
322  *      5       *16             DSP16
323  *      6       --              sndstat (obsolete)
324  *      7       *16             unused
325  *      8       --              alternate sequencer (see above)
326  *      9       *16             raw synthesizer access
327  *      10      *16             unused
328  *      11      *16             unused
329  *      12      *16             unused
330  *      13      *16             unused
331  *      14      *16             unused
332  *      15      *16             unused
333  */
334 
335 static struct sound_unit *chains[SOUND_STEP];
336 
337 /**
338  *      register_sound_special_device - register a special sound node
339  *      @fops: File operations for the driver
340  *      @unit: Unit number to allocate
341  *      @dev: device pointer
342  *
343  *      Allocate a special sound device by minor number from the sound
344  *      subsystem.
345  *
346  *      Return: The allocated number is returned on success. On failure,
347  *      a negative error code is returned.
348  */
349  
350 int register_sound_special_device(const struct file_operations *fops, int unit,
351                                   struct device *dev)
352 {
353         const int chain = unit % SOUND_STEP;
354         int max_unit = 256;
355         const char *name;
356         char _name[16];
357 
358         switch (chain) {
359             case 0:
360                 name = "mixer";
361                 break;
362             case 1:
363                 name = "sequencer";
364                 if (unit >= SOUND_STEP)
365                         goto __unknown;
366                 max_unit = unit + 1;
367                 break;
368             case 2:
369                 name = "midi";
370                 break;
371             case 3:
372                 name = "dsp";
373                 break;
374             case 4:
375                 name = "audio";
376                 break;
377             case 5:
378                 name = "dspW";
379                 break;
380             case 8:
381                 name = "sequencer2";
382                 if (unit >= SOUND_STEP)
383                         goto __unknown;
384                 max_unit = unit + 1;
385                 break;
386             case 9:
387                 name = "dmmidi";
388                 break;
389             case 10:
390                 name = "dmfm";
391                 break;
392             case 12:
393                 name = "adsp";
394                 break;
395             case 13:
396                 name = "amidi";
397                 break;
398             case 14:
399                 name = "admmidi";
400                 break;
401             default:
402                 {
403                     __unknown:
404                         sprintf(_name, "unknown%d", chain);
405                         if (unit >= SOUND_STEP)
406                                 strcat(_name, "-");
407                         name = _name;
408                 }
409                 break;
410         }
411         return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
412                                  name, 0600, dev);
413 }
414  
415 EXPORT_SYMBOL(register_sound_special_device);
416 
417 int register_sound_special(const struct file_operations *fops, int unit)
418 {
419         return register_sound_special_device(fops, unit, NULL);
420 }
421 
422 EXPORT_SYMBOL(register_sound_special);
423 
424 /**
425  *      register_sound_mixer - register a mixer device
426  *      @fops: File operations for the driver
427  *      @dev: Unit number to allocate
428  *
429  *      Allocate a mixer device. Unit is the number of the mixer requested.
430  *      Pass -1 to request the next free mixer unit.
431  *
432  *      Return: On success, the allocated number is returned. On failure,
433  *      a negative error code is returned.
434  */
435 
436 int register_sound_mixer(const struct file_operations *fops, int dev)
437 {
438         return sound_insert_unit(&chains[0], fops, dev, 0, 128,
439                                  "mixer", 0600, NULL);
440 }
441 
442 EXPORT_SYMBOL(register_sound_mixer);
443 
444 /*
445  *      DSP's are registered as a triple. Register only one and cheat
446  *      in open - see below.
447  */
448  
449 /**
450  *      register_sound_dsp - register a DSP device
451  *      @fops: File operations for the driver
452  *      @dev: Unit number to allocate
453  *
454  *      Allocate a DSP device. Unit is the number of the DSP requested.
455  *      Pass -1 to request the next free DSP unit.
456  *
457  *      This function allocates both the audio and dsp device entries together
458  *      and will always allocate them as a matching pair - eg dsp3/audio3
459  *
460  *      Return: On success, the allocated number is returned. On failure,
461  *      a negative error code is returned.
462  */
463 
464 int register_sound_dsp(const struct file_operations *fops, int dev)
465 {
466         return sound_insert_unit(&chains[3], fops, dev, 3, 131,
467                                  "dsp", 0600, NULL);
468 }
469 
470 EXPORT_SYMBOL(register_sound_dsp);
471 
472 /**
473  *      unregister_sound_special - unregister a special sound device
474  *      @unit: unit number to allocate
475  *
476  *      Release a sound device that was allocated with
477  *      register_sound_special(). The unit passed is the return value from
478  *      the register function.
479  */
480 
481 
482 void unregister_sound_special(int unit)
483 {
484         sound_remove_unit(&chains[unit % SOUND_STEP], unit);
485 }
486  
487 EXPORT_SYMBOL(unregister_sound_special);
488 
489 /**
490  *      unregister_sound_mixer - unregister a mixer
491  *      @unit: unit number to allocate
492  *
493  *      Release a sound device that was allocated with register_sound_mixer().
494  *      The unit passed is the return value from the register function.
495  */
496 
497 void unregister_sound_mixer(int unit)
498 {
499         sound_remove_unit(&chains[0], unit);
500 }
501 
502 EXPORT_SYMBOL(unregister_sound_mixer);
503 
504 /**
505  *      unregister_sound_dsp - unregister a DSP device
506  *      @unit: unit number to allocate
507  *
508  *      Release a sound device that was allocated with register_sound_dsp().
509  *      The unit passed is the return value from the register function.
510  *
511  *      Both of the allocated units are released together automatically.
512  */
513 
514 void unregister_sound_dsp(int unit)
515 {
516         sound_remove_unit(&chains[3], unit);
517 }
518 
519 
520 EXPORT_SYMBOL(unregister_sound_dsp);
521 
522 static struct sound_unit *__look_for_unit(int chain, int unit)
523 {
524         struct sound_unit *s;
525         
526         s=chains[chain];
527         while(s && s->unit_minor <= unit)
528         {
529                 if(s->unit_minor==unit)
530                         return s;
531                 s=s->next;
532         }
533         return NULL;
534 }
535 
536 static int soundcore_open(struct inode *inode, struct file *file)
537 {
538         int chain;
539         int unit = iminor(inode);
540         struct sound_unit *s;
541         const struct file_operations *new_fops = NULL;
542 
543         chain=unit&0x0F;
544         if(chain==4 || chain==5)        /* dsp/audio/dsp16 */
545         {
546                 unit&=0xF0;
547                 unit|=3;
548                 chain=3;
549         }
550         
551         spin_lock(&sound_loader_lock);
552         s = __look_for_unit(chain, unit);
553         if (s)
554                 new_fops = fops_get(s->unit_fops);
555         if (preclaim_oss && !new_fops) {
556                 spin_unlock(&sound_loader_lock);
557 
558                 /*
559                  *  Please, don't change this order or code.
560                  *  For ALSA slot means soundcard and OSS emulation code
561                  *  comes as add-on modules which aren't depend on
562                  *  ALSA toplevel modules for soundcards, thus we need
563                  *  load them at first.   [Jaroslav Kysela <perex@jcu.cz>]
564                  */
565                 request_module("sound-slot-%i", unit>>4);
566                 request_module("sound-service-%i-%i", unit>>4, chain);
567 
568                 /*
569                  * sound-slot/service-* module aliases are scheduled
570                  * for removal in favor of the standard char-major-*
571                  * module aliases.  For the time being, generate both
572                  * the legacy and standard module aliases to ease
573                  * transition.
574                  */
575                 if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)
576                         request_module("char-major-%d", SOUND_MAJOR);
577 
578                 spin_lock(&sound_loader_lock);
579                 s = __look_for_unit(chain, unit);
580                 if (s)
581                         new_fops = fops_get(s->unit_fops);
582         }
583         spin_unlock(&sound_loader_lock);
584         if (new_fops) {
585                 /*
586                  * We rely upon the fact that we can't be unloaded while the
587                  * subdriver is there.
588                  */
589                 int err = 0;
590                 replace_fops(file, new_fops);
591 
592                 if (file->f_op->open)
593                         err = file->f_op->open(inode,file);
594 
595                 return err;
596         }
597         return -ENODEV;
598 }
599 
600 MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
601 
602 static void cleanup_oss_soundcore(void)
603 {
604         /* We have nothing to really do here - we know the lists must be
605            empty */
606         unregister_chrdev(SOUND_MAJOR, "sound");
607 }
608 
609 static int __init init_oss_soundcore(void)
610 {
611         if (preclaim_oss &&
612             register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) {
613                 printk(KERN_ERR "soundcore: sound device already in use.\n");
614                 return -EBUSY;
615         }
616 
617         return 0;
618 }
619 
620 #endif /* CONFIG_SOUND_OSS_CORE */
621 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp