1 /*-
  2  * Copyright (c) 2007 Ariff Abdullah <ariff@FreeBSD.org>
  3  * All rights reserved.
  4  *
  5  * Redistribution and use in source and binary forms, with or without
  6  * modification, are permitted provided that the following conditions
  7  * are met:
  8  * 1. Redistributions of source code must retain the above copyright
  9  *    notice, this list of conditions and the following disclaimer.
 10  * 2. Redistributions in binary form must reproduce the above copyright
 11  *    notice, this list of conditions and the following disclaimer in the
 12  *    documentation and/or other materials provided with the distribution.
 13  *
 14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 24  * SUCH DAMAGE.
 25  *
 26  * $FreeBSD$
 27  */
 28 
 29 #include <sys/param.h>
 30 #include <sys/systm.h>
 31 
 32 #include <dev/sound/unit.h>
 33 
 34 /*
 35  * Unit magic allocator for sound driver.
 36  *
 37  * 'u' = Unit of attached soundcards
 38  * 'd' = Device type
 39  * 'c' = Channel number
 40  *
 41  * eg: dsp0.p1  - u=0, d=p, c=1
 42  *     dsp1.vp0 - u=1, d=vp, c=0
 43  *     dsp0.10  - u=0, d=clone, c=allocated clone (see further explanation)
 44  *
 45  * Maximum unit of soundcards can be tuned through "hw.snd.maxunit", which
 46  * is between SND_UNIT_UMIN (16) and SND_UNIT_UMAX (2048). By design,
 47  * maximum allowable allocated channel is 256, with exception for clone
 48  * devices which doesn't have any notion of channel numbering. The use of
 49  * channel numbering in a clone device is simply to provide uniqueness among
 50  * allocated clones. This also means that the maximum allowable clonable
 51  * device is largely dependant and dynamically tuned depending on
 52  * hw.snd.maxunit.
 53  */
 54 
 55 /* Default width */
 56 static int snd_u_shift = 9;     /* 0 - 0x1ff :  512 distinct soundcards   */
 57 static int snd_d_shift = 5;     /* 0 - 0x1f  :   32 distinct device types */
 58 static int snd_c_shift = 10;    /* 0 - 0x3ff : 1024 distinct channels
 59                                                (256 limit "by design",
 60                                                except for clone devices)  */
 61 
 62 static int snd_unit_initialized = 0;
 63 
 64 #ifdef SND_DIAGNOSTIC
 65 #define SND_UNIT_ASSERT()       do {                                  \
 66         if (snd_unit_initialized == 0)                                 \
 67                 panic("%s(): Uninitialized sound unit!", __func__);   \
 68 } while(0)
 69 #else
 70 #if defined(INVARIANTS) || defined(SND_DEBUG)
 71 #define SND_UNIT_ASSERT()       KASSERT(snd_unit_initialized != 0,    \
 72                                 ("%s(): Uninitialized sound unit!", \
 73                                 __func__))
 74 #else
 75 #define SND_UNIT_ASSERT()
 76 #endif
 77 #endif
 78 
 79 #define MKMASK(x)       ((1 << snd_##x##_shift) - 1)
 80 
 81 int
 82 snd_max_u(void)
 83 {
 84         SND_UNIT_ASSERT();
 85 
 86         return (MKMASK(u));
 87 }
 88 
 89 int
 90 snd_max_d(void)
 91 {
 92         SND_UNIT_ASSERT();
 93 
 94         return (MKMASK(d));
 95 }
 96 
 97 int
 98 snd_max_c(void)
 99 {
100         SND_UNIT_ASSERT();
101 
102         return (MKMASK(c));
103 }
104 
105 int
106 snd_unit2u(int unit)
107 {
108         SND_UNIT_ASSERT();
109 
110         return ((unit >> (snd_c_shift + snd_d_shift)) & MKMASK(u));
111 }
112 
113 int
114 snd_unit2d(int unit)
115 {
116         SND_UNIT_ASSERT();
117 
118         return ((unit >> snd_c_shift) & MKMASK(d));
119 }
120 
121 int
122 snd_unit2c(int unit)
123 {
124         SND_UNIT_ASSERT();
125 
126         return (unit & MKMASK(c));
127 }
128 
129 int
130 snd_u2unit(int u)
131 {
132         SND_UNIT_ASSERT();
133 
134         return ((u & MKMASK(u)) << (snd_c_shift + snd_d_shift));
135 }
136 
137 int
138 snd_d2unit(int d)
139 {
140         SND_UNIT_ASSERT();
141 
142         return ((d & MKMASK(d)) << snd_c_shift);
143 }
144 
145 int
146 snd_c2unit(int c)
147 {
148         SND_UNIT_ASSERT();
149 
150         return (c & MKMASK(c));
151 }
152 
153 int
154 snd_mkunit(int u, int d, int c)
155 {
156         SND_UNIT_ASSERT();
157 
158         return ((c & MKMASK(c)) | ((d & MKMASK(d)) << snd_c_shift) |
159             ((u & MKMASK(u)) << (snd_c_shift + snd_d_shift)));
160 }
161 
162 /*
163  * This *must* be called first before any of the functions above!!!
164  */
165 void
166 snd_unit_init(void)
167 {
168         int i;
169 
170         if (snd_unit_initialized != 0)
171                 return;
172 
173         snd_unit_initialized = 1;
174 
175         if (getenv_int("hw.snd.maxunit", &i) != 0) {
176                 if (i < SND_UNIT_UMIN)
177                         i = SND_UNIT_UMIN;
178                 else if (i > SND_UNIT_UMAX)
179                         i = SND_UNIT_UMAX;
180                 else
181                         i = roundup2(i, 2);
182 
183                 for (snd_u_shift = 0; (i >> (snd_u_shift + 1)) != 0;
184                     snd_u_shift++)
185                         ;
186 
187                 /*
188                  * Make room for channels/clones allocation unit
189                  * to fit within 24bit MAXMINOR limit.
190                  */
191                 snd_c_shift = 24 - snd_u_shift - snd_d_shift;
192         }
193 }