diff options
-rw-r--r-- | CMakeLists.txt | 48 | ||||
-rw-r--r-- | COPYING | 510 | ||||
-rw-r--r-- | audiomgr.c | 82 | ||||
-rw-r--r-- | audiomgr.h | 30 | ||||
-rw-r--r-- | classify.c | 63 | ||||
-rw-r--r-- | classify.h | 8 | ||||
-rw-r--r-- | discover.c | 758 | ||||
-rw-r--r-- | discover.h | 58 | ||||
-rw-r--r-- | list.h | 48 | ||||
-rw-r--r-- | loopback.c | 112 | ||||
-rw-r--r-- | loopback.h | 58 | ||||
-rw-r--r-- | module.c | 149 | ||||
-rw-r--r-- | node.c | 179 | ||||
-rw-r--r-- | node.h | 111 | ||||
-rw-r--r-- | router.c | 198 | ||||
-rw-r--r-- | router.h | 79 | ||||
-rw-r--r-- | routerif.h | 10 | ||||
-rw-r--r-- | socketif.c | 54 | ||||
-rw-r--r-- | switch.c | 267 | ||||
-rw-r--r-- | switch.h | 35 | ||||
-rw-r--r-- | tracker.c | 371 | ||||
-rw-r--r-- | tracker.h | 11 | ||||
-rw-r--r-- | userdata.h | 120 | ||||
-rw-r--r-- | utils.c | 277 | ||||
-rw-r--r-- | utils.h | 53 | ||||
-rw-r--r-- | zone.c | 58 | ||||
-rw-r--r-- | zone.h | 37 |
27 files changed, 3784 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7156d4f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +PROJECT(agl-audio-plugin C) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8) +SET(CMAKE_BUILD_TYPE Debug) +SET(CMAKE_POSITION_INDEPENDENT_CODE ON) + +SET(PROJECT_NAME "AGL Audio Policy Plugin") +SET(PROJECT_PRETTY_NAME "AGL Audio PulseAudio Policy Plugin") +SET(PROJECT_DESCRIPTION "AGL PulseAudio plugin, forked from module-murphy-ivi") +SET(PROJECT_VERSION "0.1") +SET(PROJECT_URL "https://github.com/Tarnyko/sample-policy-plugin") + +INCLUDE(FindPkgConfig) +INCLUDE(GNUInstallDirs) + +########################################################################### + +link_libraries(-Wl,--as-needed -Wl,--gc-sections) + +add_compile_options(-Wall -Wextra -Wconversion) +add_compile_options(-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable) +add_compile_options(-Wno-parentheses) # this syntax is heavily used +add_compile_options(-Wno-sign-compare -Wno-sign-conversion) +add_compile_options(-Werror=maybe-uninitialized) +add_compile_options(-Werror=implicit-function-declaration) +add_compile_options(-ffunction-sections -fdata-sections) + +########################################################################### + +PKG_CHECK_MODULES(pulseaudio-module-devel REQUIRED pulseaudio-module-devel) + +ADD_DEFINITIONS(${pulseaudio-module-devel_CFLAGS}) +SET(include_dirs ${INCLUDE_DIRS} ${pulseaudio-module-devel_INCLUDE_DIRS}) +SET(link_libraries ${LINK_LIBRARIES} ${pulseaudio-module-devel_LIBRARIES}) +STRING(REGEX REPLACE ";" " " link_flags "${pulseaudio-module-devel_LDFLAGS}" "") + +SET(plugin_install_dir ${CMAKE_INSTALL_LIBDIR}/pulse-6.0/modules) + +############################################################ + +ADD_LIBRARY(agl-audio-plugin MODULE module.c audiomgr.c classify.c discover.c loopback.c node.c router.c socketif.c switch.c tracker.c utils.c zone.c) +INCLUDE_DIRECTORIES(${include_dirs}) +TARGET_LINK_LIBRARIES(agl-audio-plugin ${link_libraries}) +SET_TARGET_PROPERTIES(agl-audio-plugin PROPERTIES PREFIX "" + LINK_FLAGS "${link_flags} -Wl,-rpath=${plugin_install_dir}") + +INSTALL(TARGETS agl-audio-plugin + LIBRARY DESTINATION ${plugin_install_dir}) @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/audiomgr.c b/audiomgr.c new file mode 100644 index 0000000..138d9f1 --- /dev/null +++ b/audiomgr.c @@ -0,0 +1,82 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "audiomgr.h" + +#define DS_DOWN 255 + +typedef struct { + const char *name; + uint16_t id; + uint16_t state; +} domain_t; + +typedef struct { + uint16_t fromidx; + uint16_t toidx; + uint32_t channels; +} link_t; + +typedef struct { + int maxlink; + int nlink; + link_t *links; +} routes_t; + +struct pa_audiomgr { + domain_t domain; + pa_hashmap *nodes; /**< nodes ie. sinks and sources */ + pa_hashmap *conns; /**< connections */ + routes_t defrts; /**< default routes */ +}; + +struct pa_audiomgr *pa_audiomgr_init (struct userdata *u) +{ + pa_audiomgr *am; + + pa_assert (u); + + am = pa_xnew0 (pa_audiomgr, 1); + am->domain.id = AM_ID_INVALID; + am->domain.state = DS_DOWN; + am->nodes = pa_hashmap_new (pa_idxset_trivial_hash_func, + pa_idxset_trivial_compare_func); + am->conns = pa_hashmap_new (pa_idxset_trivial_hash_func, + pa_idxset_trivial_compare_func); + return am; +} + +void pa_audiomgr_done (struct userdata *u) +{ + pa_audiomgr *am; + + if (u && (am = u->audiomgr)) { + //if (u->routerif && am->domain.id != AM_ID_INVALID) + // pa_routerif_unregister_domain (u, am->domain.id); + + pa_hashmap_free (am->nodes); + pa_hashmap_free (am->conns); + pa_xfree ((void *)am->domain.name); + pa_xfree (am); + u->audiomgr = NULL; + } +} + diff --git a/audiomgr.h b/audiomgr.h new file mode 100644 index 0000000..c668a27 --- /dev/null +++ b/audiomgr.h @@ -0,0 +1,30 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglaudiomgr +#define paaglaudiomgr + +#include "userdata.h" + +pa_audiomgr *pa_audiomgr_init (struct userdata *); +void pa_audiomgr_done (struct userdata *); + +#endif diff --git a/classify.c b/classify.c new file mode 100644 index 0000000..23d991d --- /dev/null +++ b/classify.c @@ -0,0 +1,63 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include <pulsecore/pulsecore-config.h> /* required for headers below */ +#include <pulsecore/core-util.h> /* required for "pa_streq" */ + +#include "classify.h" + +agl_node_type pa_classify_guess_stream_node_type (struct userdata *u, pa_proplist *pl) +{ + agl_node_type type; + const char *role; + + pa_assert (u); + pa_assert (pl); + + role = pa_proplist_gets (pl, PA_PROP_MEDIA_ROLE); + + if (!role) + type = agl_node_type_unknown; + else if (pa_streq (role, "radio")) + type = agl_radio; + else if (pa_streq (role, "music")) + type = agl_player; + else if (pa_streq (role, "navi")) + type = agl_navigator; + else if (pa_streq (role, "game")) + type = agl_game; + else if (pa_streq (role, "browser")) + type = agl_browser; + else if (pa_streq (role, "camera")) + type = agl_camera; + else if (pa_streq (role, "phone")) + type = agl_phone; + else if (pa_streq (role, "alert")) + type = agl_alert; + else if (pa_streq (role, "event")) + type = agl_event; + else if (pa_streq (role, "system")) + type = agl_system; + else + type = agl_player; + + return type; +} diff --git a/classify.h b/classify.h new file mode 100644 index 0000000..301a440 --- /dev/null +++ b/classify.h @@ -0,0 +1,8 @@ +#ifndef paaglclassify +#define paaglclassify + +#include "userdata.h" + +agl_node_type pa_classify_guess_stream_node_type (struct userdata *, pa_proplist *); + +#endif diff --git a/discover.c b/discover.c new file mode 100644 index 0000000..e6e8243 --- /dev/null +++ b/discover.c @@ -0,0 +1,758 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include <pulsecore/pulsecore-config.h> /* required for "core-util.h" */ +#include <pulsecore/core-util.h> /* requred for "pa_streq" */ +#include <pulsecore/device-port.h> /* required for "card.h" */ +#include <pulsecore/card.h> /* for "struct pa_card", "struct pa_card_profile" */ + +#include "discover.h" +#include "node.h" +#include "utils.h" +#include "classify.h" +#include "router.h" + +#define MAX_CARD_TARGET 4 /* max number of managed sinks/sources per card */ +#define MAX_NAME_LENGTH 256 /* max sink/source name length */ + +static void handle_alsa_card (struct userdata *, pa_card *); +static void handle_alsa_card_sinks_and_sources (struct userdata *, pa_card *, agl_node *, const char *); +static void get_sinks_and_sources_from_profile (pa_card_profile *, char **, char **, char *, int); +static char *get_sink_or_source_from_str (char **, int); +static void handle_card_ports (struct userdata *, agl_node *, pa_card *, pa_card_profile *); +static const char *node_key (struct userdata *, agl_direction, + void *, pa_device_port *, char *, size_t); +static agl_node *create_node (struct userdata *, agl_node *, bool *); + +struct pa_discover *pa_discover_init (struct userdata *u) +{ + pa_discover *discover = pa_xnew0 (pa_discover, 1); + discover->chmin = 1; + discover->chmax = 2; + discover->selected = true; + discover->nodes.byname = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + discover->nodes.byptr = pa_hashmap_new (pa_idxset_trivial_hash_func, + pa_idxset_trivial_compare_func); + + return discover; +} + +void pa_discover_done (struct userdata *u) +{ + pa_discover *discover; + void *state; + agl_node *node; + + if (u && (discover = u->discover)) { + /*PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) { + agl_node_destroy(u, node); + }*/ + pa_hashmap_free (discover->nodes.byname); + pa_hashmap_free (discover->nodes.byptr); + pa_xfree (discover); + u->discover = NULL; + } +} + +void pa_discover_add_card (struct userdata *u, pa_card *card) +{ + const char *bus; + + pa_assert(u); + pa_assert(card); + + if (!(bus = pa_utils_get_card_bus (card))) { + pa_log_debug ("ignoring card '%s' due to lack of '%s' property", + pa_utils_get_card_name (card), PA_PROP_DEVICE_BUS); + return; + } + + if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) { + pa_log_debug ("adding card '%s' thanks to its '%s' property", + pa_utils_get_card_name (card), PA_PROP_DEVICE_BUS); + pa_log_debug ("card type is '%s'", bus); + handle_alsa_card (u, card); + return; + } +/* this never happens, because "pa_utils_get_card_bus()" never returns "bluetooth", + * but have this here as a reminder */ +#if 0 + else if (pa_streq(bus, "bluetooth")) { + handle_bluetooth_card(u, card); + return; + } +#endif + + pa_log_debug ("ignoring card '%s' due to unsupported bus type '%s'", + pa_utils_get_card_name (card), bus); +} + +void pa_discover_remove_card (struct userdata *u, pa_card *card) +{ + const char *bus; + pa_discover *discover; + agl_node *node; + void *state; + + pa_assert (u); + pa_assert (card); + pa_assert_se (discover = u->discover); + + if (!(bus = pa_utils_get_card_bus(card))) + bus = "<unknown>"; + + /*PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) { + if (node->implement == agl_device && + node->pacard.index == card->index) + { + if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) + agl_constrain_destroy (u, node->paname); + + destroy_node(u, node); + }*/ + + /* this never happens, because "pa_utils_get_card_bus()" never returns "bluetooth", + * but have this here as a reminder */ +#if 0 + if (pa_streq(bus, "bluetooth")) + agl_constrain_destroy(u, card->name); +#endif +} + +void pa_discover_add_sink (struct userdata *u, pa_sink *sink, bool route) +{ + pa_core *core; + pa_discover *discover; + pa_module *module; + pa_card *card; + char kbf[256]; + const char *key; + agl_node *node; + pa_source *null_source; + + pa_assert (u); + pa_assert (sink); + pa_assert_se (core = u->core); + pa_assert_se (discover = u->discover); + + module = sink->module; + card = sink->card; + + if (card) { + /* helper function verifying that sink direction is input/output */ + key = node_key (u, agl_output, sink, NULL, kbf, sizeof(kbf)); + if (!key) return; + pa_log_debug ("Sink key: %s", key); + node = pa_discover_find_node_by_key (u, key); + if (!node) { /* ALWAYS NULL, IF CALL "handle_card_ports" FROM "handle_alsa_card" !!! */ + if (u->state.profile) + pa_log_debug ("can't find node for sink (key '%s')", key); + else { /* how do we get here ? not in initial setup */ + u->state.sink = sink->index; + pa_log_debug ("BUG ! Did you call handle_card_ports() ?"); + } + return; + } + pa_log_debug("node for '%s' found (key %s). Updating with sink data", + node->paname, node->key); + node->paidx = sink->index; + node->available = true; + pa_discover_add_node_to_ptr_hash (u, sink, node); + +#if 0 + /* loopback part : it is a device node, use "module-loopback" to make its */ + if (node->implement == agl_device) { + null_source = pa_utils_get_null_source (u); + if (!null_source) { + pa_log ("Can't load loopback module: no initial null source"); + return; + } + } +#endif + } +} + + +static void handle_alsa_card (struct userdata *u, pa_card *card) +{ + agl_node data; + const char *cnam; /* PulseAudio name */ + const char *cid; /* short PulseAudio name (with "alsa_name." stripped) */ + const char *alsanam; /* ALSA name */ + const char *udd; /* managed by udev ("1" = yes) */ + + memset (&data, 0, sizeof(data)); + data.zone = pa_utils_get_zone (card->proplist, NULL); + data.visible = true; + data.amid = AM_ID_INVALID; + data.implement = agl_device; /* this node is a physical device */ + data.paidx = PA_IDXSET_INVALID; + data.stamp = pa_utils_get_stamp (); /* each time incremented by one */ + + cnam = pa_utils_get_card_name (card); /* PulseAudio name */ + cid = cnam + 10; /* PulseAudio short name, with "alsa_card." prefix removed */ + alsanam = pa_proplist_gets (card->proplist, "alsa.card_name"); /* ALSA name */ + udd = pa_proplist_gets (card->proplist, "module-udev-detect.discovered"); + + data.amdescr = (char *)alsanam; + data.pacard.index = card->index; + + pa_log_debug ("Sound card zone: %s", data.zone); + pa_log_debug ("Sound card stamp: %d", data.stamp); + pa_log_debug ("PulseAudio card name: %s", cnam); + pa_log_debug ("PulseAudio short card name: %s", cid); + pa_log_debug ("ALSA card name: %s", alsanam); + if (udd) + pa_log_debug ("ALSA card detected by udev: %s", udd); + + /* WITH THE TIZEN MODULE, ONLY UDEV-MANAGED CARDS ARE ACCEPTED + * NO MATTER, TREAT STATIC CARDS THE SAME WAY HERE.. */ + /*if (!udd || (udd && !pa_streq(udd, "1")) + pa_log_debug ("Card not accepted, not managed by udev\n");*/ + + handle_alsa_card_sinks_and_sources (u, card, &data, cid); +} + ; +static void handle_alsa_card_sinks_and_sources (struct userdata *u, pa_card *card, agl_node *data, const char *cardid) +{ + pa_discover *discover; /* discovery restrictions (max channels...) */ + pa_card_profile *prof; + void *state; + char *sinks[MAX_CARD_TARGET+1]; /* discovered sinks array */ + char *sources[MAX_CARD_TARGET+1]; /* discovered sources array */ + char namebuf[MAX_NAME_LENGTH+1]; /* discovered sink/source name buf.*/ + const char *alsanam; + char paname[MAX_NAME_LENGTH+1]; + char amname[MAX_NAME_LENGTH+1]; + int i, j, k; + + pa_assert (card); + pa_assert (card->profiles); + pa_assert_se (discover = u->discover); + + alsanam = pa_proplist_gets (card->proplist, "alsa.card_name"); + data->paname = paname; + data->amname = amname; + data->amdescr = (char *)alsanam; + data->pacard.index = card->index; + + PA_HASHMAP_FOREACH(prof, card->profiles, state) { + /* TODO : skip selected profile here */ + + /* skip profiles withoutqx sinks/sources */ + if (!prof->n_sinks && !prof->n_sources) + continue; + /* skip profiles with too few/many channels */ + if (prof->n_sinks && + (prof->max_sink_channels < discover->chmin || + prof->max_sink_channels > discover->chmax)) + continue; + if (prof->n_sources && + (prof->max_source_channels < discover->chmin || + prof->max_source_channels > discover->chmax)) + continue; + + /* VALID PROFILE, STORE IT */ + pa_log_debug ("Discovered valid profile: %s", prof->name); + data->pacard.profile = prof->name; + /* NOW FILLING SINKS/SOURCE ARRAYS WITH PROFILE DATA */ + get_sinks_and_sources_from_profile (prof, sinks, sources, namebuf, sizeof(namebuf)); + + /* OUTPUT DIRECTION, SINKS */ + data->direction = agl_output; + data->channels = prof->max_sink_channels; + for (i = 0; sinks[i]; i++) { + pa_log_debug ("Discovered valid sink #%s on card %s", sinks[i], cardid); + snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sinks[i]); + handle_card_ports(u, data, card, prof); + } + + /* INPUT DIRECTION, SOURCES */ + data->direction = agl_input; + data->channels = prof->max_source_channels; + for (i = 0; sources[i]; i++) { + pa_log_debug ("Discovered valid source #%s on card %s", sources[i], cardid); + snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sinks[i]); + handle_card_ports(u, data, card, prof); + } + } +} + +static void get_sinks_and_sources_from_profile (pa_card_profile *prof, char **sinks, char **sources, char *buf, int buflen) +{ + char *p = buf; + int i = 0; + int j = 0; + + pa_assert (prof->name); + + strncpy (buf, prof->name, (size_t)buflen); + buf[buflen-1] = '\0'; + + memset (sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1)); + memset (sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1)); + + do { + if (!strncmp (p, "output:", 7)) { + if (i >= MAX_CARD_TARGET) { + pa_log_debug ("number of outputs exeeds the maximum %d in " + "profile name '%s'", MAX_CARD_TARGET, prof->name); + return; + } + sinks[i++] = get_sink_or_source_from_str (&p, 7); + } else if (!strncmp (p, "input:", 6)) { + if (j >= MAX_CARD_TARGET) { + pa_log_debug ("number of inputs exeeds the maximum %d in " + "profile name '%s'", MAX_CARD_TARGET, prof->name); + return; + } + sources[j++] = get_sink_or_source_from_str (&p, 6); + } else { + pa_log ("%s: failed to parse profile name '%s'", + __FILE__, prof->name); + return; + } + } while (*p); +} + +static char *get_sink_or_source_from_str (char **string_ptr, int offs) +{ + char c, *name, *end; + + name = *string_ptr + offs; + + for (end = name; (c = *end); end++) { + if (c == '+') { + *end++ = '\0'; + break; + } + } + + *string_ptr = end; + + return name; +} + +static void handle_card_ports (struct userdata *u, agl_node *data, pa_card *card, pa_card_profile *prof) { + agl_node *node = NULL; + pa_device_port *port; + void *state; + bool created; + bool have_ports = false; + char key[MAX_NAME_LENGTH+1]; + const char *amname = data->amname; + + pa_assert (u); + pa_assert (data); + pa_assert (card); + pa_assert (prof); + + if (card->ports) { + PA_HASHMAP_FOREACH (port, card->ports, state) { + if (port->profiles && + pa_hashmap_get (port->profiles, prof->name) && + ((port->direction == PA_DIRECTION_INPUT && data->direction == agl_input)|| + (port->direction == PA_DIRECTION_OUTPUT && data->direction == agl_output))) { + have_ports = true; + snprintf (key, sizeof(key), "%s@%s", data->paname, port->name); + + data->key = key; + data->available = (port->available != PA_AVAILABLE_NO); + data->type = 0; + data->amname = amname; + data->paport = port->name; + + printf ("Key : %s\n", key); + + /* this is needed to fill the "port->type" field */ + //pa_classify_node_by_card (data, card, prof, port); + + /* this is needed to complete the "pa_discover_add_sink" first pass */ + node = create_node (u, data, &created); + } + } + } + + amname = ""; + data->amname = amname; +} + +static const char *node_key (struct userdata *u, agl_direction direction, + void *data, pa_device_port *port, char *buf, size_t len) { + char *type; /* "sink" or "source" */ + const char *name; /* sink or source name */ + pa_card *card; + pa_card_profile *profile; + const char *profile_name; + const char *bus; + char *key = NULL; + + pa_assert (u); + pa_assert (data); + pa_assert (buf); + pa_assert (direction == agl_input || direction == agl_output); + + if (direction == agl_output) { + pa_sink *sink = data; + type = pa_xstrdup ("sink"); + name = pa_utils_get_sink_name (sink); + card = sink->card; + if (!port) + port = sink->active_port; + } else { + pa_source *source = data; + type = pa_xstrdup ("source"); + name = pa_utils_get_source_name (source); + card = source->card; + if (!port) + port = source->active_port; + } + + pa_log_debug ("Node type (sink/source): %s", type); + pa_log_debug ("Node name: %s", name); + + if (!card) + return NULL; + + pa_assert_se (profile = card->active_profile); + if (!u->state.profile) { + pa_log_debug ("profile is now '%s'", profile->name); + profile_name = profile->name; + } else { + pa_log_debug ("state.profile is not null. '%s' supresses '%s'", + u->state.profile, profile->name); + profile_name = u->state.profile; + } + + if (!(bus = pa_utils_get_card_bus (card))) { + pa_log_debug ("ignoring card '%s' due to lack of '%s' property", + pa_utils_get_card_name (card), PA_PROP_DEVICE_BUS); + return NULL; + } + + if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) { + if (!port) + key = (char *)name; + else { + key = buf; + snprintf (buf, len, "%s@%s", name, port->name); + } + } + /* we do not handle Bluetooth yet, and the function never returns it */ + /*else if (pa_streq(bus, "bluetooth")) { + }*/ + + return (const char *)key; +} + +agl_node *pa_discover_find_node_by_key (struct userdata *u, const char *key) +{ + pa_discover *discover; + agl_node *node; + + pa_assert (u); + pa_assert_se (discover = u->discover); + + if (key) + node = pa_hashmap_get (discover->nodes.byname, key); + else + node = NULL; + + return node; +} + +void pa_discover_add_node_to_ptr_hash (struct userdata *u, void *ptr, agl_node *node) +{ + pa_discover *discover; + + pa_assert (u); + pa_assert (ptr); + pa_assert (node); + pa_assert_se (discover = u->discover); + + pa_hashmap_put (discover->nodes.byptr, ptr, node); +} + +static agl_node *create_node (struct userdata *u, agl_node *data, bool *created_ret) +{ + pa_discover *discover; + agl_node *node; + bool created; + + pa_assert (u); + pa_assert (data); + pa_assert (data->key); + pa_assert (data->paname); + pa_assert_se (discover = u->discover); + + if ((node = pa_hashmap_get (discover->nodes.byname, data->key))) { + pa_log_debug ("No need to create this node"); + created = false; + } else { + pa_log_debug ("Creating new node"); + + node = agl_node_create (u, data); + pa_hashmap_put (discover->nodes.byname, node->key, node); + + /* TODO: registering the new node to the Optional router daemon */ + /* if (node->available) + pa_audiomgr_register_node (u, node); */ + + created = true; + } + + if (created_ret) + *created_ret = created; + + return node; +} + +void pa_discover_add_source (struct userdata *u, pa_source *source) +{ + static pa_nodeset_resdef def_resdef = {0, {0, 0}}; + + pa_core *core; + pa_discover *discover; + pa_module *module; + pa_card *card; + const char *key; + char kbf[256]; + agl_node *node; + + pa_assert (u); + pa_assert (source); + pa_assert_se (core = u->core); + pa_assert_se (discover = u->discover); + + module = source->module; + card = source->card; + + if (card) { + /* helper function verifying that sink direction is input/output */ + key = node_key (u, agl_input, source, NULL, kbf, sizeof(kbf)); + if (!key) return; + pa_log_debug ("Source key: %s", key); + node = pa_discover_find_node_by_key (u, key); + if (!node) { /* VERIFY IF THIS WORKS */ + if (u->state.profile) + pa_log_debug ("can't find node for source (key '%s')", key); + else { /* how do we get here ? not in initial setup */ + u->state.source = source->index; + pa_log_debug ("BUG !"); + } + return; + } + pa_log_debug("node for '%s' found (key %s). Updating with source data", + node->paname, node->key); + node->paidx = source->index; + node->available = true; + pa_discover_add_node_to_ptr_hash (u, source, node); + } +} + +bool pa_discover_preroute_sink_input(struct userdata *u, pa_sink_input_new_data *data) +{ + pa_core *core; + pa_sink *sink; + pa_source *source; + pa_discover *discover; + pa_nodeset *nodeset; + pa_proplist *pl; + agl_node *node; + + pa_assert (u); + pa_assert (data); + pa_assert_se (core = u->core); + pa_assert_se (discover = u->discover); + pa_assert_se (nodeset = u->nodeset); + pa_assert_se (pl = data->proplist); + + /* is there an existing matching node ? */ + node = agl_node_get_from_data (u, agl_input, data); + + if (!node) { + /* create node */ + node = agl_node_create (u, NULL); + node->direction = agl_input; + node->implement = agl_stream; + node->type = pa_classify_guess_stream_node_type (u, pl); + node->visible = true; + node->available = true; + node->ignore = true; /* gets ignored initially */ + node->paname = pa_proplist_gets (pl, PA_PROP_APPLICATION_NAME); + node->client = data->client; + /* add to global nodeset */ + pa_idxset_put (nodeset->nodes, node, &node->index); + } + + /* create NULL sink */ + if (!node->nullsink) + node->nullsink = pa_utils_create_null_sink (u, u->nsnam); + if (!node->nullsink) + return false; + + /* redirect sink input to NULL sink */ + sink = pa_utils_get_null_sink (u, node->nullsink); + + if (pa_sink_input_new_data_set_sink (data, sink, false)) + pa_log_debug ("set sink %u for new sink-input", sink->index); + else + pa_log ("can't set sink %u for new sink-input", sink->index); + + return true; +} + +void pa_discover_register_sink_input (struct userdata *u, pa_sink_input *sinp) +{ + pa_core *core; + pa_discover *discover; + pa_proplist *pl, *client_proplist; + const char *media; + const char *name; + const char *role; + agl_node_type type; + agl_node node_data, *node; + char key[256]; + pa_sink *sink; + + pa_assert (u); + pa_assert (sinp); + pa_assert_se (core = u->core); + pa_assert_se (discover = u->discover); + pa_assert_se (pl = sinp->proplist); + + media = pa_proplist_gets (sinp->proplist, PA_PROP_MEDIA_NAME); + if (media) { + /* special treatment for combine/loopback streams */ + pa_log_debug ("Stream may me combine/loopback, in this case we should ignore it, but use it for now"); + /* return; */ + } + + name = pa_utils_get_sink_input_name (sinp); + client_proplist = sinp->client ? sinp->client->proplist : NULL; + + pa_log_debug ("registering input stream '%s'", name); + + /* we could autodetect sink type by using: + * - PA_PROP_APPLICATION_PROCESS_ID; + * - PA_PROP_APPLICATION_PROCESS_BINARY; + * - PA_PROP_APPLICATION_NAME; + * - ... + but let us assume "agl_player" for now */ + type = agl_player; + + /* this sets our routing properties on the sink, which are : + * #define PA_PROP_ROUTING_CLASS_NAME "routing.class.name" + * #define PA_PROP_ROUTING_CLASS_ID "routing.class.id" + * #define PA_PROP_ROUTING_METHOD "routing.method" */ + pa_utils_set_stream_routing_properties (pl, type, NULL); + + /* we now create a new node for this sink */ + memset (&node_data, 0, sizeof(node_data)); + node_data.key = key; + node_data.direction = agl_input; + node_data.implement = agl_stream; + node_data.channels = sinp->channel_map.channels; + node_data.type = type; + node_data.zone = pa_utils_get_zone (sinp->proplist, client_proplist); + node_data.visible = true; + node_data.available = true; + node_data.amname = pa_proplist_gets (pl, "resource.set.appid"); + node_data.amdescr = pa_proplist_gets (pl, PA_PROP_MEDIA_NAME); + node_data.amid = AM_ID_INVALID; + node_data.paname = (char *)name; + node_data.paidx = sinp->index; + /*node_data.rset.id = pa_utils_get_rsetid (pl, idbuf, sizeof(idbuf));*/ + + role = pa_proplist_gets (sinp->proplist, PA_PROP_MEDIA_ROLE); + /* MAIN PREROUTING DONE HERE ! (this was failing in the samples) */ + /*sink = mir_router_make_prerouting (u, &data, &sinp->channel_map, role, &target);*/ + + node = create_node (u, &node_data, NULL); + pa_assert (node); + pa_discover_add_node_to_ptr_hash (u, sinp, node); +/* + if (sink && target) { + pa_log_debug ("move stream to sink %u (%s)", sink->index, sink->name); + + if (pa_sink_input_move_to (sinp, sink, false) < 0) + pa_log ("failed to route '%s' => '%s'",node->amname,target->amname); + else + pa_audiomgr_add_default_route (u, node, target); + }*/ +} + +void pa_discover_register_source_output (struct userdata *u, pa_source_output *sout) +{ + pa_core *core; + pa_discover *discover; + pa_proplist *pl, *client_proplist; + const char *media; + const char *name; + const char *role; + agl_node_type type; + agl_node node_data, *node; + char key[256]; + pa_source *source; + + pa_assert (u); + pa_assert (sout); + pa_assert_se (core = u->core); + pa_assert_se (discover = u->discover); + pa_assert_se (pl = sout->proplist); + + media = pa_proplist_gets (sout->proplist, PA_PROP_MEDIA_NAME); + if (media) { + /* special treatment for loopback streams (not combine as with sinks !) */ + pa_log_debug ("Stream may me loopback, in this case we should ignore it, but use it for now"); + /* return; */ + } + + /* TODO : contrary to "pa_discover_register_sink_input", this function is always called + * even if we do not find PA_PROP_MEDIA_NAME (see above). */ + //pa_utils_set_stream_routing_properties (pl, type, NULL); + + name = pa_utils_get_source_output_name (sout); + client_proplist = sout->client ? sout->client->proplist : NULL; + + pa_log_debug("registering output stream '%s'", name); +} + +void pa_discover_add_sink_input (struct userdata *u, pa_sink_input *sinp) +{ + pa_core *core; + agl_node *node; + + pa_assert (u); + pa_assert (sinp); + pa_assert_se (core = u->core); + + if (!sinp->client) + return; + + /* is there an existing matching node ? */ + node = agl_node_get_from_client (u, sinp->client); + if (!node) return; + + /* start the default routing */ + implement_default_route (u, node, NULL, pa_utils_new_stamp ()); +} diff --git a/discover.h b/discover.h new file mode 100644 index 0000000..8845e40 --- /dev/null +++ b/discover.h @@ -0,0 +1,58 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paagldiscover +#define paagldiscover + +#include "userdata.h" + +struct pa_discover { + /* criteria for filtering sinks and sources */ + unsigned chmin; /**< minimum of max channels */ + unsigned chmax; /**< maximum of max channels */ + bool selected; /**< for alsa cards: whether to consider the + selected profile alone. + for bluetooth cards: no effect */ + struct { + pa_hashmap *byname; + pa_hashmap *byptr; + } nodes; +}; + +struct pa_discover *pa_discover_init (struct userdata *); +void pa_discover_done (struct userdata *); + +void pa_discover_add_card (struct userdata *, pa_card *); +void pa_discover_remove_card (struct userdata *, pa_card *); +void pa_discover_add_sink (struct userdata *, pa_sink *, bool); +void pa_discover_remove_sink (struct userdata *, pa_sink *); +void pa_discover_add_source (struct userdata *, pa_source *); +void pa_discover_remove_source (struct userdata *, pa_source *); +void pa_discover_add_sink_input (struct userdata *, pa_sink_input *); +void pa_discover_remove_sink_input (struct userdata *, pa_sink_input *); +bool pa_discover_preroute_sink_input (struct userdata *, pa_sink_input_new_data *); +void pa_discover_register_sink_input (struct userdata *, pa_sink_input *); +void pa_discover_register_source_output (struct userdata *, pa_source_output *); + +agl_node *pa_discover_find_node_by_key (struct userdata *, const char *); +void pa_discover_add_node_to_ptr_hash (struct userdata *, void *, agl_node *); + +#endif @@ -0,0 +1,48 @@ +#ifndef paagllist +#define paagllist + +#define AGL_DLIST_INIT(self) \ + do { \ + (&(self))->prev = &(self); \ + (&(self))->next = &(self); \ + } while(0) + +#define AGL_OFFSET(structure, member) \ + ((int)((char *)((&((structure *)0)->member)) - (char *)0)) + +#define AGL_LIST_RELOCATE(structure, member, ptr) \ + ((structure *)(void *)((char *)ptr - AGL_OFFSET(structure, member))) + +#define AGL_DLIST_FOR_EACH(structure, member, pos, head) \ + for (pos = AGL_LIST_RELOCATE(structure, member, (head)->next); \ + &pos->member != (head); \ + pos = AGL_LIST_RELOCATE(structure, member, pos->member.next)) + +#define AGL_DLIST_FOR_EACH_SAFE(structure, member, pos, n, head) \ + for (pos = AGL_LIST_RELOCATE(structure, member, (head)->next), \ + n = AGL_LIST_RELOCATE(structure, member, pos->member.next); \ + &pos->member != (head); \ + pos = n, \ + n = AGL_LIST_RELOCATE(structure, member, pos->member.next)) + +#define AGL_DLIST_FOR_EACH_BACKWARDS(structure, member, pos, head) \ + for (pos = AGL_LIST_RELOCATE(structure, member, (head)->prev); \ + &pos->member != (head); \ + pos = AGL_LIST_RELOCATE(structure, member, pos->member.prev)) + +#define AGL_DLIST_UNLINK(structure, member, elem) \ + do { \ + agl_dlist *after = (elem)->member.prev; \ + agl_dlist *before = (elem)->member.next; \ + after->next = before; \ + before->prev = after; \ + (elem)->member.prev = (elem)->member.next = &(elem)->member; \ + } while(0) + + +typedef struct agl_dlist { + struct agl_dlist *prev; + struct agl_dlist *next; +} agl_dlist; + +#endif diff --git a/loopback.c b/loopback.c new file mode 100644 index 0000000..5b07ef7 --- /dev/null +++ b/loopback.c @@ -0,0 +1,112 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "loopback.h" +#include "utils.h" +#include "list.h" + +pa_loopback *pa_loopback_init (void) +{ + pa_loopback *loopback = pa_xnew0 (pa_loopback, 1); + + return loopback; +} + +void pa_loopback_done (struct userdata *u, pa_loopback *loopback) +{ + pa_loopnode *loop, *n; + pa_core *core; + + pa_assert_se (core = u->core); + + PA_LLIST_FOREACH_SAFE(loop, n, loopback->loopnodes) { + pa_module_unload_by_index(core, loop->module_index, false); + } +} + +pa_loopnode *pa_loopnode_create (struct userdata *u, pa_loopnode_type type, + uint32_t node_index, uint32_t source_index, uint32_t sink_index) +{ + pa_core *core; + pa_module *module; + pa_source *source; + pa_sink *sink; + const char *sonam; + const char *sinam; + pa_source_output *sout; + pa_sink_input *sinp; + pa_loopnode *loopnode; + int idx; + char args[256]; + + pa_assert (u); + pa_assert_se (core = u->core); + + source = pa_idxset_get_by_index (core->sources, source_index); + sonam = pa_utils_get_source_name (source); + sink = pa_idxset_get_by_index (core->sinks, sink_index); + sinam = pa_utils_get_sink_name (sink); + + snprintf (args, sizeof(args), "source=\"%s\" sink=\"%s\"", sonam, sinam); + module = pa_module_load (core, "module-loopback", args); + + if (!module) { + pa_log ("failed to load loopback for source '%s' & sink '%s'", sonam, sinam); + return NULL; + } + + /* find the sink_input/source_output couple generated but the module we just loaded */ + PA_IDXSET_FOREACH(sout, core->source_outputs, idx) { + if (sout->module && sout->module == module) + break; + sout = NULL; + } + PA_IDXSET_FOREACH(sinp, core->sink_inputs, idx) { + if (sinp->module && sinp->module == module) + break; + sinp = NULL; + } + if (!sout || !sinp) { + pa_module_unload (core, module, false); + return NULL; + } + + loopnode = pa_xnew0 (pa_loopnode, 1); + loopnode->module_index = module->index; + loopnode->source_output_index = sout->index; + loopnode->sink_input_index = sinp->index; + + return loopnode; +} + +void pa_loopnode_destroy (struct userdata *u, pa_loopnode *loopnode) +{ + pa_core *core; + pa_module *module; + + if (u && (core = u->core)) { + if ((module = pa_idxset_get_by_index (core->modules, loopnode->module_index))){ + pa_log_info ("unloading loopback"); + pa_module_unload (core, module, false); + } + pa_xfree (loopnode); + } +} diff --git a/loopback.h b/loopback.h new file mode 100644 index 0000000..27cdef9 --- /dev/null +++ b/loopback.h @@ -0,0 +1,58 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglloopback +#define paaglloopback + +#include <stdint.h> + +#include <pulsecore/pulsecore-config.h> /* required for "core.h" */ +#include <pulsecore/core.h> /* required for "PA_LLIST_*" */ + +#include "userdata.h" + +typedef struct pa_loopnode pa_loopnode; +struct pa_loopnode { + PA_LLIST_FIELDS(pa_loopnode); + uint32_t module_index; + uint32_t node_index; + uint32_t sink_input_index; + uint32_t source_output_index; +}; + +typedef struct pa_loopback { + PA_LLIST_HEAD(pa_loopnode, loopnodes); +} pa_loopback; + +typedef enum { + PA_LOOPNODE_TYPE_UNKNOWN = 0, + PA_LOOPNODE_SOURCE, + PA_LOOPNODE_SINK, +} pa_loopnode_type; + +pa_loopback *pa_loopback_init (void); +void pa_loopback_done (struct userdata *, pa_loopback *); + +pa_loopnode *pa_loopnode_create (struct userdata *, pa_loopnode_type, + uint32_t, uint32_t, uint32_t); +void pa_loopnode_destroy (struct userdata *, pa_loopnode *); + +#endif diff --git a/module.c b/module.c new file mode 100644 index 0000000..beb2c73 --- /dev/null +++ b/module.c @@ -0,0 +1,149 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ + +/* Load the module with : +$ pulseaudio --system --dl-search-path=/mypath +$ pactl load-module mypamodule + (PS : /mypath must be world-readable, because PulseAudio drops + its own privileges after initial startup) +*/ + + /* THIS IS PROVIDED BY "pulseaudio-module-devel" package */ + +#include <pulsecore/pulsecore-config.h> /* required for "module.h" */ +#include <pulsecore/module.h> +#include <pulsecore/modargs.h> /* for "pa_modargs" */ + +#include "userdata.h" /* for "struct userdata" */ +#include "utils.h" /* for "struct pa_null_sink", "pa_utils_create_null_sink()"... */ +#include "loopback.h" /* for "struct pa_loopback/loopnode" */ +#include "zone.h" /* for "struct pa_zoneset" */ +#include "node.h" /* for "struct pa_nodeset" */ +#include "audiomgr.h" /* for "struct pa_audiomgr" */ +#include "routerif.h" /* for "struct pa_routerif" */ +#include "discover.h" /* for "struct pa_discover" */ +#include "tracker.h" /* for "struct pa_tracker" */ +#include "router.h" /* for "struct pa_router" */ + +#ifndef DEFAULT_CONFIG_DIR +#define DEFAULT_CONFIG_DIR "/etc/pulse" +#endif + +#ifndef DEFAULT_CONFIG_FILE +#define DEFAULT_CONFIG_FILE "pulseaudio-agl.cfg" +#endif + + + /* VALID ARGUMENTS */ + +static const char* const valid_modargs[] = { + "config_dir", + "config_file", + "null_sink_name", + "audiomgr_socktype", + "audiomgr_address", + "audiomgr_port", + NULL +}; + + + /* INITIALIZATION FUNCTION */ + +int pa__init (pa_module *m) +{ + pa_log ("Initializing \"pulseaudio-agl\" module"); + + struct userdata *u = NULL; + pa_modargs *ma = NULL; /* will contain module arguments */ + const char *cfgdir; + const char *cfgfile; + const char *nsnam; /* NULL sink name (default = "null.agl") */ + const char *amsocktype; /* Optional external routing daemon: socket type ("unix"/"tcp") */ + const char *amaddr; /* Optional external routing daemon: socket address (path/ip address) */ + const char *amport; /* Optional external routing daemon: socket port ("tcp" type only) */ + + pa_assert (m); + + /* treat module arguments */ + + ma = pa_modargs_new (m->argument, valid_modargs); + + cfgdir = pa_modargs_get_value (ma, "config_dir", DEFAULT_CONFIG_DIR); + cfgfile = pa_modargs_get_value (ma, "config_file", DEFAULT_CONFIG_FILE); + nsnam = pa_modargs_get_value (ma, "null_sink_name", NULL); + amsocktype = pa_modargs_get_value (ma, "audiomgr_socktype", NULL); + amaddr = pa_modargs_get_value (ma, "audiomgr_address", NULL); + amport = pa_modargs_get_value (ma, "audiomgr_port", NULL); + + pa_log ("cfgdir : %s", cfgdir); + pa_log ("cfgfile : %s", cfgfile); + + pa_utils_init_stamp (); + + /* initialize userdata */ + + u = pa_xnew0 (struct userdata, 1); + u->core = m->core; + u->module = m; + u->nsnam = pa_xstrdup (nsnam) ; + u->zoneset = pa_zoneset_init (u); + u->nodeset = pa_nodeset_init (u); + u->audiomgr = pa_audiomgr_init (u); + u->routerif = pa_routerif_init (u, amsocktype, amaddr, amport); + u->router = pa_router_init (u); + u->discover = pa_discover_init (u); + u->tracker = pa_tracker_init (u); + + m->userdata = u; + + /* really initialize the module's core logic */ + + pa_tracker_synchronize (u); + + /* end */ + + pa_modargs_free(ma); + + return 0; +} + + + /* CLOSEUP FUNCTION */ +void pa__done (pa_module *m) +{ + pa_log ("Closing \"pulseaudio-agl\" module"); + + struct userdata *u; + + pa_assert (m); + + if (u = m->userdata) { + pa_tracker_done (u); + pa_discover_done (u); + pa_routerif_done (u); + pa_audiomgr_done (u); + pa_nodeset_done (u); + pa_zoneset_done (u); + pa_xfree (u->nsnam); + pa_xfree (u); + } +} @@ -0,0 +1,179 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "node.h" + +#include <pulsecore/idxset.h> + +pa_nodeset *pa_nodeset_init (struct userdata *u) +{ + pa_nodeset *ns; + + pa_assert (u); + + ns = pa_xnew0 (pa_nodeset, 1); + ns->nodes = pa_idxset_new (pa_idxset_trivial_hash_func, + pa_idxset_trivial_compare_func); + ns->roles = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + ns->binaries = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + return ns; +} + +void pa_nodeset_done(struct userdata *u) +{ + pa_nodeset *ns; + pa_nodeset_map *role, *binary; + void *state; + int i; + + if (u && (ns = u->nodeset)) { + pa_idxset_free (ns->nodes, NULL); + + PA_HASHMAP_FOREACH(role, ns->roles, state) { + pa_xfree ((void *)role->name); + pa_xfree ((void *)role->resdef); + } + pa_hashmap_free (ns->roles); + + PA_HASHMAP_FOREACH(binary, ns->binaries, state) { + pa_xfree ((void *)binary->name); + pa_xfree ((void *)binary->resdef); + } + pa_hashmap_free (ns->binaries); + + for (i = 0; i < APCLASS_DIM; i++) + pa_xfree((void *)ns->class_name[i]); + + free(ns); + } +} + +agl_node *agl_node_create (struct userdata *u, agl_node *data) +{ + pa_nodeset *ns; + agl_node *node; + + pa_assert (u); + pa_assert_se (ns = u->nodeset); + + node = pa_xnew0 (agl_node, 1); + + pa_idxset_put (ns->nodes, node, &node->index); + + if (data) { + node->key = pa_xstrdup (data->key); + node->direction = data->direction; + node->implement = data->implement; + node->channels = data->channels; + node->location = data->location; + node->privacy = data->privacy; + node->type = data->type; + node->zone = pa_xstrdup (data->zone); + node->visible = data->visible; + node->available = data->available; + node->amname = pa_xstrdup (data->amname ? data->amname : data->paname); + node->amdescr = pa_xstrdup(data->amdescr ? data->amdescr : ""); + node->amid = data->amid; + node->paname = pa_xstrdup (data->paname); + node->stamp = data->stamp; + node->rset.id = data->rset.id ? pa_xstrdup(data->rset.id) : NULL; + node->rset.grant = data->rset.grant; + + if (node->implement == agl_device) { + node->pacard.index = data->pacard.index; + if (data->pacard.profile) + node->pacard.profile = pa_xstrdup (data->pacard.profile); + if (data->paport) + node->paport = data->paport; + } + } + + /* TODO : register the node to the router */ + /* agl_router_register_node (u, node); */ + + return node; +} + +const char *agl_node_type_str (agl_node_type type) +{ + switch (type) { + case agl_node_type_unknown: return "Unknown"; + case agl_radio: return "Radio"; + case agl_player: return "Player"; + case agl_navigator: return "Navigator"; + case agl_game: return "Game"; + case agl_browser: return "Browser"; + case agl_camera: return "Camera"; + case agl_phone: return "Phone"; + case agl_alert: return "Alert"; + case agl_event: return "Event"; + case agl_system: return "System"; + default: return "<user defined>"; + } +} + +agl_node *agl_node_get_from_data (struct userdata *u, agl_direction type, void *data) +{ + pa_sink_input_new_data *sinp_data; + pa_source_output_new_data *sout_data; + pa_nodeset *nodeset; + agl_node *node; + uint32_t index; + + pa_assert (u); + pa_assert (data); + pa_assert (nodeset = u->nodeset); + + pa_assert (type == agl_input || type == agl_output); + + /* input (= sink_input) */ + if (type == agl_input) { + sinp_data = (pa_sink_input_new_data *) data; + PA_IDXSET_FOREACH(node, nodeset->nodes, index) { + if (node->client == sinp_data->client) + return node; + } + /* output (= source_output) TODO */ + /*} else {*/ + } + + return NULL; +} + +agl_node *agl_node_get_from_client (struct userdata *u, pa_client *client) +{ + pa_nodeset *nodeset; + agl_node *node; + uint32_t index; + + pa_assert (u); + pa_assert (client); + pa_assert (nodeset = u->nodeset); + + PA_IDXSET_FOREACH(node, nodeset->nodes, index) { + if (node->client == client) + return node; + } + + return NULL; +} @@ -0,0 +1,111 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglnode +#define paaglnode + +#include <stdbool.h> +#include <stdint.h> + +#include "userdata.h" +#include "list.h" +#include "loopback.h" + +#define APCLASS_DIM (agl_application_class_end - agl_application_class_begin + 1) + +struct pa_nodeset { + pa_idxset *nodes; + pa_hashmap *roles; + pa_hashmap *binaries; + const char *class_name[APCLASS_DIM]; /* as much elements as app.classes (see in "userdata.h") */ +}; + +struct pa_nodeset_resdef { + uint32_t priority; + struct { + uint32_t rset; + uint32_t audio; + } flags; +}; + +struct pa_nodeset_map { + const char *name; + agl_node_type type; + const char *role; + pa_nodeset_resdef *resdef; +}; + +struct pa_node_card { + uint32_t index; + char *profile; +}; + +struct pa_node_rset { + char *id; /**< resource set id, if any */ + bool grant; /**< permission to play/render etc */ +}; + +struct agl_node { + uint32_t index; /**< index into nodeset->idxset */ + char *key; /**< hash key for discover lookups */ + agl_direction direction; /**< agl_input | agl_output */ + agl_implement implement; /**< agl_device | agl_stream */ + pa_client *client; /**< matching client pointer (for agl_input nodes only) */ + pa_null_sink *nullsink; /**< associated null sink (for agl_input nodes only) */ + pa_loopnode *loopnode; /**< associated loopback */ + uint32_t channels; /**< number of channels (eg. 1=mono, 2=stereo) */ + agl_location location; /**< mir_internal | mir_external */ + agl_privacy privacy; /**< mir_public | mir_private */ + agl_node_type type; /**< mir_speakers | mir_headset | ... */ + char *zone; /**< zone where the node belong */ + bool visible; /**< internal or can appear on UI */ + bool available; /**< eg. is the headset connected? */ + bool ignore; /**< do not consider it while routing */ + bool localrset; /**< locally generated resource set */ + const char *amname; /**< audiomanager name */ + const char *amdescr; /**< UI description */ + uint16_t amid; /**< handle to audiomanager, if any */ + const char *paname; /**< sink|source|sink_input|source_output name */ + uint32_t paidx; /**< sink|source|sink_input|source_output index*/ + pa_node_card pacard; /**< pulse card related data, if any */ + const char *paport; /**< sink or source port if applies */ + /*pa_muxnode *mux;*/ /**< for multiplexable input streams only */ + /*pa_loopnode *loop;*/ /**< for looped back sources only */ + agl_dlist rtentries; /**< in device nodes: listhead of nodchain */ + agl_dlist rtprilist; /**< in stream nodes: priority link (head is in + pa_router)*/ + agl_dlist constrains; /**< listhead of constrains */ + /*mir_vlim vlim;*/ /**< volume limit */ + pa_node_rset rset; /**< resource set info if applies */ + uint32_t stamp; + /*scripting_node *scripting;*/ /** scripting data, if any */ +}; + +pa_nodeset *pa_nodeset_init (struct userdata *); +void pa_nodeset_done (struct userdata *); + +agl_node *agl_node_create (struct userdata *, agl_node *); +const char *agl_node_type_str (agl_node_type); + +agl_node *agl_node_get_from_data (struct userdata *, agl_direction, void *); +agl_node *agl_node_get_from_client (struct userdata *, pa_client *); + +#endif diff --git a/router.c b/router.c new file mode 100644 index 0000000..855f9f8 --- /dev/null +++ b/router.c @@ -0,0 +1,198 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "router.h" +#include "switch.h" +#include "utils.h" + +pa_router *pa_router_init (struct userdata *u) +{ + pa_router *router; + size_t num_classes; + + num_classes = agl_application_class_end; + + router = pa_xnew0 (pa_router, 1); + router->rtgroups.input = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + router->rtgroups.output = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + router->maplen = num_classes; + router->priormap = pa_xnew0 (int, num_classes); + + AGL_DLIST_INIT (router->nodlist); + AGL_DLIST_INIT (router->connlist); + + return router; +} + +void pa_router_done (struct userdata *u) +{ + pa_router *router; + agl_node *e,*n; + agl_connection *conn, *c; + agl_rtgroup *rtg; + agl_rtgroup **map; + int i; + + if (u && (router = u->router)) { + AGL_DLIST_FOR_EACH_SAFE(agl_node, rtprilist, e,n, &router->nodlist) + AGL_DLIST_UNLINK(agl_node, rtprilist, e); + AGL_DLIST_FOR_EACH_SAFE(agl_connection, link, conn,c, &router->connlist) { + AGL_DLIST_UNLINK(agl_connection, link, conn); + pa_xfree (conn); + } + /* + PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) { + rtgroup_destroy(u, rtg); + } + PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) { + rtgroup_destroy(u, rtg); + }*/ + pa_hashmap_free (router->rtgroups.input); + pa_hashmap_free (router->rtgroups.output); + + for (i = 0; i < AGL_ZONE_MAX; i++) { + if ((map = router->classmap.input[i])) + pa_xfree(map); + if ((map = router->classmap.output[i])) + pa_xfree(map); + } + + pa_xfree (router->priormap); + pa_xfree (router); + + u->router = NULL; + } +} + +agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data) +{ + pa_router *router; + int priority; + static bool done_prerouting; + uint32_t stamp; + agl_node *start, *end; + agl_node *target; + + pa_assert (u); + pa_assert_se (router = u->router); + pa_assert_se (data->implement == agl_stream); + + //priority = node_priority (u, data); + + done_prerouting = false; + target = NULL; + stamp = pa_utils_new_stamp (); + + //make_explicit_routes (u, stamp); + + //pa_audiomgr_delete_default_routes(u); + + AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) { + //if ((start->implement == agl_device) && + // (!start->loop)) /* only manage looped real devices */ + // continue; + + /*if (priority >= node_priority (u, start)) { + target = find_default_route (u, data, stamp); + if (target) + implement_preroute (u, data, target, stamp); + else + done_prerouting = true; + }*/ + + if (start->stamp >= stamp) + continue; + + //end = find_default_route (u, start, stamp); + //if (end) + // implement_default_route(u, start, end, stamp); + } + + if (!done_prerouting) { + pa_log_debug ("Prerouting failed, trying to find default route as last resort"); + + //target = find_default_route (u, data, stamp); + //if (target) + // implement_preroute (u, data, target, stamp); + } + + return target; +} + +void agl_router_make_routing (struct userdata *u) +{ + pa_router *router; + static bool ongoing_routing; /* true while we are actively routing */ + uint32_t stamp; + agl_node *start, *end; + + pa_assert (u); + pa_assert_se (router = u->router); + + if (ongoing_routing) /* already routing, canceling */ + return; + ongoing_routing = true; + stamp = pa_utils_new_stamp (); + + pa_log_debug("stamp for routing: %d", stamp); + + // make_explicit_routes (u, stamp); + + // pa_audiomgr_delete_default_routes (u); + + AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) { + //if ((start->implement == agl_device) && + // (!start->loop)) /* only manage looped real devices */ + // continue; + + if (start->stamp >= stamp) + continue; + + end = find_default_route (u, start, stamp); + if (end) + implement_default_route (u, start, end, stamp); + } + + // pa_audiomgr_send_default_routes (u); + + ongoing_routing = false; +} + +void implement_default_route (struct userdata *u, + agl_node *start, agl_node *end, + uint32_t stamp) +{ + if (start->direction == agl_input) { + agl_switch_setup_link (u, start, end, false); + //agl_volume_add_limiting_class(u, end, volume_class(start), stamp); + } else { + agl_switch_setup_link (u, end, start, false); + } +} + +agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp) +{ + /* TODO */ + + return NULL; +} diff --git a/router.h b/router.h new file mode 100644 index 0000000..ffb9361 --- /dev/null +++ b/router.h @@ -0,0 +1,79 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglrouter +#define paaglrouter + +#include "userdata.h" +#include "list.h" +#include "node.h" + +#define AGL_ZONE_MAX 8 /* max 8 zones, demo is using 5 */ /* DEFINED IN MURPHY */ + +/*typedef bool (*agl_rtgroup_accept_t)(struct userdata *, agl_rtgroup *, agl_node *);*/ +/*typedef int (*agl_rtgroup_compare_t)(struct userdata *, agl_rtgroup *, agl_node *, agl_node *);*/ + +struct agl_rtgroup { + char *name; /**< name of the rtgroup */ + agl_dlist entries; /**< listhead of ordered rtentries */ + /*agl_rtgroup_accept_t accept;*/ /**< function pointer, whether to accept a node or not */ + /*agl_rtgroup_compare_t compare;*/ /**< function pointer, comparision for ordering */ + /*scripting_rtgroup *scripting;*/ /**< data for scripting, if any */ +}; + +typedef struct { + pa_hashmap *input; + pa_hashmap *output; +} pa_rtgroup_hash; + +typedef struct { + agl_rtgroup **input[AGL_ZONE_MAX]; + agl_rtgroup **output[AGL_ZONE_MAX]; +} pa_rtgroup_classmap; + +struct pa_router { + pa_rtgroup_hash rtgroups; + size_t maplen; /**< length of the class */ + pa_rtgroup_classmap classmap; /**< map device node types to rtgroups */ + int *priormap; /**< stream node priorities */ + agl_dlist nodlist; /**< priorized list of the stream nodes + (entry in node: rtprilist) */ + agl_dlist connlist; /**< listhead of the connections */ +}; + +struct agl_connection { + agl_dlist link; /**< list of connections */ + bool blocked; /**< true if this conflicts with another route */ + uint16_t amid; /**< audio manager connection id */ + uint32_t from; /**< source node index */ + uint32_t to; /**< destination node index */ + uint32_t stream; /**< index of the sink-input to be routed */ +}; + +pa_router *pa_router_init (struct userdata *); +void pa_router_done (struct userdata *); +agl_node *agl_router_make_prerouting (struct userdata *, agl_node *); +void agl_router_make_routing (struct userdata *); + +void implement_default_route (struct userdata *, agl_node *, agl_node *, uint32_t); +agl_node *find_default_route (struct userdata *, agl_node *, uint32_t); + +#endif diff --git a/routerif.h b/routerif.h new file mode 100644 index 0000000..05d2d96 --- /dev/null +++ b/routerif.h @@ -0,0 +1,10 @@ +#ifndef paaglrouterif +#define paaglrouterif + +#include "userdata.h" + +pa_routerif *pa_routerif_init (struct userdata *, const char *, + const char *, const char *); +void pa_routerif_done (struct userdata *); + +#endif diff --git a/socketif.c b/socketif.c new file mode 100644 index 0000000..dd29752 --- /dev/null +++ b/socketif.c @@ -0,0 +1,54 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "routerif.h" + +struct pa_routerif { + int sock; +}; + +pa_routerif *pa_routerif_init (struct userdata *u, + const char *socktyp, + const char *addr, + const char *port) +{ + pa_module *m = u->module; + pa_routerif *routerif = NULL; + + routerif = pa_xnew0 (pa_routerif, 1); + routerif->sock = -1; + + return routerif; +} + +void pa_routerif_done (struct userdata *u) +{ + pa_routerif *routerif; + + if (u && (routerif = u->routerif)) { + if (routerif->sock >= 0) + close (routerif->sock); + + pa_xfree (routerif); + + u->routerif = NULL; + } +} diff --git a/switch.c b/switch.c new file mode 100644 index 0000000..5b77180 --- /dev/null +++ b/switch.c @@ -0,0 +1,267 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include <pulsecore/pulsecore-config.h> /* required for headers below */ +#include <pulsecore/core-util.h> /* requred for "pa_streq" */ +#include <pulsecore/namereg.h> /* for PA_NAMEREG_SOURCE */ + +#include "utils.h" +#include "switch.h" +#include "node.h" + +bool agl_switch_setup_link (struct userdata *u, agl_node *from, agl_node *to, bool explicit) +{ + pa_core *core; + pa_sink *sink; + pa_source *source; + + pa_assert (u); + pa_assert_se (core = u->core); + + /* EXPLICIT ROUTES/DEFAULT ROUTES */ + + /* 1) EXPLICIT ROUTES : "FROM" AND "TO" ARE DEFINED */ + if (explicit) { + pa_assert (from); + pa_assert (to); + + switch (from->implement) { + /* STREAM SOURCE */ + case agl_stream: + switch (to->implement) { + /* STREAM TO STREAM : NOT IMPLEMENTED */ + case agl_stream: + pa_log_debug ("routing to streams not implemented"); + break; + /* STREAM TO DEVICE : OK */ + case agl_device: + //if (!setup_explicit_stream2dev_link (u, from, to)) + // return false; + break; + /* DEFAULT */ + default: + pa_log ("can't setup link: invalid sink node"); + return false; + } + break; + + /* DEVICE SOURCE : NOT IMPLEMENTED */ + case agl_device: + pa_log_debug("input device routing is not implemented yet"); + break; + + /* DEFAULT */ + default: + pa_log ("can't setup link: invalid sink node"); + return false; + } + } + + /* 2) DEFAULT ROUTES : EITHER ONE OF "FROM" AND "TO" ARE DEFINED */ + else { + pa_assert (from || to); + + /* "TO" IS DEFINED */ + if (to) { + switch (to->implement) { + /* STREAM DESTINATION */ + case agl_stream: + switch (from->implement) { + /* STREAM TO STREAM : NOT IMPLEMENTED */ + case agl_stream: + pa_log_debug ("routing to streams not implemented"); + break; + /* DEVICE TO STREAM : OK */ + case agl_device: + //if (!setup_default_dev2stream_link(u, from, to)) + // return false; + break; + /* DEFAULT */ + default: + pa_log ("can't setup link: invalid sink node"); + return false; + } + break; + + /* DEVICE DESTINATION */ + case agl_device: + /* IF THERE IS NO SOURCE : DEFAULT OUTPUT PREROUTE */ + /* if (!from) + return setup_device_output(u, to) != NULL; + else { */ + switch (from->implement) { + /* STREAM TO DEVICE : OK */ + case agl_stream: + //if (!setup_default_stream2dev_link (u, from, to)) + // return false; + break; + /* DEVICE TO DEVICE : OK */ + case agl_device: + //if (!setup_default_dev2dev_link (u, from, to)) + // return false; + break; + /* DEFAULT */ + default: + pa_log ("can't setup link: invalid source node"); + return false; + } + break; + /* } */ + + /* DEFAULT DESTINATION : NULL */ + default: + pa_log ("can't setup link"); + return false; + } + } + + /* ONLY "FROM" IS DEFINED */ + else { + /* only works with a device, use default input prerouting - TODO */ + if (from->implement == agl_device) { + pa_log_debug ("default routing for a device input is not supported yet"); + return false; + } + /* (the rest supposes "from->implement == agl_stream") */ + /* refuse unknown node types for default routing */ + if (from->type == agl_node_type_unknown) { + pa_log_debug ("default routing for unknown node type is refused"); + return false; + } + + sink = pa_utils_get_primary_alsa_sink (u); + source = pa_utils_get_null_source (u, from->nullsink); + + from->loopnode = pa_loopnode_create (u, PA_LOOPNODE_SINK, from->index, source->index, sink->index); + } + } + + //pa_log_debug ("link %s => %s is established", from->amname, to->amname); + + return true; +} + +bool agl_switch_teardown_link (struct userdata *u, agl_node *from, agl_node *to) +{ + pa_core *core; + + pa_assert (u); + pa_assert_se (core = u->core); + pa_assert (from || from->direction == agl_input); + pa_assert (to || to->direction == agl_output); + + switch (from->implement) { + case agl_stream: + switch (to->implement) { + /* STREAM TO STREAM : NOT IMPLEMENTED */ + case agl_stream: + pa_log_debug ("routing to streams is not implemented yet"); + break; + /* STREAM TO DEVICE : NOT OK */ + case agl_device: + //if (!teardown_explicit_stream2dev_link (u, from, to)) + // return false; + break; + default: + pa_log ("can't teardown link: invalid sink node"); + return false; + } + break; + + case agl_device: + /* DEVICE TO STREAM OR DEVICE TO DEVICE : NOT HANDLED */ + pa_log_debug ("input device routing is not implemented yet"); + break; + + default: + pa_log ("can't teardown link: invalid source node"); + return false; + } + + pa_log_debug("link %s => %s is torn down", from->amname, to->amname); + + return true; +} + + +bool set_port (struct userdata *u, agl_node *node) +{ + pa_core *core; + pa_sink *sink; + pa_source *source; + pa_device_port *port; + void *data = NULL; + uint32_t paidx = PA_IDXSET_INVALID; + + pa_assert (u); + pa_assert (node); + pa_assert (node->paname); + pa_assert_se (core = u->core); + + if (node->direction != agl_input && node->direction != agl_output) + return false; + if (node->implement != agl_device) + return true; + if (!node->paport) + return true; + + if (node->direction == agl_input) { + source = pa_namereg_get (core, node->paname, PA_NAMEREG_SOURCE); + if (!source) { + pa_log ("cannot set port for node '%s': source not found", node->paname); + return false; + } + + port = source->active_port; + /* active port and wanted port already match */ + if (pa_streq (node->paport, port->name)) + return true; + + /* ACTIVE CODE */ + if (pa_source_set_port (source, node->paport, false) < 0) + return false; + + data = source; + paidx = source->index; + } + + if (node->direction == agl_output) { + sink = pa_namereg_get (core, node->paname, PA_NAMEREG_SINK); + if (!sink) { + pa_log ("cannot set port for node '%s': source not found", node->paname); + return false; + } + + port = sink->active_port; + /* active port and wanted port already match */ + if (pa_streq (node->paport, port->name)) + return true; + + /* ACTIVE CODE */ + if (pa_sink_set_port (sink, node->paport, false) < 0) + return false; + + data = sink; + paidx = sink->index; + } + + return true; +} diff --git a/switch.h b/switch.h new file mode 100644 index 0000000..c454f1a --- /dev/null +++ b/switch.h @@ -0,0 +1,35 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglswitch +#define paaglswitch + +#include "userdata.h" + +bool agl_switch_setup_link (struct userdata *, agl_node *, agl_node *, bool); +bool agl_switch_teardown_link (struct userdata *, agl_node *, agl_node *); + +/*pa_source *setup_device_input(struct userdata *, agl_node *);*/ +/*pa_sink *setup_device_output(struct userdata *, agl_node *);*/ + +bool set_port (struct userdata *, agl_node *); + +#endif diff --git a/tracker.c b/tracker.c new file mode 100644 index 0000000..21ce02c --- /dev/null +++ b/tracker.c @@ -0,0 +1,371 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "tracker.h" +#include "discover.h" +#include "router.h" +#include "utils.h" + +struct pa_card_hooks { + pa_hook_slot *put; + pa_hook_slot *unlink; + pa_hook_slot *profchg; +}; + +struct pa_port_hooks { + pa_hook_slot *avail; +}; + +struct pa_sink_hooks { + pa_hook_slot *put; + pa_hook_slot *unlink; + pa_hook_slot *portchg; +}; + +struct pa_source_hooks { + pa_hook_slot *put; + pa_hook_slot *unlink; + pa_hook_slot *portchg; +}; + +struct pa_sink_input_hooks { + pa_hook_slot *neew; + pa_hook_slot *put; + pa_hook_slot *unlink; +}; + +struct pa_source_output_hooks { + pa_hook_slot *neew; + pa_hook_slot *put; + pa_hook_slot *unlink; +}; + +struct pa_tracker { + pa_card_hooks card; + pa_port_hooks port; + pa_sink_hooks sink; + pa_source_hooks source; + pa_sink_input_hooks sink_input; + pa_source_output_hooks source_output; +}; + + +static pa_hook_result_t card_put (void *, void *, void *); +static pa_hook_result_t card_unlink (void *, void *, void *); +static pa_hook_result_t card_profile_changed (void *, void *, void *); + +static pa_hook_result_t port_available_changed (void *, void *, void *); + +static pa_hook_result_t sink_put (void *, void *, void *); +static pa_hook_result_t sink_unlink (void *, void *, void *); +static pa_hook_result_t sink_port_changed (void *, void *, void *); + +static pa_hook_result_t source_put (void *, void *, void *); +static pa_hook_result_t source_unlink (void *, void *, void *); +static pa_hook_result_t source_port_changed (void *, void *, void *); + +static pa_hook_result_t sink_input_new (void *, void *, void *); +static pa_hook_result_t sink_input_put (void *, void *, void *); +static pa_hook_result_t sink_input_unlink (void *, void *, void *); + +static pa_hook_result_t source_output_new (void *, void *, void *); +static pa_hook_result_t source_output_put (void *, void *, void *); +static pa_hook_result_t source_output_unlink (void *, void *, void *); + + +pa_tracker *pa_tracker_init (struct userdata *u) +{ + pa_core *core; + pa_hook *hooks; + pa_tracker *tracker; + pa_card_hooks *card; + pa_port_hooks *port; + pa_sink_hooks *sink; + pa_source_hooks *source; + pa_sink_input_hooks *sinp; + pa_source_output_hooks *sout; + + pa_assert (u); + pa_assert_se (core = u->core); + pa_assert_se (hooks = core->hooks); + + tracker = pa_xnew0 (pa_tracker, 1); + card = &tracker->card; + port = &tracker->port; + sink = &tracker->sink; + source = &tracker->source; + sinp = &tracker->sink_input; + sout = &tracker->source_output; + + /* card */ + card->put = pa_hook_connect (hooks + PA_CORE_HOOK_CARD_PUT, + PA_HOOK_LATE, card_put, u); + card->unlink = pa_hook_connect (hooks + PA_CORE_HOOK_CARD_UNLINK, + PA_HOOK_LATE, card_unlink, u); + card->profchg = pa_hook_connect (hooks + PA_CORE_HOOK_CARD_UNLINK, + PA_HOOK_LATE, card_profile_changed, u); + + /* port */ + port->avail = pa_hook_connect (hooks + PA_CORE_HOOK_PORT_AVAILABLE_CHANGED, + PA_HOOK_LATE, port_available_changed, u); + + /* sink */ + sink->put = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_PUT, + PA_HOOK_LATE, sink_put, u); + sink->unlink = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_UNLINK, + PA_HOOK_LATE, sink_unlink, u); + sink->portchg = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_PORT_CHANGED, + PA_HOOK_LATE, sink_port_changed, u); + + /* source */ + source->put = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_PUT, + PA_HOOK_LATE, source_put, u); + source->unlink = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_UNLINK, + PA_HOOK_LATE, source_unlink, u); + source->portchg = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_PORT_CHANGED, + PA_HOOK_LATE, source_port_changed, u); + + /* sink-input */ + sinp->neew = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_INPUT_NEW, + PA_HOOK_EARLY, sink_input_new, u); + sinp->put = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_INPUT_PUT, + PA_HOOK_LATE, sink_input_put, u); + sinp->unlink = pa_hook_connect (hooks + PA_CORE_HOOK_SINK_INPUT_UNLINK, + PA_HOOK_LATE, sink_input_unlink, u); + + /* source-output */ + sout->neew = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_OUTPUT_NEW, + PA_HOOK_EARLY, source_output_new, u); + sout->put = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK, + PA_HOOK_LATE, source_output_put, u); + sout->unlink = pa_hook_connect (hooks + PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK, + PA_HOOK_LATE, source_output_unlink, u); + + return tracker; +} + +void pa_tracker_done (struct userdata *u) +{ + pa_tracker *tracker; + pa_card_hooks *card; + pa_port_hooks *port; + pa_sink_hooks *sink; + pa_source_hooks *source; + pa_sink_input_hooks *sinp; + + if (u && (tracker = u->tracker)) { + + card = &tracker->card; + pa_hook_slot_free (card->put); + pa_hook_slot_free (card->unlink); + pa_hook_slot_free (card->profchg); + + port = &tracker->port; + pa_hook_slot_free (port->avail); + + sink = &tracker->sink; + pa_hook_slot_free (sink->put); + pa_hook_slot_free (sink->unlink); + pa_hook_slot_free (sink->portchg); + + source = &tracker->source; + pa_hook_slot_free (source->put); + pa_hook_slot_free (source->unlink); + pa_hook_slot_free (source->portchg); + + sinp = &tracker->sink_input; + pa_hook_slot_free (sinp->neew); + pa_hook_slot_free (sinp->put); + pa_hook_slot_free (sinp->unlink); + + pa_xfree(tracker); + + u->tracker = NULL; + } +} + + /* main logic initialization function */ +void pa_tracker_synchronize (struct userdata *u) +{ + pa_core *core; + pa_card *card; + pa_sink *sink; + pa_source *source; + pa_sink_input *sinp; + pa_source_output *sout; + uint32_t index; + + pa_assert (u); + pa_assert_se (core = u->core); + + /* initialize "stamp" incremental card property to 0 */ + pa_utils_init_stamp (); + + /* discover.c : add each valid USB/PCI/Platform ALSA sound card */ + PA_IDXSET_FOREACH (card, core->cards, index) { + pa_discover_add_card (u, card); + } + + PA_IDXSET_FOREACH (sink, core->sinks, index) { + pa_discover_add_sink (u, sink, false); + } + + PA_IDXSET_FOREACH (source, core->sources, index) { + pa_discover_add_source (u, source); + } + + PA_IDXSET_FOREACH(sinp, core->sink_inputs, index) { + pa_discover_register_sink_input (u, sinp); + } + + PA_IDXSET_FOREACH(sout, core->source_outputs, index) { + pa_discover_register_source_output (u, sout); + } + + agl_router_make_routing (u); +} + + + /* HOOK IMPLEMENTATIONS */ +static pa_hook_result_t card_put (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t card_unlink (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t card_profile_changed (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t port_available_changed (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_put (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_unlink (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_port_changed (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_put (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_unlink (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_port_changed (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_new (void *hook_data, + void *call_data, + void *slot_data) +{ + /* main hook, called by each client in its 1st phase */ + pa_sink_input_new_data *data = (pa_sink_input_new_data *)call_data; + struct userdata *u = (struct userdata *)slot_data; + bool success; + + success = pa_discover_preroute_sink_input (u, data); + + return success ? PA_HOOK_OK : PA_HOOK_CANCEL; +} + +static pa_hook_result_t sink_input_put (void *hook_data, + void *call_data, + void *slot_data) +{ + /* called by each client in its 2nd phase */ + pa_sink_input *sinp = (pa_sink_input *)call_data; + struct userdata *u = (struct userdata *)slot_data; + + pa_discover_add_sink_input (u, sinp); + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_unlink (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_put (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_unlink (void *hook_data, + void *call_data, + void *slot_data) +{ + return PA_HOOK_OK; +} diff --git a/tracker.h b/tracker.h new file mode 100644 index 0000000..aae5a83 --- /dev/null +++ b/tracker.h @@ -0,0 +1,11 @@ +#ifndef paagltracker +#define paagltracker + +#include "userdata.h" + +pa_tracker *pa_tracker_init (struct userdata *); +void pa_tracker_done (struct userdata *); + +void pa_tracker_synchronize(struct userdata *); + +#endif diff --git a/userdata.h b/userdata.h new file mode 100644 index 0000000..d484cbf --- /dev/null +++ b/userdata.h @@ -0,0 +1,120 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paagluserdata +#define paagluserdata + +#include <pulsecore/pulsecore-config.h> /* required for "core.h" and "module.h" */ +#include <pulsecore/core.h> +#include <pulsecore/module.h> + +#define AM_ID_INVALID 65535 /* invalid state in several places */ + + +typedef struct pa_null_sink pa_null_sink; +typedef struct pa_zoneset pa_zoneset; +typedef struct pa_nodeset pa_nodeset; +typedef struct pa_audiomgr pa_audiomgr; +typedef struct pa_routerif pa_routerif; +typedef struct pa_discover pa_discover; +typedef struct pa_tracker pa_tracker; +typedef struct pa_router pa_router; + +typedef struct pa_nodeset_resdef pa_nodeset_resdef; +typedef struct pa_nodeset_map pa_nodeset_map; +typedef struct pa_node_card pa_node_card; +typedef struct pa_node_rset pa_node_rset; + +typedef struct pa_card_hooks pa_card_hooks; +typedef struct pa_port_hooks pa_port_hooks; +typedef struct pa_sink_hooks pa_sink_hooks; +typedef struct pa_source_hooks pa_source_hooks; +typedef struct pa_sink_input_hooks pa_sink_input_hooks; +typedef struct pa_source_output_hooks pa_source_output_hooks; + +typedef struct agl_zone agl_zone; +typedef struct agl_node agl_node; +typedef struct agl_rtgroup agl_rtgroup; +typedef struct agl_connection agl_connection; + +typedef struct { + char *profile; + uint32_t sink; + uint32_t source; +} pa_agl_state; + +struct userdata { + pa_core *core; + pa_module *module; + char *nsnam; + pa_zoneset *zoneset; + pa_nodeset *nodeset; + pa_audiomgr *audiomgr; + pa_routerif *routerif; + pa_router *router; + pa_discover *discover; + pa_tracker *tracker; + pa_agl_state state; +}; + + /* application classes */ +typedef enum agl_node_type { + agl_node_type_unknown = 0, + + agl_application_class_begin, + agl_radio = agl_application_class_begin, + agl_player, + agl_navigator, + agl_game, + agl_browser, + agl_camera, + agl_phone, /**< telephony voice */ + agl_alert, /**< ringtone, alarm */ + agl_event, /**< notifications */ + agl_system, /**< always audible system notifications, events */ + agl_application_class_end, +} agl_node_type; + +typedef enum agl_direction { + agl_direction_unknown = 0, + agl_input, + agl_output +} agl_direction; + +typedef enum agl_implement { + agl_implementation_unknown = 0, + agl_device, + agl_stream +} agl_implement; + +typedef enum agl_location { + agl_location_unknown = 0, + agl_internal, + agl_external +} agl_location; + +typedef enum agl_privacy { + agl_privacy_unknown = 0, + agl_public, + agl_private +} agl_privacy; + +#endif @@ -0,0 +1,277 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "userdata.h" +#include "utils.h" +#include "node.h" + +#define DEFAULT_NULL_SINK_NAME "null.agl" + +#ifndef PA_PROP_PROCESS_ENVIRONMENT +#define PA_PROP_PROCESS_ENVIRONMENT "application.process.environment" +#endif +#define PA_ZONE_NAME_DEFAULT "driver" +#define PA_PROP_ZONE_NAME "zone.name" +#define PA_PROP_ENV_ZONE PA_PROP_PROCESS_ENVIRONMENT ".AUDIO_ZONE" + +#define PA_PROP_ROUTING_CLASS_NAME "routing.class.name" +#define PA_PROP_ROUTING_CLASS_ID "routing.class.id" +#define PA_PROP_ROUTING_METHOD "routing.method" + +static uint32_t stamp; + +struct pa_null_sink { + char *name; + uint32_t module_index; + uint32_t sink_index; +}; + +pa_null_sink *pa_utils_create_null_sink (struct userdata *u, const char *name) +{ + pa_core *core; + pa_module *module; + pa_null_sink *ns; + pa_sink *s, *sink; + uint32_t idx; + char args[256]; + + pa_assert (u); + pa_assert_se (core = u->core); + + if (!name) + name = DEFAULT_NULL_SINK_NAME; /* default is "null.agl" */ + + snprintf (args, sizeof(args), "sink_name=\"%s.%d\" channels=2", name, pa_utils_new_stamp ()); + module = pa_module_load (core, "module-null-sink", args); + sink = NULL; + + if (!module) { + pa_log ("failed to load null sink '%s'", name); + return NULL; + } else { + PA_IDXSET_FOREACH(s, core->sinks, idx) { + if (s->module && s->module == module) { + sink = s; + pa_log_info("created agl null sink named '%s'", name); + break; + } + } + } + + ns = pa_xnew0 (pa_null_sink, 1); + ns->name = pa_xstrdup (name); + ns->module_index = module ? module->index : PA_IDXSET_INVALID; + ns->sink_index = sink ? sink->index : PA_IDXSET_INVALID; + + return ns; +} + +void pa_utils_destroy_null_sink (struct userdata *u, pa_null_sink *ns) +{ + pa_core *core; + pa_module *module; + + if (u && (core = u->core)) { + if ((module = pa_idxset_get_by_index (core->modules, ns->module_index))){ + pa_log_info ("unloading null sink '%s'", ns->name); + pa_module_unload (core, module, false); + } + + pa_xfree (ns->name); + pa_xfree (ns); + } +} + +pa_sink *pa_utils_get_null_sink (struct userdata *u, struct pa_null_sink *ns) +{ + pa_core *core; + pa_sink *sink; + + pa_assert (u); + pa_assert_se ((core = u->core)); + + return pa_idxset_get_by_index (core->sinks, ns->sink_index); +} + +pa_source *pa_utils_get_null_source (struct userdata *u, struct pa_null_sink *ns) +{ + pa_sink *sink; + + sink = pa_utils_get_null_sink (u, ns); + + return sink ? sink->monitor_source : NULL; +} + + +const char *pa_utils_get_card_name (pa_card *card) +{ + return (card && card->name) ? card->name : "<unknown>"; +} + +const char *pa_utils_get_card_bus (pa_card *card) +{ + const char *bus = NULL; + const char *name; + + if (card && !(bus = pa_proplist_gets (card->proplist,PA_PROP_DEVICE_BUS))) { + name = pa_utils_get_card_name (card); + if (!strncmp (name, "alsa_card.", 10)) { + if (!strncmp (name + 10, "pci-", 4)) + bus = "pci"; + else if (!strncmp (name + 10, "platform-", 9)) + bus = "platform"; + else if (!strncmp (name + 10, "usb-", 4)) + bus = "usb"; + } + } + + return (char *)bus; +} + +const char *pa_utils_get_sink_name (pa_sink *sink) +{ + return (sink && sink->name) ? sink->name : "<unknown>"; +} + +const char *pa_utils_get_source_name (pa_source *source) +{ + return (source && source->name) ? source->name : "<unknown>"; +} + +const char *pa_utils_get_sink_input_name (pa_sink_input *sinp) +{ + char *name = NULL; + + if (sinp && sinp->proplist) { + name = (char *)pa_proplist_gets (sinp->proplist, PA_PROP_APPLICATION_NAME); + if (!name) + name = (char *)pa_proplist_gets (sinp->proplist, PA_PROP_APPLICATION_PROCESS_BINARY); + if (!name) + name = "<unknown>"; + } + + return (const char *)name; +} + +const char *pa_utils_get_source_output_name (pa_source_output *sout) +{ + char *name = NULL; + + if (sout && sout->proplist) { + name = (char *)pa_proplist_gets (sout->proplist, PA_PROP_APPLICATION_NAME); + if (!name) + name = (char *)pa_proplist_gets (sout->proplist, PA_PROP_APPLICATION_PROCESS_BINARY); + if (!name) + name = "<unknown>"; + } + + return (const char *)name; +} + +pa_sink *pa_utils_get_primary_alsa_sink (struct userdata *u) +{ + pa_core *core; + pa_sink *sink; + int idx; + + pa_assert (u); + pa_assert_se ((core = u->core)); + + PA_IDXSET_FOREACH(sink, core->sinks, idx) { + if (sink->name && strstr (sink->name, "alsa_output")) + return sink; + } + + return NULL; +} + +void pa_utils_init_stamp (void) +{ + stamp = 0; +} + +uint32_t pa_utils_new_stamp (void) +{ + return ++stamp; +} + +uint32_t pa_utils_get_stamp (void) +{ + return stamp; +} + + + +char *pa_utils_get_zone (pa_proplist *pl, pa_proplist *client_props) +{ + const char *zone; + + pa_assert (pl); + + /* grab the "zone.name" PA_PROP environment variable ; + * otherwise just fall back to default "driver" zone */ + zone = pa_proplist_gets (pl, PA_PROP_ZONE_NAME); + if (!zone) { + if (!client_props || !(zone = pa_proplist_gets (client_props, PA_PROP_ENV_ZONE))) + zone = PA_ZONE_NAME_DEFAULT; + } + + return (char *)zone; +} + +bool pa_utils_set_stream_routing_properties (pa_proplist *pl, int styp, void *target) +{ + const char *clnam; /* will become "agl_player" e.g. */ + char clid[32]; /* will become "1" e.g. */ + const char *method; /* will become "default" (it target is NULL) or "explicit" */ + + pa_assert (pl); + pa_assert (styp >= 0); /* type different from "agl_node_type_unknown" */ + + clnam = agl_node_type_str (styp); + snprintf (clid, sizeof(clid), "%d", styp); + method = target ? "explicit" : "default"; + + if (pa_proplist_sets (pl, PA_PROP_ROUTING_CLASS_NAME, clnam ) < 0 || + pa_proplist_sets (pl, PA_PROP_ROUTING_CLASS_ID , clid ) < 0 || + pa_proplist_sets (pl, PA_PROP_ROUTING_METHOD , method) < 0) + { + pa_log ("failed to set some stream property"); + return false; + } + + return true; +} + +bool pa_utils_unset_stream_routing_properties (pa_proplist *pl) +{ + pa_assert (pl); + + if (pa_proplist_unset (pl, PA_PROP_ROUTING_CLASS_NAME) < 0 || + pa_proplist_unset (pl, PA_PROP_ROUTING_CLASS_ID ) < 0 || + pa_proplist_unset (pl, PA_PROP_ROUTING_METHOD ) < 0) + { + pa_log ("failed to unset some stream property"); + return false; + } + + return true; +} @@ -0,0 +1,53 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglutils +#define paaglutils + +#include <pulsecore/core.h> + +#include "userdata.h" + +struct pa_null_sink; + +struct pa_null_sink *pa_utils_create_null_sink (struct userdata *, const char *); +void pa_utils_destroy_null_sink (struct userdata *, struct pa_null_sink *); +pa_sink *pa_utils_get_null_sink (struct userdata *, struct pa_null_sink *); +pa_source *pa_utils_get_null_source (struct userdata *, struct pa_null_sink *); + + /* general helper functions */ +const char *pa_utils_get_card_name (pa_card *); +const char *pa_utils_get_card_bus (pa_card *); +const char *pa_utils_get_sink_name (pa_sink *); +const char *pa_utils_get_source_name (pa_source *); +const char *pa_utils_get_sink_input_name (pa_sink_input *); +const char *pa_utils_get_source_output_name (pa_source_output *); +pa_sink *pa_utils_get_primary_alsa_sink (struct userdata *); +void pa_utils_init_stamp (void); +uint32_t pa_utils_new_stamp (void); +uint32_t pa_utils_get_stamp (void); + + /* AM-oriented helper functions */ +char *pa_utils_get_zone (pa_proplist *, pa_proplist *); +bool pa_utils_set_stream_routing_properties (pa_proplist *, int, void *); +bool pa_utils_unset_stream_routing_properties (pa_proplist *); + +#endif @@ -0,0 +1,58 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#include "zone.h" + +struct pa_zoneset { + struct { + pa_hashmap *hash; + agl_zone *index[AGL_ZONE_MAX]; + } zones; +}; + +pa_zoneset *pa_zoneset_init (struct userdata *u) +{ + pa_zoneset *zs; + + pa_assert (u); + + zs = pa_xnew0 (pa_zoneset, 1); + zs->zones.hash = pa_hashmap_new (pa_idxset_string_hash_func, + pa_idxset_string_compare_func); + + return zs; +} + +void pa_zoneset_done (struct userdata *u) +{ + pa_zoneset *zs; + void *state; + agl_zone *zone; + + if (u && (zs = u->zoneset)) { + + PA_HASHMAP_FOREACH(zone, zs->zones.hash, state) { + pa_xfree ((void *)zone->name); + } + pa_hashmap_free (zs->zones.hash); + free (zs); + } +} @@ -0,0 +1,37 @@ +/* + * module-agl-audio -- PulseAudio module for providing audio routing support + * (forked from "module-murphy-ivi" - https://github.com/otcshare ) + * Copyright (c) 2012, Intel Corporation. + * Copyright (c) 2016, IoT.bzh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, + * MA 02110-1301 USA. + * + */ +#ifndef paaglzone +#define paaglzone + +#include "userdata.h" + +#define AGL_ZONE_MAX 8 + +struct agl_zone { + const char *name; + uint32_t index; +}; + +pa_zoneset *pa_zoneset_init (struct userdata *); +void pa_zoneset_done (struct userdata *); + +#endif |