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