]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mixer/mixer.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / usr.sbin / mixer / mixer.c
1 /*-
2  * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22
23 #include <err.h>
24 #include <errno.h>
25 #include <mixer.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 enum {
32         C_VOL = 0,
33         C_MUT,
34         C_SRC,
35 };
36
37 static void usage(void) __dead2;
38 static void initctls(struct mixer *);
39 static void printall(struct mixer *, int);
40 static void printminfo(struct mixer *, int);
41 static void printdev(struct mixer *, int);
42 static void printrecsrc(struct mixer *, int); /* XXX: change name */
43 static int set_dunit(struct mixer *, int);
44 /* Control handlers */
45 static int mod_volume(struct mix_dev *, void *);
46 static int mod_mute(struct mix_dev *, void *);
47 static int mod_recsrc(struct mix_dev *, void *);
48 static int print_volume(struct mix_dev *, void *);
49 static int print_mute(struct mix_dev *, void *);
50 static int print_recsrc(struct mix_dev *, void *);
51
52 int
53 main(int argc, char *argv[])
54 {
55         struct mixer *m;
56         mix_ctl_t *cp;
57         char *name = NULL, buf[NAME_MAX];
58         char *p, *q, *devstr, *ctlstr, *valstr = NULL;
59         int dunit, i, n, pall = 1, shorthand;
60         int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
61         int ch;
62
63         while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) {
64                 switch (ch) {
65                 case 'a':
66                         aflag = 1;
67                         break;
68                 case 'd':
69                         dunit = strtol(optarg, NULL, 10);
70                         if (errno == EINVAL || errno == ERANGE)
71                                 err(1, "strtol");
72                         dflag = 1;
73                         break;
74                 case 'f':
75                         name = optarg;
76                         break;
77                 case 'o':
78                         oflag = 1;
79                         break;
80                 case 's':
81                         sflag = 1;
82                         break;
83                 case 'h': /* FALLTHROUGH */
84                 case '?':
85                 default:
86                         usage();
87                 }
88         }
89         argc -= optind;
90         argv += optind;
91
92         /* Print all mixers and exit. */
93         if (aflag) {
94                 if ((n = mixer_get_nmixers()) < 0)
95                         err(1, "mixer_get_nmixers");
96                 for (i = 0; i < n; i++) {
97                         (void)snprintf(buf, sizeof(buf), "/dev/mixer%d", i);
98                         if ((m = mixer_open(buf)) == NULL)
99                                 err(1, "mixer_open: %s", buf);
100                         initctls(m);
101                         if (sflag)
102                                 printrecsrc(m, oflag);
103                         else {
104                                 printall(m, oflag);
105                                 if (oflag)
106                                         printf("\n");
107                         }
108                         (void)mixer_close(m);
109                 }
110                 return (0);
111         }
112
113         if ((m = mixer_open(name)) == NULL)
114                 err(1, "mixer_open: %s", name);
115
116         initctls(m);
117
118         if (dflag && set_dunit(m, dunit) < 0)
119                 goto parse;
120         if (sflag) {
121                 printrecsrc(m, oflag);
122                 (void)mixer_close(m);
123                 return (0);
124         }
125
126 parse:
127         while (argc > 0) {
128                 if ((p = strdup(*argv)) == NULL)
129                         err(1, "strdup(%s)", *argv);
130
131                 /* Check if we're using the shorthand syntax for volume setting. */
132                 shorthand = 0;
133                 for (q = p; *q != '\0'; q++) {
134                         if (*q == '=') {
135                                 q++;
136                                 shorthand = ((*q >= '0' && *q <= '9') ||
137                                     *q == '+' || *q == '-' || *q == '.');
138                                 break;
139                         } else if (*q == '.')
140                                 break;
141                 }
142
143                 /* Split the string into device, control and value. */
144                 devstr = strsep(&p, ".=");
145                 if ((m->dev = mixer_get_dev_byname(m, devstr)) == NULL) {
146                         warnx("%s: no such device", devstr);
147                         goto next;
148                 }
149                 /* Input: `dev`. */
150                 if (p == NULL) {
151                         printdev(m, 1);
152                         pall = 0;
153                         goto next;
154                 } else if (shorthand) {
155                         /*
156                          * Input: `dev=N` -> shorthand for `dev.volume=N`.
157                          *
158                          * We don't care what the rest of the string contains as
159                          * long as we're sure the very beginning is right,
160                          * mod_volume() will take care of parsing it properly.
161                          */
162                         cp = mixer_get_ctl(m->dev, C_VOL);
163                         cp->mod(cp->parent_dev, p);
164                         goto next;
165                 }
166                 ctlstr = strsep(&p, "=");
167                 if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
168                         warnx("%s.%s: no such control", devstr, ctlstr);
169                         goto next;
170                 }
171                 /* Input: `dev.control`. */
172                 if (p == NULL) {
173                         (void)cp->print(cp->parent_dev, cp->name);
174                         pall = 0;
175                         goto next;
176                 }
177                 valstr = p;
178                 /* Input: `dev.control=val`. */
179                 cp->mod(cp->parent_dev, valstr);
180 next:
181                 free(p);
182                 argc--;
183                 argv++;
184         }
185
186         if (pall)
187                 printall(m, oflag);
188         (void)mixer_close(m);
189
190         return (0);
191 }
192
193 static void __dead2
194 usage(void)
195 {
196         fprintf(stderr, "usage: %1$s [-f device] [-d unit] [-os] [dev[.control[=value]]] ...\n"
197             "       %1$s [-d unit] [-os] -a\n"
198             "       %1$s -h\n", getprogname());
199         exit(1);
200 }
201
202 static void
203 initctls(struct mixer *m)
204 {
205         struct mix_dev *dp;
206         int rc = 0;
207
208         TAILQ_FOREACH(dp, &m->devs, devs) {
209                 rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, print_volume);
210                 rc += mixer_add_ctl(dp, C_MUT, "mute", mod_mute, print_mute);
211                 rc += mixer_add_ctl(dp, C_SRC, "recsrc", mod_recsrc, print_recsrc);
212         }
213         if (rc) {
214                 (void)mixer_close(m);
215                 err(1, "cannot make controls");
216         }
217 }
218
219 static void
220 printall(struct mixer *m, int oflag)
221 {
222         struct mix_dev *dp;
223
224         printminfo(m, oflag);
225         TAILQ_FOREACH(dp, &m->devs, devs) {
226                 m->dev = dp;
227                 printdev(m, oflag);
228         }
229 }
230
231 static void
232 printminfo(struct mixer *m, int oflag)
233 {
234         int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
235
236         if (oflag)
237                 return;
238         printf("%s:", m->mi.name);
239         if (*m->ci.longname != '\0')
240                 printf(" <%s>", m->ci.longname);
241         if (*m->ci.hw_info != '\0')
242                 printf(" %s", m->ci.hw_info);
243
244         if (m->mode != 0)
245                 printf(" (");
246         if (m->mode & MIX_MODE_PLAY)
247                 printf("play");
248         if ((m->mode & playrec) == playrec)
249                 printf("/");
250         if (m->mode & MIX_MODE_REC)
251                 printf("rec");
252         if (m->mode != 0)
253                 printf(")");
254
255         if (m->f_default)
256                 printf(" (default)");
257         printf("\n");
258 }
259
260 static void
261 printdev(struct mixer *m, int oflag)
262 {
263         struct mix_dev *d = m->dev;
264         mix_ctl_t *cp;
265
266         if (!oflag) {
267                 printf("    %-10s= %.2f:%.2f    ",
268                     d->name, d->vol.left, d->vol.right);
269                 if (!MIX_ISREC(m, d->devno))
270                         printf(" pbk");
271                 if (MIX_ISREC(m, d->devno))
272                         printf(" rec");
273                 if (MIX_ISRECSRC(m, d->devno))
274                         printf(" src");
275                 if (MIX_ISMUTE(m, d->devno))
276                         printf(" mute");
277                 printf("\n");
278         } else {
279                 TAILQ_FOREACH(cp, &d->ctls, ctls) {
280                         (void)cp->print(cp->parent_dev, cp->name);
281                 }
282         }
283 }
284
285 static void
286 printrecsrc(struct mixer *m, int oflag)
287 {
288         struct mix_dev *dp;
289         int n = 0;
290
291         if (!m->recmask)
292                 return;
293         if (!oflag)
294                 printf("%s: ", m->mi.name);
295         TAILQ_FOREACH(dp, &m->devs, devs) {
296                 if (MIX_ISRECSRC(m, dp->devno)) {
297                         if (n++ && !oflag)
298                                 printf(", ");
299                         printf("%s", dp->name);
300                         if (oflag)
301                                 printf(".%s=+%s",
302                                     mixer_get_ctl(dp, C_SRC)->name, n ? " " : "");
303                 }
304         }
305         printf("\n");
306 }
307
308 static int
309 set_dunit(struct mixer *m, int dunit)
310 {
311         int n;
312
313         if ((n = mixer_get_dunit()) < 0) {
314                 warn("cannot get default unit");
315                 return (-1);
316         }
317         if (mixer_set_dunit(m, dunit) < 0) {
318                 warn("cannot set default unit to: %d", dunit);
319                 return (-1);
320         }
321         printf("default_unit: %d -> %d\n", n, dunit);
322
323         return (0);
324 }
325
326 static int
327 mod_volume(struct mix_dev *d, void *p)
328 {
329         struct mixer *m;
330         mix_ctl_t *cp;
331         mix_volume_t v;
332         const char *val;
333         char *endp, lstr[8], rstr[8];
334         float lprev, rprev, lrel, rrel;
335         int n;
336
337         m = d->parent_mixer;
338         cp = mixer_get_ctl(m->dev, C_VOL);
339         val = p;
340         n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
341         if (n == EOF) {
342                 warnx("invalid volume value: %s", val);
343                 return (-1);
344         }
345         lrel = rrel = 0;
346         if (n > 0) {
347                 if (*lstr == '+' || *lstr == '-')
348                         lrel = 1;
349                 v.left = strtof(lstr, &endp);
350                 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
351                         warnx("invalid volume value: %s", lstr);
352                         return (-1);
353                 }
354
355                 if (*endp == '%')
356                         v.left /= 100.0f;
357         }
358         if (n > 1) {
359                 if (*rstr == '+' || *rstr == '-')
360                         rrel = 1;
361                 v.right = strtof(rstr, &endp);
362                 if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
363                         warnx("invalid volume value: %s", rstr);
364                         return (-1);
365                 }
366
367                 if (*endp == '%')
368                         v.right /= 100.0f;
369         }
370         switch (n) {
371         case 1:
372                 v.right = v.left; /* FALLTHROUGH */
373                 rrel = lrel;
374         case 2:
375                 if (lrel)
376                         v.left += m->dev->vol.left;
377                 if (rrel)
378                         v.right += m->dev->vol.right;
379
380                 if (v.left < MIX_VOLMIN)
381                         v.left = MIX_VOLMIN;
382                 else if (v.left > MIX_VOLMAX)
383                         v.left = MIX_VOLMAX;
384                 if (v.right < MIX_VOLMIN)
385                         v.right = MIX_VOLMIN;
386                 else if (v.right > MIX_VOLMAX)
387                         v.right = MIX_VOLMAX;
388
389                 lprev = m->dev->vol.left;
390                 rprev = m->dev->vol.right;
391                 if (mixer_set_vol(m, v) < 0)
392                         warn("%s.%s=%.2f:%.2f",
393                             m->dev->name, cp->name, v.left, v.right);
394                 else
395                         printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
396                            m->dev->name, cp->name, lprev, rprev, v.left, v.right);
397         }
398
399         return (0);
400 }
401
402 static int
403 mod_mute(struct mix_dev *d, void *p)
404 {
405         struct mixer *m;
406         mix_ctl_t *cp;
407         const char *val;
408         int n, opt = -1;
409
410         m = d->parent_mixer;
411         cp = mixer_get_ctl(m->dev, C_MUT);
412         val = p;
413         switch (*val) {
414         case '0':
415                 opt = MIX_UNMUTE;
416                 break;
417         case '1':
418                 opt = MIX_MUTE;
419                 break;
420         case '^':
421                 opt = MIX_TOGGLEMUTE;
422                 break;
423         default:
424                 warnx("%c: no such modifier", *val);
425                 return (-1);
426         }
427         n = MIX_ISMUTE(m, m->dev->devno);
428         if (mixer_set_mute(m, opt) < 0)
429                 warn("%s.%s=%c", m->dev->name, cp->name, *val);
430         else
431                 printf("%s.%s: %d -> %d\n",
432                     m->dev->name, cp->name, n, MIX_ISMUTE(m, m->dev->devno));
433
434         return (0);
435 }
436
437 static int
438 mod_recsrc(struct mix_dev *d, void *p)
439 {
440         struct mixer *m;
441         mix_ctl_t *cp;
442         const char *val;
443         int n, opt = -1;
444
445         m = d->parent_mixer;
446         cp = mixer_get_ctl(m->dev, C_SRC);
447         val = p;
448         switch (*val) {
449         case '+':
450                 opt = MIX_ADDRECSRC;
451                 break;
452         case '-':
453                 opt = MIX_REMOVERECSRC;
454                 break;
455         case '=':
456                 opt = MIX_SETRECSRC;
457                 break;
458         case '^':
459                 opt = MIX_TOGGLERECSRC;
460                 break;
461         default:
462                 warnx("%c: no such modifier", *val);
463                 return (-1);
464         }
465         n = MIX_ISRECSRC(m, m->dev->devno);
466         if (mixer_mod_recsrc(m, opt) < 0)
467                 warn("%s.%s=%c", m->dev->name, cp->name, *val);
468         else
469                 printf("%s.%s: %d -> %d\n",
470                     m->dev->name, cp->name, n, MIX_ISRECSRC(m, m->dev->devno));
471
472         return (0);
473 }
474
475 static int
476 print_volume(struct mix_dev *d, void *p)
477 {
478         struct mixer *m = d->parent_mixer;
479         const char *ctl_name = p;
480
481         printf("%s.%s=%.2f:%.2f\n",
482             m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
483
484         return (0);
485 }
486
487 static int
488 print_mute(struct mix_dev *d, void *p)
489 {
490         struct mixer *m = d->parent_mixer;
491         const char *ctl_name = p;
492
493         printf("%s.%s=%d\n", m->dev->name, ctl_name, MIX_ISMUTE(m, m->dev->devno));
494
495         return (0);
496 }
497
498 static int
499 print_recsrc(struct mix_dev *d, void *p)
500 {
501         struct mixer *m = d->parent_mixer;
502         const char *ctl_name = p;
503
504         if (!MIX_ISRECSRC(m, m->dev->devno))
505                 return (-1);
506         printf("%s.%s=+\n", m->dev->name, ctl_name);
507
508         return (0);
509 }