Deleted Added
full compact
mixer.c (70680) mixer.c (70944)
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
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 *
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
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: head/sys/dev/sound/pcm/mixer.c 70680 2001-01-05 07:07:03Z jhb $
26 * $FreeBSD: head/sys/dev/sound/pcm/mixer.c 70944 2001-01-11 23:26:16Z jhb $
27 */
28
29#include <dev/sound/pcm/sound.h>
30
31#include "mixer_if.h"
32
33MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34
35static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
36 [SOUND_MIXER_VOLUME] = 75,
37 [SOUND_MIXER_BASS] = 50,
38 [SOUND_MIXER_TREBLE] = 50,
39 [SOUND_MIXER_SYNTH] = 75,
40 [SOUND_MIXER_PCM] = 75,
41 [SOUND_MIXER_SPEAKER] = 75,
42 [SOUND_MIXER_LINE] = 75,
43 [SOUND_MIXER_MIC] = 0,
44 [SOUND_MIXER_CD] = 75,
45 [SOUND_MIXER_LINE1] = 75,
46 [SOUND_MIXER_VIDEO] = 75,
47 [SOUND_MIXER_RECLEV] = 0,
48 [SOUND_MIXER_OGAIN] = 50,
49};
50
51static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
52
53static int
54mixer_lookup(char *devname)
55{
56 int i;
57
58 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
59 if (strncmp(devname, snd_mixernames[i],
60 strlen(snd_mixernames[i])) == 0)
61 return i;
62 return -1;
63}
64
65static int
66mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev)
67{
68 unsigned l, r;
69 int v;
70
71 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
72 return -1;
73
74 l = min((lev & 0x00ff), 100);
75 r = min(((lev & 0xff00) >> 8), 100);
76
77 v = MIXER_SET(mixer, dev, l, r);
78 if (v < 0)
79 return -1;
80
81 mixer->level[dev] = l | (r << 8);
82 return 0;
83}
84
85static int
86mixer_get(snd_mixer *mixer, int dev)
87{
88 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
89 return mixer->level[dev];
90 else return -1;
91}
92
93static int
94mixer_setrecsrc(snd_mixer *mixer, u_int32_t src)
95{
96 src &= mixer->recdevs;
97 if (src == 0)
98 src = SOUND_MASK_MIC;
99 mixer->recsrc = MIXER_SETRECSRC(mixer, src);
100 return 0;
101}
102
103static int
104mixer_getrecsrc(snd_mixer *mixer)
105{
106 return mixer->recsrc;
107}
108
109void
110mix_setdevs(snd_mixer *m, u_int32_t v)
111{
112 m->devs = v;
113}
114
115void
116mix_setrecdevs(snd_mixer *m, u_int32_t v)
117{
118 m->recdevs = v;
119}
120
121u_int32_t
122mix_getdevs(snd_mixer *m)
123{
124 return m->devs;
125}
126
127u_int32_t
128mix_getrecdevs(snd_mixer *m)
129{
130 return m->recdevs;
131}
132
133void *
134mix_getdevinfo(snd_mixer *m)
135{
136 return m->devinfo;
137}
138
139int
140mixer_busy(snd_mixer *m, int busy)
141{
142 m->busy = busy;
143 return 0;
144}
145
146int
147mixer_isbusy(snd_mixer *m)
148{
149 return m->busy;
150}
151
152int
153mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
154{
155 snddev_info *d;
156 snd_mixer *m;
157 u_int16_t v;
158 int i;
159
160 d = device_get_softc(dev);
161 m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
162
163 m->name = cls->name;
164 m->devinfo = devinfo;
165
166 if (MIXER_INIT(m))
167 goto bad;
168
169 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
170 v = snd_mixerdefaults[i];
171 mixer_set(m, i, v | (v << 8));
172 }
173
174 mixer_setrecsrc(m, SOUND_MASK_MIC);
175
176 d->mixer = m;
177
178 return 0;
179
180bad: kobj_delete((kobj_t)m, M_MIXER);
181 return -1;
182}
183
184int
185mixer_uninit(device_t dev)
186{
187 int i;
188 snddev_info *d;
189 snd_mixer *m;
190
191 d = device_get_softc(dev);
192 m = d->mixer;
193
194 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
195 mixer_set(m, i, 0);
196
197 mixer_setrecsrc(m, SOUND_MASK_MIC);
198
199 MIXER_UNINIT(m);
200
201 kobj_delete((kobj_t)m, M_MIXER);
202 d->mixer = NULL;
203
204 return 0;
205}
206
207int
208mixer_reinit(device_t dev)
209{
210 int i;
211 snddev_info *d;
212 snd_mixer *m;
213
214 d = device_get_softc(dev);
215 m = d->mixer;
216
217 i = MIXER_REINIT(m);
218 if (i)
219 return i;
220
221 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
222 mixer_set(m, i, m->level[i]);
223
224 mixer_setrecsrc(m, m->recsrc);
225
226 return 0;
227}
228
229int
230mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
231{
232 int ret, *arg_i = (int *)arg;
233 int v = -1, j = cmd & 0xff;
234 snd_mixer *m;
235
236 m = d->mixer;
237
238 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
239 if (j == SOUND_MIXER_RECSRC)
240 ret = mixer_setrecsrc(m, *arg_i);
241 else
242 ret = mixer_set(m, j, *arg_i);
243 return (ret == 0)? 0 : ENXIO;
244 }
245
246 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
247 switch (j) {
248 case SOUND_MIXER_DEVMASK:
249 case SOUND_MIXER_CAPS:
250 case SOUND_MIXER_STEREODEVS:
251 v = mix_getdevs(m);
252 break;
253
254 case SOUND_MIXER_RECMASK:
255 v = mix_getrecdevs(m);
256 break;
257
258 case SOUND_MIXER_RECSRC:
259 v = mixer_getrecsrc(m);
260 break;
261
262 default:
263 v = mixer_get(m, j);
264 }
265 *arg_i = v;
266 return (v != -1)? 0 : ENXIO;
267 }
268 return ENXIO;
269}
270
271static int
272sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
273{
274 char devname[32];
275 int error, dev;
276 snd_mixer *m;
277
278 m = oidp->oid_arg1;
279 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
280 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
281 if (error == 0 && req->newptr != NULL) {
282 dev = mixer_lookup(devname);
283 if (dev == -1)
284 return EINVAL;
27 */
28
29#include <dev/sound/pcm/sound.h>
30
31#include "mixer_if.h"
32
33MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34
35static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
36 [SOUND_MIXER_VOLUME] = 75,
37 [SOUND_MIXER_BASS] = 50,
38 [SOUND_MIXER_TREBLE] = 50,
39 [SOUND_MIXER_SYNTH] = 75,
40 [SOUND_MIXER_PCM] = 75,
41 [SOUND_MIXER_SPEAKER] = 75,
42 [SOUND_MIXER_LINE] = 75,
43 [SOUND_MIXER_MIC] = 0,
44 [SOUND_MIXER_CD] = 75,
45 [SOUND_MIXER_LINE1] = 75,
46 [SOUND_MIXER_VIDEO] = 75,
47 [SOUND_MIXER_RECLEV] = 0,
48 [SOUND_MIXER_OGAIN] = 50,
49};
50
51static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
52
53static int
54mixer_lookup(char *devname)
55{
56 int i;
57
58 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
59 if (strncmp(devname, snd_mixernames[i],
60 strlen(snd_mixernames[i])) == 0)
61 return i;
62 return -1;
63}
64
65static int
66mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev)
67{
68 unsigned l, r;
69 int v;
70
71 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
72 return -1;
73
74 l = min((lev & 0x00ff), 100);
75 r = min(((lev & 0xff00) >> 8), 100);
76
77 v = MIXER_SET(mixer, dev, l, r);
78 if (v < 0)
79 return -1;
80
81 mixer->level[dev] = l | (r << 8);
82 return 0;
83}
84
85static int
86mixer_get(snd_mixer *mixer, int dev)
87{
88 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
89 return mixer->level[dev];
90 else return -1;
91}
92
93static int
94mixer_setrecsrc(snd_mixer *mixer, u_int32_t src)
95{
96 src &= mixer->recdevs;
97 if (src == 0)
98 src = SOUND_MASK_MIC;
99 mixer->recsrc = MIXER_SETRECSRC(mixer, src);
100 return 0;
101}
102
103static int
104mixer_getrecsrc(snd_mixer *mixer)
105{
106 return mixer->recsrc;
107}
108
109void
110mix_setdevs(snd_mixer *m, u_int32_t v)
111{
112 m->devs = v;
113}
114
115void
116mix_setrecdevs(snd_mixer *m, u_int32_t v)
117{
118 m->recdevs = v;
119}
120
121u_int32_t
122mix_getdevs(snd_mixer *m)
123{
124 return m->devs;
125}
126
127u_int32_t
128mix_getrecdevs(snd_mixer *m)
129{
130 return m->recdevs;
131}
132
133void *
134mix_getdevinfo(snd_mixer *m)
135{
136 return m->devinfo;
137}
138
139int
140mixer_busy(snd_mixer *m, int busy)
141{
142 m->busy = busy;
143 return 0;
144}
145
146int
147mixer_isbusy(snd_mixer *m)
148{
149 return m->busy;
150}
151
152int
153mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
154{
155 snddev_info *d;
156 snd_mixer *m;
157 u_int16_t v;
158 int i;
159
160 d = device_get_softc(dev);
161 m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
162
163 m->name = cls->name;
164 m->devinfo = devinfo;
165
166 if (MIXER_INIT(m))
167 goto bad;
168
169 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
170 v = snd_mixerdefaults[i];
171 mixer_set(m, i, v | (v << 8));
172 }
173
174 mixer_setrecsrc(m, SOUND_MASK_MIC);
175
176 d->mixer = m;
177
178 return 0;
179
180bad: kobj_delete((kobj_t)m, M_MIXER);
181 return -1;
182}
183
184int
185mixer_uninit(device_t dev)
186{
187 int i;
188 snddev_info *d;
189 snd_mixer *m;
190
191 d = device_get_softc(dev);
192 m = d->mixer;
193
194 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
195 mixer_set(m, i, 0);
196
197 mixer_setrecsrc(m, SOUND_MASK_MIC);
198
199 MIXER_UNINIT(m);
200
201 kobj_delete((kobj_t)m, M_MIXER);
202 d->mixer = NULL;
203
204 return 0;
205}
206
207int
208mixer_reinit(device_t dev)
209{
210 int i;
211 snddev_info *d;
212 snd_mixer *m;
213
214 d = device_get_softc(dev);
215 m = d->mixer;
216
217 i = MIXER_REINIT(m);
218 if (i)
219 return i;
220
221 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
222 mixer_set(m, i, m->level[i]);
223
224 mixer_setrecsrc(m, m->recsrc);
225
226 return 0;
227}
228
229int
230mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
231{
232 int ret, *arg_i = (int *)arg;
233 int v = -1, j = cmd & 0xff;
234 snd_mixer *m;
235
236 m = d->mixer;
237
238 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
239 if (j == SOUND_MIXER_RECSRC)
240 ret = mixer_setrecsrc(m, *arg_i);
241 else
242 ret = mixer_set(m, j, *arg_i);
243 return (ret == 0)? 0 : ENXIO;
244 }
245
246 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
247 switch (j) {
248 case SOUND_MIXER_DEVMASK:
249 case SOUND_MIXER_CAPS:
250 case SOUND_MIXER_STEREODEVS:
251 v = mix_getdevs(m);
252 break;
253
254 case SOUND_MIXER_RECMASK:
255 v = mix_getrecdevs(m);
256 break;
257
258 case SOUND_MIXER_RECSRC:
259 v = mixer_getrecsrc(m);
260 break;
261
262 default:
263 v = mixer_get(m, j);
264 }
265 *arg_i = v;
266 return (v != -1)? 0 : ENXIO;
267 }
268 return ENXIO;
269}
270
271static int
272sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
273{
274 char devname[32];
275 int error, dev;
276 snd_mixer *m;
277
278 m = oidp->oid_arg1;
279 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
280 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
281 if (error == 0 && req->newptr != NULL) {
282 dev = mixer_lookup(devname);
283 if (dev == -1)
284 return EINVAL;
285 else
285 else if (dev != m->hwvol_mixer) {
286 m->hwvol_mixer = dev;
286 m->hwvol_mixer = dev;
287 m->hwvol_muted = 0;
288 }
287 }
288 return error;
289}
290
291int
289 }
290 return error;
291}
292
293int
292mixer_hwinit(device_t dev)
294mixer_hwvol_init(device_t dev)
293{
294 snddev_info *d;
295 snd_mixer *m;
296
297 d = device_get_softc(dev);
298 m = d->mixer;
299 m->hwvol_mixer = SOUND_MIXER_VOLUME;
300 m->hwvol_step = 5;
301 SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
302 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
303 SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
304 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
305 sysctl_hw_snd_hwvol_mixer, "A", "")
306 return 0;
307}
308
309void
295{
296 snddev_info *d;
297 snd_mixer *m;
298
299 d = device_get_softc(dev);
300 m = d->mixer;
301 m->hwvol_mixer = SOUND_MIXER_VOLUME;
302 m->hwvol_step = 5;
303 SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
304 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
305 SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
306 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
307 sysctl_hw_snd_hwvol_mixer, "A", "")
308 return 0;
309}
310
311void
310mixer_hwmute(device_t dev)
312mixer_hwvol_mute(device_t dev)
311{
312 snddev_info *d;
313 snd_mixer *m;
314
315 d = device_get_softc(dev);
316 m = d->mixer;
313{
314 snddev_info *d;
315 snd_mixer *m;
316
317 d = device_get_softc(dev);
318 m = d->mixer;
317 if (m->muted) {
318 m->muted = 0;
319 mixer_set(m, m->hwvol_mixer, m->mute_level);
319 if (m->hwvol_muted) {
320 m->hwvol_muted = 0;
321 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
320 } else {
322 } else {
321 m->muted++;
322 m->mute_level = mixer_get(m, m->hwvol_mixer);
323 m->hwvol_muted++;
324 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
323 mixer_set(m, m->hwvol_mixer, 0);
324 }
325}
326
327void
325 mixer_set(m, m->hwvol_mixer, 0);
326 }
327}
328
329void
328mixer_hwstep(device_t dev, int left_step, int right_step)
330mixer_hwvol_step(device_t dev, int left_step, int right_step)
329{
330 snddev_info *d;
331 snd_mixer *m;
332 int level, left, right;
333
334 d = device_get_softc(dev);
335 m = d->mixer;
331{
332 snddev_info *d;
333 snd_mixer *m;
334 int level, left, right;
335
336 d = device_get_softc(dev);
337 m = d->mixer;
336 level = mixer_get(m, m->hwvol_mixer);
338 if (m->hwvol_muted) {
339 m->hwvol_muted = 0;
340 level = m->hwvol_mute_level;
341 } else
342 level = mixer_get(m, m->hwvol_mixer);
337 if (level != -1) {
338 left = level & 0xff;
339 right = level >> 8;
340 left += left_step * m->hwvol_step;
341 if (left < 0)
342 left = 0;
343 right += right_step * m->hwvol_step;
344 if (right < 0)
345 right = 0;
346 mixer_set(m, m->hwvol_mixer, left | right << 8);
347 }
348}
349
350/*
351 * The various mixers use a variety of bitmasks etc. The Voxware
352 * driver had a very nice technique to describe a mixer and interface
353 * to it. A table defines, for each channel, which register, bits,
354 * offset, polarity to use. This procedure creates the new value
355 * using the table and the old value.
356 */
357
358void
359change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
360{
361 u_char mask;
362 int shift;
363
364 DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
365 "r %d p %d bit %d off %d\n",
366 dev, chn, newval, *regval,
367 (*t)[dev][chn].regno, (*t)[dev][chn].polarity,
368 (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
369
370 if ( (*t)[dev][chn].polarity == 1) /* reverse */
371 newval = 100 - newval ;
372
373 mask = (1 << (*t)[dev][chn].nbits) - 1;
374 newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
375 shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
376
377 *regval &= ~(mask << shift); /* Filter out the previous value */
378 *regval |= (newval & mask) << shift; /* Set the new value */
379}
380
343 if (level != -1) {
344 left = level & 0xff;
345 right = level >> 8;
346 left += left_step * m->hwvol_step;
347 if (left < 0)
348 left = 0;
349 right += right_step * m->hwvol_step;
350 if (right < 0)
351 right = 0;
352 mixer_set(m, m->hwvol_mixer, left | right << 8);
353 }
354}
355
356/*
357 * The various mixers use a variety of bitmasks etc. The Voxware
358 * driver had a very nice technique to describe a mixer and interface
359 * to it. A table defines, for each channel, which register, bits,
360 * offset, polarity to use. This procedure creates the new value
361 * using the table and the old value.
362 */
363
364void
365change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
366{
367 u_char mask;
368 int shift;
369
370 DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
371 "r %d p %d bit %d off %d\n",
372 dev, chn, newval, *regval,
373 (*t)[dev][chn].regno, (*t)[dev][chn].polarity,
374 (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
375
376 if ( (*t)[dev][chn].polarity == 1) /* reverse */
377 newval = 100 - newval ;
378
379 mask = (1 << (*t)[dev][chn].nbits) - 1;
380 newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
381 shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
382
383 *regval &= ~(mask << shift); /* Filter out the previous value */
384 *regval |= (newval & mask) << shift; /* Set the new value */
385}
386