32 |
32 |
*
|
33 |
33 |
*/
|
34 |
34 |
|
|
35 |
#define HAVE_OSSV4
|
|
36 |
|
35 |
37 |
#ifdef HAVE_CONFIG_H
|
36 |
38 |
#include <config.h>
|
37 |
39 |
#endif
|
... | ... | |
96 |
98 |
PA_MODULE_DEPRECATED("Please use module-alsa-card instead of module-oss!");
|
97 |
99 |
#endif
|
98 |
100 |
|
|
101 |
#ifdef HAVE_SOLARIS
|
|
102 |
#define DEFAULT_DEVICE "/dev/audio"
|
|
103 |
#else
|
99 |
104 |
#define DEFAULT_DEVICE "/dev/dsp"
|
|
105 |
#endif /* HAVE_SOLARIS */
|
100 |
106 |
|
101 |
107 |
struct userdata {
|
102 |
108 |
pa_core *core;
|
... | ... | |
123 |
129 |
int mode;
|
124 |
130 |
|
125 |
131 |
int mixer_fd;
|
|
132 |
|
|
133 |
#ifdef HAVE_OSSV4
|
|
134 |
int mixer_dev;
|
|
135 |
int mixer_sink_control;
|
|
136 |
int mixer_source_control;
|
|
137 |
int mixer_cmax;
|
|
138 |
int mixer_dsp_fd;
|
|
139 |
oss_mixext sink_mixext;
|
|
140 |
oss_mixext source_mixext;
|
|
141 |
#else
|
126 |
142 |
int mixer_devmask;
|
|
143 |
#endif
|
127 |
144 |
|
128 |
145 |
int nfrags, frag_size, orig_frag_size;
|
129 |
146 |
|
... | ... | |
809 |
826 |
return ret;
|
810 |
827 |
}
|
811 |
828 |
|
|
829 |
#ifdef HAVE_OSSV4
|
|
830 |
|
|
831 |
static const char *
|
|
832 |
mixer_ext_type_get_name (int type)
|
|
833 |
{
|
|
834 |
switch (type) {
|
|
835 |
case MIXT_DEVROOT:
|
|
836 |
return "Device root entry";
|
|
837 |
case MIXT_GROUP:
|
|
838 |
return "Controller group";
|
|
839 |
case MIXT_ONOFF:
|
|
840 |
return "On/Off switch";
|
|
841 |
case MIXT_ENUM:
|
|
842 |
return "Enumeration control";
|
|
843 |
case MIXT_MONOSLIDER:
|
|
844 |
return "Mono slider (0-255)";
|
|
845 |
case MIXT_STEREOSLIDER:
|
|
846 |
return "Stereo slider (0-255)";
|
|
847 |
case MIXT_MESSAGE:
|
|
848 |
return "Textual message";
|
|
849 |
case MIXT_MONOVU:
|
|
850 |
return "Mono VU meter value";
|
|
851 |
case MIXT_STEREOVU:
|
|
852 |
return "Stereo VU meter value";
|
|
853 |
case MIXT_MONOPEAK:
|
|
854 |
return "Mono VU meter peak value";
|
|
855 |
case MIXT_STEREOPEAK:
|
|
856 |
return "Stereo VU meter peak value";
|
|
857 |
case MIXT_RADIOGROUP:
|
|
858 |
return "Radio button group";
|
|
859 |
case MIXT_MARKER:
|
|
860 |
/* Separator between normal and extension entries */
|
|
861 |
return "Separator";
|
|
862 |
case MIXT_VALUE:
|
|
863 |
return "Decimal value entry";
|
|
864 |
case MIXT_HEXVALUE:
|
|
865 |
return "Hex value entry";
|
|
866 |
case MIXT_SLIDER:
|
|
867 |
return "Mono slider (31-bit value range)";
|
|
868 |
case MIXT_3D:
|
|
869 |
return "3D";
|
|
870 |
case MIXT_MONOSLIDER16:
|
|
871 |
return "Mono slider (0-32767)";
|
|
872 |
case MIXT_STEREOSLIDER16:
|
|
873 |
return "Stereo slider (0-32767)";
|
|
874 |
case MIXT_MUTE:
|
|
875 |
return "Mute switch";
|
|
876 |
default:
|
|
877 |
break;
|
|
878 |
}
|
|
879 |
|
|
880 |
return "Unknown";
|
|
881 |
}
|
|
882 |
|
|
883 |
void
|
|
884 |
mixer_showflags (int flags)
|
|
885 |
{
|
|
886 |
struct
|
|
887 |
{
|
|
888 |
int flag;
|
|
889 |
char nick[16];
|
|
890 |
} all_flags[] = {
|
|
891 |
/* first the important ones */
|
|
892 |
{
|
|
893 |
MIXF_MAINVOL, "MAINVOL"}, {
|
|
894 |
MIXF_PCMVOL, "PCMVOL"}, {
|
|
895 |
MIXF_RECVOL, "RECVOL"}, {
|
|
896 |
MIXF_MONVOL, "MONVOL"}, {
|
|
897 |
MIXF_DESCR, "DESCR"},
|
|
898 |
|
|
899 |
/* now the rest in the right order */
|
|
900 |
{
|
|
901 |
MIXF_READABLE, "READABLE"}, {
|
|
902 |
MIXF_WRITEABLE, "WRITABLE"}, {
|
|
903 |
MIXF_POLL, "POLL"}, {
|
|
904 |
MIXF_HZ, "HZ"}, {
|
|
905 |
MIXF_STRING, "STRING"}, {
|
|
906 |
MIXF_DYNAMIC, "DYNAMIC"}, {
|
|
907 |
MIXF_OKFAIL, "OKFAIL"}, {
|
|
908 |
MIXF_FLAT, "FLAT"}, {
|
|
909 |
MIXF_LEGACY, "LEGACY"}, {
|
|
910 |
MIXF_CENTIBEL, "CENTIBEL"}, {
|
|
911 |
MIXF_DECIBEL, "DECIBEL"}, {
|
|
912 |
MIXF_WIDE, "WIDE"}
|
|
913 |
};
|
|
914 |
int num_flags = (sizeof (all_flags) / sizeof ((all_flags)[0]));
|
|
915 |
int i;
|
|
916 |
|
|
917 |
if (flags == 0) {
|
|
918 |
pa_log_debug (" flags : None");
|
|
919 |
return;
|
|
920 |
}
|
|
921 |
|
|
922 |
for (i=0; i < num_flags; i++) {
|
|
923 |
if ((flags & all_flags[i].flag)) {
|
|
924 |
pa_log_debug (" flag : %s", all_flags[i].nick);
|
|
925 |
flags &= ~all_flags[i].flag; /* unset */
|
|
926 |
}
|
|
927 |
}
|
|
928 |
|
|
929 |
/* Unknown flags? */
|
|
930 |
if (flags != 0) {
|
|
931 |
pa_log_debug (" flag : ????");
|
|
932 |
}
|
|
933 |
|
|
934 |
return;
|
|
935 |
}
|
|
936 |
|
812 |
937 |
static void sink_get_volume(pa_sink *s) {
|
813 |
938 |
struct userdata *u;
|
814 |
939 |
|
815 |
940 |
pa_assert_se(u = s->userdata);
|
816 |
941 |
|
|
942 |
if (pa_oss_get_volume(u->mixer_dsp_fd, &(u->sink_mixext), &s->sample_spec, &s->real_volume) >= 0)
|
|
943 |
return;
|
|
944 |
|
|
945 |
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
|
|
946 |
}
|
|
947 |
|
|
948 |
static void sink_set_volume(pa_sink *s) {
|
|
949 |
struct userdata *u;
|
|
950 |
|
|
951 |
pa_assert_se(u = s->userdata);
|
|
952 |
|
|
953 |
if (pa_oss_set_volume(u->mixer_dsp_fd, &(u->sink_mixext), &s->sample_spec, &s->real_volume) >= 0)
|
|
954 |
return;
|
|
955 |
|
|
956 |
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
|
|
957 |
}
|
|
958 |
|
|
959 |
static void source_get_volume(pa_source *s) {
|
|
960 |
struct userdata *u;
|
|
961 |
|
|
962 |
pa_assert_se(u = s->userdata);
|
|
963 |
|
|
964 |
if (pa_oss_get_volume(u->mixer_dsp_fd, &(u->source_mixext), &s->sample_spec, &s->real_volume) >= 0)
|
|
965 |
return;
|
|
966 |
|
|
967 |
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
|
|
968 |
}
|
|
969 |
|
|
970 |
static void source_set_volume(pa_source *s) {
|
|
971 |
struct userdata *u;
|
|
972 |
|
|
973 |
pa_assert_se(u = s->userdata);
|
|
974 |
|
|
975 |
if (pa_oss_set_volume(u->mixer_dsp_fd, &(u->source_mixext), &s->sample_spec, &s->real_volume) >= 0)
|
|
976 |
return;
|
|
977 |
|
|
978 |
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
|
|
979 |
}
|
|
980 |
|
|
981 |
#else
|
|
982 |
|
|
983 |
static void sink_get_volume(pa_sink *s) {
|
|
984 |
struct userdata *u;
|
|
985 |
|
|
986 |
pa_assert_se(u = s->userdata);
|
|
987 |
|
817 |
988 |
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
|
818 |
989 |
|
819 |
990 |
if (u->mixer_devmask & SOUND_MASK_VOLUME)
|
... | ... | |
880 |
1051 |
|
881 |
1052 |
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
|
882 |
1053 |
}
|
|
1054 |
#endif
|
883 |
1055 |
|
884 |
1056 |
static void thread_func(void *userdata) {
|
885 |
1057 |
struct userdata *u = userdata;
|
... | ... | |
1162 |
1334 |
|
1163 |
1335 |
int pa__init(pa_module*m) {
|
1164 |
1336 |
|
|
1337 |
#ifdef HAVE_OSSV4
|
|
1338 |
struct oss_sysinfo si = { {0,}, };
|
|
1339 |
struct oss_mixerinfo mi = { 0, };
|
|
1340 |
#endif
|
|
1341 |
|
1165 |
1342 |
struct audio_buf_info info;
|
1166 |
1343 |
struct userdata *u = NULL;
|
1167 |
1344 |
const char *dev;
|
|
1345 |
const char *default_dev;
|
1168 |
1346 |
int fd = -1;
|
1169 |
1347 |
int nfrags, orig_frag_size, frag_size;
|
1170 |
1348 |
int mode, caps;
|
|
1349 |
int dev_num;
|
1171 |
1350 |
bool record = true, playback = true, use_mmap = true;
|
1172 |
1351 |
pa_sample_spec ss;
|
1173 |
1352 |
pa_channel_map map;
|
... | ... | |
1219 |
1398 |
goto fail;
|
1220 |
1399 |
}
|
1221 |
1400 |
|
1222 |
|
if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
|
|
1401 |
#ifdef HAVE_SOLARIS
|
|
1402 |
default_dev = getenv("AUDIODEV");
|
|
1403 |
if (!default_dev)
|
|
1404 |
default_dev = DEFAULT_DEVICE;
|
|
1405 |
#else /* HAVE_SOLARIS */
|
|
1406 |
default_dev = DEFAULT_DEVICE;
|
|
1407 |
#endif /* HAVE_SOLARIS */
|
|
1408 |
|
|
1409 |
if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", default_dev), &mode, &caps)) < 0)
|
1223 |
1410 |
goto fail;
|
1224 |
1411 |
|
1225 |
1412 |
if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
|
... | ... | |
1259 |
1446 |
m->userdata = u;
|
1260 |
1447 |
u->fd = fd;
|
1261 |
1448 |
u->mixer_fd = -1;
|
|
1449 |
#ifndef HAVE_OSSV4
|
1262 |
1450 |
u->mixer_devmask = 0;
|
|
1451 |
#endif
|
1263 |
1452 |
u->use_getospace = u->use_getispace = true;
|
1264 |
1453 |
u->use_getodelay = true;
|
1265 |
1454 |
u->mode = mode;
|
... | ... | |
1428 |
1617 |
u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
|
1429 |
1618 |
}
|
1430 |
1619 |
|
1431 |
|
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
|
|
1620 |
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name, &dev_num)) >= 0) {
|
1432 |
1621 |
bool do_close = true;
|
1433 |
1622 |
|
|
1623 |
#ifdef HAVE_OSSV4
|
|
1624 |
#define IGNORE_DEV 1
|
|
1625 |
int i;
|
|
1626 |
|
|
1627 |
u->mixer_dsp_fd = -1;
|
|
1628 |
|
|
1629 |
if (ioctl (u->mixer_fd, SNDCTL_SYSINFO, &si) < 0) {
|
|
1630 |
pa_log_debug ("SNDCTL_SYSINFO failed");
|
|
1631 |
goto fail;
|
|
1632 |
}
|
|
1633 |
for (i = 0; i < si.nummixers; i++) {
|
|
1634 |
mi.dev = i;
|
|
1635 |
if (ioctl (u->mixer_fd, SNDCTL_MIXERINFO, &mi) < 0) {
|
|
1636 |
pa_log_debug("SNDCTL_MIXERINFO failed");
|
|
1637 |
goto fail;
|
|
1638 |
}
|
|
1639 |
if (mi.enabled != 0) {
|
|
1640 |
/*
|
|
1641 |
* There could be multiple mixers. First we only care about
|
|
1642 |
* ones which are enabled. In any case we should not touch
|
|
1643 |
* disabled mixers!
|
|
1644 |
* This code now will always pick the first enabled mixer
|
|
1645 |
* found that has the matching device number.
|
|
1646 |
*/
|
|
1647 |
if (i == dev_num) break;
|
|
1648 |
}
|
|
1649 |
}
|
|
1650 |
|
|
1651 |
if (i < si.nummixers) {
|
|
1652 |
struct stat sbuf;
|
|
1653 |
|
|
1654 |
if ((stat(mi.devnode, &sbuf) != 0) ||
|
|
1655 |
((sbuf.st_mode & S_IFCHR) == 0)) {
|
|
1656 |
pa_log("Failed to get mixer dsp device.");
|
|
1657 |
i = si.nummixers;
|
|
1658 |
}
|
|
1659 |
}
|
|
1660 |
|
|
1661 |
if (i < si.nummixers &&
|
|
1662 |
(u->mixer_dsp_fd = pa_oss_open_mixer(mi.devnode)) >= 0) {
|
|
1663 |
|
|
1664 |
/* Will cause for loop to exit if not filled in by OSS */
|
|
1665 |
u->mixer_cmax = -1;
|
|
1666 |
if (ioctl(u->mixer_dsp_fd, SNDCTL_MIX_NREXT, &u->mixer_cmax) < 0) {
|
|
1667 |
pa_log("Failed to get max control.");
|
|
1668 |
goto fail;
|
|
1669 |
}
|
|
1670 |
|
|
1671 |
pa_log_debug ("Opened mixer device %d with %d controls\n",
|
|
1672 |
mi.dev, mi.nrext);
|
|
1673 |
|
|
1674 |
u->mixer_sink_control = -1;
|
|
1675 |
u->mixer_source_control = -1;
|
|
1676 |
|
|
1677 |
for (i=0; i < u->mixer_cmax; i++) {
|
|
1678 |
memset (&(u->sink_mixext), 0, sizeof (oss_mixext));
|
|
1679 |
|
|
1680 |
#ifdef IGNORE_DEV
|
|
1681 |
/* This will cause dev to be ignored */
|
|
1682 |
u->sink_mixext.dev = -1;
|
|
1683 |
#else
|
|
1684 |
u->sink_mixext.dev = mi.dev;
|
|
1685 |
#endif
|
|
1686 |
|
|
1687 |
/*
|
|
1688 |
* The real way to pick a control on a mixer is with this
|
|
1689 |
* number. Note that control numbers are unique across all
|
|
1690 |
* mixers. So dev can just be ignored. When dev is included
|
|
1691 |
* it will only be used to check for correct dev from
|
|
1692 |
* userland. But it will not be used to select a mixer.
|
|
1693 |
*/
|
|
1694 |
u->sink_mixext.ctrl = i;
|
|
1695 |
|
|
1696 |
pa_log_debug ("Control %d", u->sink_mixext.ctrl);
|
|
1697 |
|
|
1698 |
if (ioctl (u->mixer_dsp_fd, SNDCTL_MIX_EXTINFO,
|
|
1699 |
&(u->sink_mixext)) < 0) {
|
|
1700 |
pa_log_debug ("SNDCTL_MIX_EXTINFO failed");
|
|
1701 |
continue;
|
|
1702 |
}
|
|
1703 |
|
|
1704 |
pa_log_debug (" name : %s", u->sink_mixext.extname);
|
|
1705 |
pa_log_debug (" type : %s (%d)",
|
|
1706 |
mixer_ext_type_get_name (u->sink_mixext.type),
|
|
1707 |
u->sink_mixext.type);
|
|
1708 |
pa_log_debug (" maxval : %d", u->sink_mixext.maxvalue);
|
|
1709 |
pa_log_debug (" parent : %d", u->sink_mixext.parent);
|
|
1710 |
mixer_showflags (u->sink_mixext.flags);
|
|
1711 |
|
|
1712 |
if ((u->sink_mixext.flags & MIXF_PCMVOL)) {
|
|
1713 |
pa_log_debug ("First PCM control: %d", i);
|
|
1714 |
u->mixer_sink_control = i;
|
|
1715 |
break;
|
|
1716 |
}
|
|
1717 |
|
|
1718 |
/*
|
|
1719 |
* Note that MIXF_MAINVOL may not be an exclusive single
|
|
1720 |
* control. For example on AudioHD there will be one for each
|
|
1721 |
* output jack (Green, Black, Orange...). So to really do a
|
|
1722 |
* master volume you would need to do all MIXF_MAINVOL at the
|
|
1723 |
* same time...
|
|
1724 |
*/
|
|
1725 |
if (((u->sink_mixext.flags & MIXF_MAINVOL)) &&
|
|
1726 |
u->mixer_sink_control == -1) {
|
|
1727 |
pa_log_debug ("First main volume control: %d", i);
|
|
1728 |
u->mixer_sink_control = i;
|
|
1729 |
}
|
|
1730 |
}
|
|
1731 |
|
|
1732 |
if (u->mixer_sink_control != -1) {
|
|
1733 |
pa_log_debug ("Setting OSS sink callbacks.");
|
|
1734 |
pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
|
|
1735 |
pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
|
|
1736 |
u->sink->n_volume_steps = 101;
|
|
1737 |
do_close = false;
|
|
1738 |
} else {
|
|
1739 |
pa_log_debug ("Not setting OSS sink callbacks.");
|
|
1740 |
}
|
|
1741 |
|
|
1742 |
for (i=0; i < mi.nrext; i++) {
|
|
1743 |
memset (&(u->source_mixext), 0, sizeof (oss_mixext));
|
|
1744 |
#ifdef IGNORE_DEV
|
|
1745 |
/* This will cause dev to be ignored */
|
|
1746 |
u->source_mixext.dev = -1;
|
|
1747 |
#else
|
|
1748 |
u->source_mixext.dev = mi.dev;
|
|
1749 |
#endif
|
|
1750 |
/*
|
|
1751 |
* The real way to pick a control on a mixer is with this
|
|
1752 |
* number. Note that control numbers are unique across all
|
|
1753 |
* mixers. So dev can just be ignored. When dev is included
|
|
1754 |
* it will only be used to check for correct dev from userland.
|
|
1755 |
* But it will not be used to select a mixer.
|
|
1756 |
*/
|
|
1757 |
u->source_mixext.ctrl = i;
|
|
1758 |
|
|
1759 |
pa_log_debug ("Control %d", u->source_mixext.ctrl);
|
|
1760 |
|
|
1761 |
if (ioctl (u->mixer_dsp_fd, SNDCTL_MIX_EXTINFO,
|
|
1762 |
&(u->source_mixext)) == -1) {
|
|
1763 |
pa_log_debug ("SNDCTL_MIX_EXTINFO failed");
|
|
1764 |
continue;
|
|
1765 |
}
|
|
1766 |
|
|
1767 |
pa_log_debug (" name : %s", u->source_mixext.extname);
|
|
1768 |
pa_log_debug (" type : %s (%d)",
|
|
1769 |
mixer_ext_type_get_name (u->source_mixext.type),
|
|
1770 |
u->source_mixext.type);
|
|
1771 |
pa_log_debug (" maxval : %d", u->source_mixext.maxvalue);
|
|
1772 |
pa_log_debug (" parent : %d", u->source_mixext.parent);
|
|
1773 |
mixer_showflags (u->source_mixext.flags);
|
|
1774 |
|
|
1775 |
/*
|
|
1776 |
* There may be more then one MIXF_RECVOL on a mixer. In fact
|
|
1777 |
* for audioHD the can be three (line-in, mix-in, and cd-in).
|
|
1778 |
* For a master gain it may be good to adjust all...
|
|
1779 |
*/
|
|
1780 |
if ((u->source_mixext.flags & MIXF_RECVOL)) {
|
|
1781 |
pa_log_debug ("First REC control: %d", i);
|
|
1782 |
u->mixer_source_control = i;
|
|
1783 |
break;
|
|
1784 |
}
|
|
1785 |
}
|
|
1786 |
|
|
1787 |
if (u->mixer_source_control != -1) {
|
|
1788 |
pa_log_debug ("Setting OSS source callbacks.");
|
|
1789 |
pa_source_set_get_volume_callback(u->source, source_get_volume);
|
|
1790 |
pa_source_set_set_volume_callback(u->source, source_set_volume);
|
|
1791 |
u->source->n_volume_steps = 101;
|
|
1792 |
do_close = false;
|
|
1793 |
} else {
|
|
1794 |
pa_log_debug ("Not setting OSS source callbacks.");
|
|
1795 |
}
|
|
1796 |
|
|
1797 |
if (do_close == true) {
|
|
1798 |
pa_close(u->mixer_dsp_fd);
|
|
1799 |
u->mixer_dsp_fd = -1;
|
|
1800 |
}
|
|
1801 |
}
|
|
1802 |
|
|
1803 |
#else
|
1434 |
1804 |
if (ioctl(u->mixer_fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
|
1435 |
1805 |
pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
|
1436 |
1806 |
else {
|
... | ... | |
1450 |
1820 |
do_close = false;
|
1451 |
1821 |
}
|
1452 |
1822 |
}
|
|
1823 |
#endif
|
1453 |
1824 |
|
1454 |
1825 |
if (do_close) {
|
1455 |
1826 |
pa_close(u->mixer_fd);
|
1456 |
1827 |
u->mixer_fd = -1;
|
|
1828 |
#ifndef HAVE_OSSV4
|
1457 |
1829 |
u->mixer_devmask = 0;
|
|
1830 |
#endif
|
1458 |
1831 |
}
|
1459 |
1832 |
}
|
1460 |
1833 |
|
... | ... | |
1573 |
1946 |
if (u->fd >= 0)
|
1574 |
1947 |
pa_close(u->fd);
|
1575 |
1948 |
|
|
1949 |
#ifdef HAVE_OSSV4
|
|
1950 |
if (u->mixer_dsp_fd >= 0)
|
|
1951 |
pa_close(u->mixer_dsp_fd);
|
|
1952 |
#endif
|
1576 |
1953 |
if (u->mixer_fd >= 0)
|
1577 |
1954 |
pa_close(u->mixer_fd);
|
1578 |
1955 |
|