From cf189940be05ac0df56ce188ea870e9e80579091 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 11 May 2019 22:31:12 +0200 Subject: [PATCH] Initial code import Signed-off-by: Sylvain Munaut --- COPYING | 9 + COPYING.gpl2 | 339 +++++++++++++++++ COPYING.lgpl3 | 165 +++++++++ Makefile.am | 21 ++ configure.ac | 78 ++++ git-version-gen | 151 ++++++++ include/Makefile.am | 1 + include/osmocom/e1d/proto.h | 109 ++++++ include/osmocom/e1d/proto_clnt.h | 47 +++ include/osmocom/e1d/proto_srv.h | 49 +++ libosmo-e1d.pc.in | 10 + src/Makefile.am | 38 ++ src/ctl.c | 340 +++++++++++++++++ src/e1d.h | 82 ++++ src/log.c | 41 ++ src/log.h | 33 ++ src/osmo-e1d.c | 136 +++++++ src/proto.c | 149 ++++++++ src/proto_clnt.c | 319 ++++++++++++++++ src/proto_srv.c | 260 +++++++++++++ src/usb.c | 616 +++++++++++++++++++++++++++++++ 21 files changed, 2993 insertions(+) create mode 100644 COPYING create mode 100644 COPYING.gpl2 create mode 100644 COPYING.lgpl3 create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100755 git-version-gen create mode 100644 include/Makefile.am create mode 100644 include/osmocom/e1d/proto.h create mode 100644 include/osmocom/e1d/proto_clnt.h create mode 100644 include/osmocom/e1d/proto_srv.h create mode 100644 libosmo-e1d.pc.in create mode 100644 src/Makefile.am create mode 100644 src/ctl.c create mode 100644 src/e1d.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/osmo-e1d.c create mode 100644 src/proto.c create mode 100644 src/proto_clnt.c create mode 100644 src/proto_srv.c create mode 100644 src/usb.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..42329c4 --- /dev/null +++ b/COPYING @@ -0,0 +1,9 @@ +See COPYING.* for the full licenses. + +The libosmo-e1d library is licensed under the terms of the GNU Lesser General +Public License as published by the Free Software Foundation, either version 3 +of the License, or (at your option) any later version. + +The osmo-e1d daemon is licensed under the terms of the GNU General Public +License as published by the Free Software Foundation, either version 2 of the +License, or (at your option) any later version. diff --git a/COPYING.gpl2 b/COPYING.gpl2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING.gpl2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/COPYING.lgpl3 b/COPYING.lgpl3 new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/COPYING.lgpl3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9862cd8 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 + +SUBDIRS = \ + src \ + include \ + $(NULL) + +EXTRA_DIST = \ + .version \ + $(NULL) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libosmo-e1d.pc + +@RELMAKE@ + +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..91d34e8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,78 @@ +AC_INIT([osmo-e1d], + m4_esyscmd([./git-version-gen .tarball-version]), + [openbsc@lists.osmocom.org]) + +dnl *This* is the root dir, even if an install-sh exists in ../ or ../../ +AC_CONFIG_AUX_DIR([.]) + +dnl libtool init +LT_INIT + +AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl include release helper +RELMAKE='-include osmo-release.mk' +AC_SUBST([RELMAKE]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_MKDIR_P +AC_PROG_CC +AC_PROG_INSTALL + +dnl check for pkg-config (explained in detail in libosmocore/configure.ac) +AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) +if test "x$PKG_CONFIG_INSTALLED" = "xno"; then + AC_MSG_WARN([You need to install pkg-config]) +fi +PKG_PROG_PKG_CONFIG([0.20]) + +PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1]) + +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.1.120) +PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.21) + +AC_CONFIG_MACRO_DIR([m4]) + +dnl checks for header files +AC_HEADER_STDC + +AC_ARG_ENABLE(sanitize, + [AS_HELP_STRING( + [--enable-sanitize], + [Compile with address sanitizer enabled], + )], + [sanitize=$enableval], [sanitize="no"]) +if test x"$sanitize" = x"yes" +then + CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" + CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" +fi + +AC_ARG_ENABLE(werror, + [AS_HELP_STRING( + [--enable-werror], + [Turn all compiler warnings into errors, with exceptions: + a) deprecation (allow upstream to mark deprecation without breaking builds); + b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) + ] + )], + [werror=$enableval], [werror="no"]) +if test x"$werror" = x"yes" +then + WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" + WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" + CFLAGS="$CFLAGS $WERROR_FLAGS" + CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" +fi + +AC_OUTPUT( + Makefile + src/Makefile + include/Makefile + libosmo-e1d.pc + ) diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 0000000..42cf3d2 --- /dev/null +++ b/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + [0-9]*) ;; + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..e74dd6e --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1 @@ +nobase_include_HEADERS = osmocom/e1d/proto_clnt.h osmocom/e1d/proto.h osmocom/e1d/proto_srv.h diff --git a/include/osmocom/e1d/proto.h b/include/osmocom/e1d/proto.h new file mode 100644 index 0000000..a3c94bb --- /dev/null +++ b/include/osmocom/e1d/proto.h @@ -0,0 +1,109 @@ +/* + * proto.h + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +#include + + +/* E1DP_CMD_INTF_QUERY + * filter: intf (optional) + * in: n/a + * out: array of osmo_e1dp_intf_info + * + * E1DP_CMD_LINE_QUERY + * filter: intf (required), line (optional) + * in: n/a + * out: array of osmo_e1dp_line_info + * + * E1DP_CMD_TS_QUERY + * filter: intf (required), line (required), ts (optional) + * in: n/a + * out: array of osmo_e1dp_ts_info + * + * E1DP_CMD_TS_OPEN + * filter: intf (required), line (required), ts (required) + * in: osmo_e1dp_ts_config + * out: osmo_e1dp_ts_info with the opened TS (or an invalid one with id == -1 for errors) + * + message with the file descriptor + */ + +enum osmo_e1dp_msg_type { + E1DP_CMD_INTF_QUERY = 0x00, + E1DP_CMD_LINE_QUERY = 0x01, + E1DP_CMD_TS_QUERY = 0x02, + E1DP_CMD_TS_OPEN = 0x04, + E1DP_EVT_TYPE = 0x40, + E1DP_RESP_TYPE = 0x80, + E1DP_ERR_TYPE = 0xc0, + E1DP_TYPE_MSK = 0xc0, +}; + +enum osmo_e1dp_ts_mode { + E1DP_TSMODE_OFF = 0x00, + E1DP_TSMODE_RAW = 0x10, + E1DP_TSMODE_HDLCFCS = 0x11, +}; + + +#define E1DP_MAGIC 0x00e1 +#define E1DP_MAX_LEN 4096 +#define E1DP_INVALID 0xff + + +struct osmo_e1dp_msg_hdr { + uint16_t magic; + uint16_t len; + + uint8_t type; + uint8_t intf; + uint8_t line; + uint8_t ts; +} __attribute__((packed)); + +struct osmo_e1dp_intf_info { + uint8_t id; + uint8_t n_lines; +} __attribute__((packed)); + +struct osmo_e1dp_line_info { + uint8_t id; + uint8_t status; /* TBD */ +} __attribute__((packed)); + +struct osmo_e1dp_ts_config { + uint8_t mode; +} __attribute__((packed)); + +struct osmo_e1dp_ts_info { + uint8_t id; + struct osmo_e1dp_ts_config cfg; + uint8_t status; /* TBD */ +} __attribute__((packed)); + + +struct msgb *osmo_e1dp_recv(struct osmo_fd *ofd, int *fd); +int osmo_e1dp_send(struct osmo_fd *ofd, struct msgb *msgb, int fd); diff --git a/include/osmocom/e1d/proto_clnt.h b/include/osmocom/e1d/proto_clnt.h new file mode 100644 index 0000000..9288af9 --- /dev/null +++ b/include/osmocom/e1d/proto_clnt.h @@ -0,0 +1,47 @@ +/* + * proto_clnt.h + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +#include + +struct osmo_e1dp_client; + +struct osmo_e1dp_client *osmo_e1dp_client_create(void *ctx, const char *path); +void osmo_e1dp_client_destroy(struct osmo_e1dp_client *srv); + +int osmo_e1dp_client_intf_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_intf_info **ii, int *n, + uint8_t intf); +int osmo_e1dp_client_line_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_line_info **li, int *n, + uint8_t intf, uint8_t line); +int osmo_e1dp_client_ts_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_ts_info **ti, int *n, + uint8_t intf, uint8_t line, uint8_t ts); +int osmo_e1dp_client_ts_open(struct osmo_e1dp_client *clnt, + uint8_t intf, uint8_t line, uint8_t ts, + enum osmo_e1dp_ts_mode mode); diff --git a/include/osmocom/e1d/proto_srv.h b/include/osmocom/e1d/proto_srv.h new file mode 100644 index 0000000..c5faec8 --- /dev/null +++ b/include/osmocom/e1d/proto_srv.h @@ -0,0 +1,49 @@ +/* + * proto_srv.h + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +struct osmo_e1dp_server; + +#define E1DP_SF_INTF_OPT (1<<0) +#define E1DP_SF_INTF_REQ (1<<1) +#define E1DP_SF_LINE_OPT (1<<2) +#define E1DP_SF_LINE_REQ (1<<3) +#define E1DP_SF_TS_OPT (1<<4) +#define E1DP_SF_TS_REQ (1<<5) + +typedef int (*osmo_e1dp_server_handler_fn)(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd); + +struct osmo_e1dp_server_handler { + uint8_t type; + int flags; + int payload_len; + osmo_e1dp_server_handler_fn fn; +}; + +struct osmo_e1dp_server *osmo_e1dp_server_create(void *ctx, const char *path, + struct osmo_e1dp_server_handler *handlers, void *handler_data); +void osmo_e1dp_server_destroy(struct osmo_e1dp_server *srv); diff --git a/libosmo-e1d.pc.in b/libosmo-e1d.pc.in new file mode 100644 index 0000000..fc930aa --- /dev/null +++ b/libosmo-e1d.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom E1 Daemon Protocol Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmo-e1d +Cflags: -I${includedir}/ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..42c4ec8 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,38 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool +# documentation before making any modification +LIBVERSION=0:0:0 + +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBUSB_CFLAGS) + +lib_LTLIBRARIES = libosmo-e1d.la + +libosmo_e1d_la_SOURCES = \ + proto.c \ + proto_clnt.c \ + proto_srv.c \ + $(NULL) + +libosmo_e1d_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' +libosmo_e1d_la_LIBADD = $(LIBOSMOCORE_LIBS) + + +noinst_HEADERS = \ + e1d.h \ + log.h \ + $(NULL) + + +bin_PROGRAMS = \ + osmo-e1d \ + $(NULL) + +osmo_e1d_SOURCES = \ + ctl.c \ + log.c \ + osmo-e1d.c \ + usb.c \ + $(NULL) + +osmo_e1d_LDADD = $(LIBOSMOCORE_LIBS) $(LIBUSB_LIBS) libosmo-e1d.la diff --git a/src/ctl.c b/src/ctl.c new file mode 100644 index 0000000..835811f --- /dev/null +++ b/src/ctl.c @@ -0,0 +1,340 @@ +/* + * ctl.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "e1d.h" + + +static struct e1_intf * +_e1d_find_intf(struct e1_daemon *e1d, uint8_t id) +{ + struct e1_intf *intf; + + llist_for_each_entry(intf, &e1d->interfaces, list) + if (intf->id == id) + return intf; + + return NULL; +} + +static struct e1_line * +_e1d_find_line(struct e1_intf *intf, uint8_t id) +{ + struct e1_line *line; + + llist_for_each_entry(line, &intf->lines, list) + if (line->id == id) + return line; + + return NULL; +} + +static struct e1_ts * +_e1d_get_ts(struct e1_line *line, uint8_t ts) +{ + return (ts < 32) ? &line->ts[ts] : NULL; +} + +static void +_e1d_fill_intf_info(struct osmo_e1dp_intf_info *ii, struct e1_intf *intf) +{ + ii->id = intf->id; + ii->n_lines = llist_count(&intf->lines); +} + +static void +_e1d_fill_line_info(struct osmo_e1dp_line_info *li, struct e1_line *line) +{ + li->id = line->id; + li->status = 0x00; +} + +static void +_e1d_fill_ts_info(struct osmo_e1dp_ts_info *ti, struct e1_ts *ts) +{ + ti->id = ts->id; + ti->cfg.mode = 0; + ti->status = 0; +} + + +static void +_e1d_ts_stop(struct e1_ts *ts) +{ + ts->mode = E1_TS_MODE_OFF; + + if (ts->fd > 0) { + close(ts->fd); + ts->fd = -1; + } +} + +static int +_e1d_ts_start(struct e1_ts *ts, enum e1_ts_mode mode) +{ + int ret, sd[2]; + + ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sd); + if (ret < 0) + return ret; + + ts->fd = sd[0]; + ts->mode = mode; + + if (mode == E1_TS_MODE_HDLCFCS) { + osmo_isdnhdlc_out_init(&ts->hdlc_tx, OSMO_HDLC_F_BITREVERSE); + osmo_isdnhdlc_rcv_init(&ts->hdlc_rx, OSMO_HDLC_F_BITREVERSE); + } + + int flags = fcntl(ts->fd, F_GETFL); + fcntl(ts->fd, F_SETFL, flags | O_NONBLOCK); + + return sd[1]; +} + + +static int +_e1d_ctl_intf_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) +{ + struct e1_daemon *e1d = (struct e1_daemon *)data; + struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb); + struct osmo_e1dp_intf_info *ii; + struct e1_intf *intf = NULL; + int n; + + /* Process query and find interface */ + if (hdr->intf != E1DP_INVALID) { + intf = _e1d_find_intf(e1d, hdr->intf); + n = intf ? 1 : 0; + } else { + n = llist_count(&e1d->interfaces); + } + + if (!n) + return 0; + + /* Allocate reponse */ + rmsgb->l2h = msgb_put(rmsgb, n * sizeof(struct osmo_e1dp_intf_info)); + ii = msgb_l2(rmsgb); + + memset(ii, 0x00, n * sizeof(struct osmo_e1dp_intf_info)); + + /* Fill response */ + if (intf) { + _e1d_fill_intf_info(ii, intf); + } else { + llist_for_each_entry(intf, &e1d->interfaces, list) + _e1d_fill_intf_info(ii++, intf); + } + + return 0; +} + +static int +_e1d_ctl_line_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) +{ + struct e1_daemon *e1d = (struct e1_daemon *)data; + struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb); + struct osmo_e1dp_line_info *li; + struct e1_intf *intf = NULL; + struct e1_line *line = NULL; + int n; + + /* Process query and find line */ + intf = _e1d_find_intf(e1d, hdr->intf); + if (!intf) + return 0; + + if (hdr->line != E1DP_INVALID) { + line = _e1d_find_line(intf, hdr->line); + n = line ? 1 : 0; + } else{ + n = llist_count(&intf->lines); + } + + if (!n) + return 0; + + /* Allocate reponse */ + rmsgb->l2h = msgb_put(rmsgb, n * sizeof(struct osmo_e1dp_line_info)); + li = msgb_l2(rmsgb); + + memset(li, 0x00, n * sizeof(struct osmo_e1dp_line_info)); + + /* Fill response */ + if (line) { + _e1d_fill_line_info(li, line); + } else { + llist_for_each_entry(line, &intf->lines, list) + _e1d_fill_line_info(li++, line); + } + + return 0; +} + +static int +_e1d_ctl_ts_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) +{ + struct e1_daemon *e1d = (struct e1_daemon *)data; + struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb); + struct osmo_e1dp_ts_info *ti; + struct e1_intf *intf = NULL; + struct e1_line *line = NULL; + int n; + + /* Process query and find timeslot */ + intf = _e1d_find_intf(e1d, hdr->intf); + if (!intf) + return 0; + + line = _e1d_find_line(intf, hdr->line); + if (!line) + return 0; + + n = (hdr->ts == E1DP_INVALID) ? 32 : ( + ((hdr->ts >= 0) && (hdr->ts < 31)) ? 1 : 0 + ); + + if (!n) + return 0; + + /* Allocate reponse */ + rmsgb->l2h = msgb_put(rmsgb, n * sizeof(struct osmo_e1dp_ts_info)); + ti = msgb_l2(rmsgb); + + memset(ti, 0x00, n * sizeof(struct osmo_e1dp_line_info)); + + /* Fill response */ + if (n == 1) { + _e1d_fill_ts_info(ti, &line->ts[hdr->ts]); + } else { + for (int i=0; i<32; i++) + _e1d_fill_ts_info(ti++, &line->ts[i]); + } + + return 0; +} + +static int +_e1d_ctl_ts_open(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) +{ + struct e1_daemon *e1d = (struct e1_daemon *)data; + struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb); + struct osmo_e1dp_ts_config *cfg = msgb_l2(msgb); + struct osmo_e1dp_ts_info *ti; + struct e1_intf *intf = NULL; + struct e1_line *line = NULL; + struct e1_ts *ts = NULL; + enum e1_ts_mode mode; + int ret; + + /* Process query and find timeslot */ + intf = _e1d_find_intf(e1d, hdr->intf); + if (!intf) + return 0; + + line = _e1d_find_line(intf, hdr->line); + if (!line) + return 0; + + ts = _e1d_get_ts(line, hdr->ts); + if (!ts) + return 0; + + /* Select mode */ + switch (cfg->mode) { + case E1DP_TSMODE_RAW: + mode = E1_TS_MODE_RAW; + break; + case E1DP_TSMODE_HDLCFCS: + mode = E1_TS_MODE_HDLCFCS; + break; + default: + return 0; + } + + /* If already open, close previous */ + _e1d_ts_stop(ts); + + /* Init */ + ret = _e1d_ts_start(ts, mode); + if (ret < 0) + return ret; + + *rfd = ret; + + /* Allocate response */ + rmsgb->l2h = msgb_put(rmsgb, sizeof(struct osmo_e1dp_ts_info)); + ti = msgb_l2(rmsgb); + + memset(ti, 0x00, sizeof(struct osmo_e1dp_line_info)); + + /* Fill reponse */ + ti->id = hdr->ts; + ti->cfg.mode = cfg->mode; + ti->status = 0xa5; + + return 0; +} + + +struct osmo_e1dp_server_handler e1d_ctl_handlers[] = { + { + .type = E1DP_CMD_INTF_QUERY, + .flags = E1DP_SF_INTF_OPT, + .payload_len = 0, + .fn = _e1d_ctl_intf_query, + }, + { + .type = E1DP_CMD_LINE_QUERY, + .flags = E1DP_SF_INTF_REQ | E1DP_SF_LINE_OPT, + .payload_len = 0, + .fn = _e1d_ctl_line_query, + }, + { + .type = E1DP_CMD_TS_QUERY, + .flags = E1DP_SF_INTF_REQ | E1DP_SF_LINE_REQ | E1DP_SF_TS_OPT, + .payload_len = 0, + .fn = _e1d_ctl_ts_query, + }, + { + .type = E1DP_CMD_TS_OPEN, + .flags = E1DP_SF_INTF_REQ | E1DP_SF_LINE_REQ | E1DP_SF_TS_REQ, + .payload_len = sizeof(struct osmo_e1dp_ts_config), + .fn = _e1d_ctl_ts_open, + }, + { /* guard */ }, +}; diff --git a/src/e1d.h b/src/e1d.h new file mode 100644 index 0000000..330168b --- /dev/null +++ b/src/e1d.h @@ -0,0 +1,82 @@ +/* + * e1d.h + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include + + +enum e1_ts_mode { + E1_TS_MODE_OFF = 0, + E1_TS_MODE_RAW, + E1_TS_MODE_HDLCFCS, +}; + +struct e1_ts { + struct e1_line *line; + uint8_t id; + + /* Mode */ + enum e1_ts_mode mode; + + /* HDLC handling */ + struct osmo_isdnhdlc_vars hdlc_tx; + struct osmo_isdnhdlc_vars hdlc_rx; + + uint8_t rx_buf[264]; + uint8_t tx_buf[264]; + int tx_ofs; + int tx_len; + + /* Remote end */ + int fd; +}; + +struct e1_line { + struct llist_head list; + + struct e1_intf *intf; + uint8_t id; + + void *drv_data; + + struct e1_ts ts[32]; +}; + +struct e1_intf { + struct llist_head list; + + struct e1_daemon *e1d; + uint8_t id; + + void *drv_data; + + struct llist_head lines; +}; + +struct e1_daemon { + void *ctx; + struct llist_head interfaces; +}; diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..805d355 --- /dev/null +++ b/src/log.c @@ -0,0 +1,41 @@ +/* + * log.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include "log.h" + +static const struct log_info_cat default_categories[] = { + [DE1D] = { + .name = "DE1D", + .loglevel = LOGL_DEBUG, + .enabled = 1, + }, +}; + +const struct log_info log_info = { + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..b3bed47 --- /dev/null +++ b/src/log.h @@ -0,0 +1,33 @@ +/* + * log.h + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +enum { + DE1D, +}; + +extern const struct log_info log_info; diff --git a/src/osmo-e1d.c b/src/osmo-e1d.c new file mode 100644 index 0000000..fa3fc6c --- /dev/null +++ b/src/osmo-e1d.c @@ -0,0 +1,136 @@ +/* + * osmo-e1d.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "e1d.h" +#include "log.h" + + +extern struct osmo_e1dp_server_handler e1d_ctl_handlers[]; +extern int e1_usb_probe(struct e1_daemon *e1d); +extern int e1_usb_poll(void); + + + +static void *g_e1d_ctx = NULL; +static int g_shutdown = 0; + + +static void sig_handler(int signo) +{ + fprintf(stdout, "signal %d received\n", signo); + switch (signo) { + case SIGINT: + case SIGTERM: + fprintf(stdout, "shutting down\n"); + g_shutdown = 1; + break; + case SIGABRT: + case SIGUSR1: + talloc_report(g_e1d_ctx, stderr); + talloc_report_full(g_e1d_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(g_e1d_ctx, stderr); + break; + default: + break; + } +} + + +int main(int argc, char *argv[]) +{ + struct e1_daemon *e1d = NULL; + struct osmo_e1dp_server *srv = NULL; + struct sched_param sp; + int rv; + + /* talloc init */ + g_e1d_ctx = talloc_named_const(NULL, 0, "osmo-e1d"); + msgb_talloc_ctx_init(g_e1d_ctx, 0); + + /* logging init */ + osmo_init_logging2(g_e1d_ctx, &log_info); + + /* signals init */ + signal(SIGINT, &sig_handler); + signal(SIGTERM, &sig_handler); + signal(SIGABRT, &sig_handler); + signal(SIGUSR1, &sig_handler); + signal(SIGUSR2, &sig_handler); + osmo_init_ignore_signals(); + + /* rt prio */ + memset(&sp, 0x00, sizeof(sp)); + sp.sched_priority = 50; + rv = sched_setscheduler(0, SCHED_RR, &sp); + if (rv != 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to set Real-Time priority. USB comms might be unstable.\n"); + perror("sched_setscheduler"); + } + + /* main state */ + e1d = talloc_zero(g_e1d_ctx, struct e1_daemon); + OSMO_ASSERT(e1d); + + INIT_LLIST_HEAD(&e1d->interfaces); + + /* probe devices */ + rv = e1_usb_probe(e1d); + if (rv != 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to prove usb devices\n"); + } + + /* server init */ + srv = osmo_e1dp_server_create(g_e1d_ctx, "/tmp/osmo-e1d.ctl", e1d_ctl_handlers, e1d); + OSMO_ASSERT(srv); + + /* main loop */ + while (!g_shutdown) { + osmo_select_main(1); + e1_usb_poll(); + } + + /* cleanup */ + if (srv) + osmo_e1dp_server_destroy(srv); + + talloc_free(e1d); + + return 0; +} diff --git a/src/proto.c b/src/proto.c new file mode 100644 index 0000000..04e533a --- /dev/null +++ b/src/proto.c @@ -0,0 +1,149 @@ +/* + * proto.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "log.h" + + +struct msgb * +osmo_e1dp_recv(struct osmo_fd *ofd, int *fd) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr *hdr; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + char cms[CMSG_SPACE(sizeof(int))]; + int rc; + + msgb = msgb_alloc(E1DP_MAX_LEN, "e1d proto rx message"); + + memset(&msg, 0x00, sizeof(msg)); + + iov.iov_base = msgb->data; + iov.iov_len = E1DP_MAX_LEN; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = (caddr_t) cms; + msg.msg_controllen = sizeof(cms); + + rc = recvmsg(ofd->fd, &msg, MSG_WAITALL | MSG_CMSG_CLOEXEC); + if (rc == 0) + goto err; + if (rc < (int)sizeof(struct osmo_e1dp_msg_hdr)) { + LOGP(DE1D, LOGL_ERROR, "Failed to read packet header.\n"); + goto err; + } + + msgb->l1h = msgb_put(msgb, sizeof(struct osmo_e1dp_msg_hdr)); + hdr = msgb_l1(msgb); + + if ((hdr->magic != E1DP_MAGIC) || (hdr->len < sizeof(struct osmo_e1dp_msg_hdr))) { + LOGP(DE1D, LOGL_ERROR, "Invalid packet header.\n"); + goto err; + } + + if (hdr->len > sizeof(struct osmo_e1dp_msg_hdr)) + msgb->l2h = msgb_put(msgb, hdr->len - sizeof(struct osmo_e1dp_msg_hdr)); + else + msgb->l2h = msgb->tail; + + if (fd) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg) { + memmove(fd, CMSG_DATA(cmsg), sizeof(int)); + } else { + *fd = -1; + } + } + + LOGP(DE1D, LOGL_DEBUG, "rx pkt: %d %s\n", fd ? *fd : -2, msgb_hexdump(msgb)); + + return msgb; + +err: + msgb_free(msgb); + return NULL; +} + +int +osmo_e1dp_send(struct osmo_fd *ofd, struct msgb *msgb, int fd) +{ + struct msghdr msg; + struct iovec iov; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + int rc; + + memset(&msg, 0x00, sizeof(msg)); + + iov.iov_base = msgb->data; + iov.iov_len = msgb_length(msgb); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + if (fd >= 0) { + msg.msg_control = cmsgbuf; + msg.msg_controllen = CMSG_LEN(sizeof(int)); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + *(int*) CMSG_DATA(cmsg) = fd; + } + + rc = sendmsg(ofd->fd, &msg, 0); + if (rc < 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to send packet.\n"); + perror("tx"); + } + + if (fd >= 0) + close(fd); + + LOGP(DE1D, LOGL_DEBUG, "tx pkt: %d %s\n", msgb_length(msgb), msgb_hexdump(msgb)); + + return rc; +} diff --git a/src/proto_clnt.c b/src/proto_clnt.c new file mode 100644 index 0000000..f378f5b --- /dev/null +++ b/src/proto_clnt.c @@ -0,0 +1,319 @@ +/* + * proto_clnt.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" + + +struct osmo_e1dp_client { + void *ctx; + struct osmo_fd ctl_fd; +}; + + +static int +_e1dp_client_event(struct osmo_e1dp_client *clnt, struct msgb *msgb) +{ + /* FIXME */ + return 0; +} + + +static int +_e1dp_client_read(struct osmo_fd *ofd, unsigned int flags) +{ + struct osmo_e1dp_client *clnt = ofd->data; + struct msgb *msgb; + struct osmo_e1dp_msg_hdr *hdr; + + msgb = osmo_e1dp_recv(ofd, NULL); + if (!msgb) + goto err; + + hdr = msgb_l1(msgb); + if ((hdr->type & E1DP_TYPE_MSK) != E1DP_EVT_TYPE) + goto err; + + _e1dp_client_event(clnt, msgb); + + msgb_free(msgb); + + return 0; + +err: + msgb_free(msgb); + + return -1; +} + + +struct osmo_e1dp_client * +osmo_e1dp_client_create(void *ctx, const char *path) +{ + struct osmo_e1dp_client *clnt; + int rc; + + /* Base structure init */ + clnt = talloc_zero(ctx, struct osmo_e1dp_client); + OSMO_ASSERT(clnt); + + clnt->ctx = ctx; + + /* Client socket */ + rc = osmo_sock_unix_init_ofd(&clnt->ctl_fd, SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_CONNECT); + if (rc < 0) + goto err; + + clnt->ctl_fd.cb = _e1dp_client_read; + clnt->ctl_fd.data = clnt; + + return clnt; + +err: + talloc_free(clnt); + return NULL; +} + + +void +osmo_e1dp_client_destroy(struct osmo_e1dp_client *clnt) +{ + if (!clnt) + return; + + osmo_fd_close(&clnt->ctl_fd); + talloc_free(clnt); +} + + +static int +_e1dp_client_query_base(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_msg_hdr *hdr, void *payload, int payload_len, + struct msgb **resp, int *rfd) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr *msg_hdr; + int rc, fd; + + /* Request */ + msgb = msgb_alloc(E1DP_MAX_LEN, "e1dp client request"); + OSMO_ASSERT(msgb); + + msg_hdr = (struct osmo_e1dp_msg_hdr *)msgb_put(msgb, sizeof(struct osmo_e1dp_msg_hdr)); + memcpy(msg_hdr, hdr, sizeof(struct osmo_e1dp_msg_hdr)); + + msg_hdr->magic = E1DP_MAGIC; + msg_hdr->len = sizeof(struct osmo_e1dp_msg_hdr) + payload_len; + + if (payload_len) { + msgb->l2h = msgb_put(msgb, payload_len); + memcpy(msgb_l2(msgb), payload, payload_len); + } + + rc = osmo_e1dp_send(&clnt->ctl_fd, msgb, -1); + if (rc < 0) + return rc; + + msgb_free(msgb); + + /* Response */ + int flags = fcntl(clnt->ctl_fd.fd, F_GETFL, 0); + fcntl(clnt->ctl_fd.fd, F_SETFL, flags & ~O_NONBLOCK); + + while (1) { + fd = -1; + msgb = osmo_e1dp_recv(&clnt->ctl_fd, &fd); + if (!msgb) { + rc = -EPIPE; + goto err; + } + + msg_hdr = msgb_l1(msgb); + if ((msg_hdr->type & E1DP_TYPE_MSK) != E1DP_EVT_TYPE) + break; + + _e1dp_client_event(clnt, msgb); + msgb_free(msgb); + } + + fcntl(clnt->ctl_fd.fd, F_SETFL, flags); + + if (msg_hdr->type != (hdr->type | E1DP_RESP_TYPE)) { + rc = -EPIPE; + goto err; + } + + *resp = msgb; + if (rfd) + *rfd = fd; + + return 0; +err: + fcntl(clnt->ctl_fd.fd, F_SETFL, flags); + msgb_free(msgb); + return rc; +} + +int +osmo_e1dp_client_intf_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_intf_info **ii, int *n, + uint8_t intf) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr hdr; + int rc; + + memset(&hdr, 0x00, sizeof(struct osmo_e1dp_msg_hdr)); + hdr.type = E1DP_CMD_INTF_QUERY; + hdr.intf = intf; + hdr.line = E1DP_INVALID; + hdr.ts = E1DP_INVALID; + + rc = _e1dp_client_query_base(clnt, &hdr, NULL, 0, &msgb, NULL); + if (rc) + return rc; + + *n = msgb_l2len(msgb) / sizeof(struct osmo_e1dp_intf_info); + + if (*n) { + *ii = talloc_array(clnt->ctx, struct osmo_e1dp_intf_info, *n); + memcpy(*ii, msgb_l2(msgb), *n * sizeof(struct osmo_e1dp_intf_info)); + } + + msgb_free(msgb); + + return 0; +} + +int +osmo_e1dp_client_line_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_line_info **li, int *n, + uint8_t intf, uint8_t line) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr hdr; + int rc; + + memset(&hdr, 0x00, sizeof(struct osmo_e1dp_msg_hdr)); + hdr.type = E1DP_CMD_LINE_QUERY; + hdr.intf = intf; + hdr.line = line; + hdr.ts = E1DP_INVALID; + + rc = _e1dp_client_query_base(clnt, &hdr, NULL, 0, &msgb, NULL); + if (rc) + return rc; + + *n = msgb_l2len(msgb) / sizeof(struct osmo_e1dp_line_info); + + if (*n) { + *li = talloc_array(clnt->ctx, struct osmo_e1dp_line_info, *n); + memcpy(*li, msgb_l2(msgb), *n * sizeof(struct osmo_e1dp_line_info)); + } + + msgb_free(msgb); + + return 0; +} + +int +osmo_e1dp_client_ts_query(struct osmo_e1dp_client *clnt, + struct osmo_e1dp_ts_info **ti, int *n, + uint8_t intf, uint8_t line, uint8_t ts) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr hdr; + int rc; + + memset(&hdr, 0x00, sizeof(struct osmo_e1dp_msg_hdr)); + hdr.type = E1DP_CMD_TS_QUERY; + hdr.intf = intf; + hdr.line = line; + hdr.ts = ts; + + rc = _e1dp_client_query_base(clnt, &hdr, NULL, 0, &msgb, NULL); + if (rc) + return rc; + + *n = msgb_l2len(msgb) / sizeof(struct osmo_e1dp_ts_info); + + if (*n) { + *ti = talloc_array(clnt->ctx, struct osmo_e1dp_ts_info, *n); + memcpy(*ti, msgb_l2(msgb), *n * sizeof(struct osmo_e1dp_ts_info)); + } + + msgb_free(msgb); + + return 0; +} + +int +osmo_e1dp_client_ts_open(struct osmo_e1dp_client *clnt, + uint8_t intf, uint8_t line, uint8_t ts, + enum osmo_e1dp_ts_mode mode) +{ + struct msgb *msgb; + struct osmo_e1dp_msg_hdr hdr; + struct osmo_e1dp_ts_config cfg; + int rc, tsfd; + + memset(&hdr, 0x00, sizeof(struct osmo_e1dp_msg_hdr)); + hdr.type = E1DP_CMD_TS_OPEN; + hdr.intf = intf; + hdr.line = line; + hdr.ts = ts; + + memset(&cfg, 0x00, sizeof(struct osmo_e1dp_ts_config)); + cfg.mode = mode; + + tsfd = -1; + + rc = _e1dp_client_query_base(clnt, &hdr, &cfg, sizeof(struct osmo_e1dp_ts_config), &msgb, &tsfd); + if (rc) + return rc; + + if ((tsfd < 0) || (msgb_l2len(msgb) != sizeof(struct osmo_e1dp_ts_info))) + return -EPIPE; + + msgb_free(msgb); + + return tsfd; +} diff --git a/src/proto_srv.c b/src/proto_srv.c new file mode 100644 index 0000000..c74e043 --- /dev/null +++ b/src/proto_srv.c @@ -0,0 +1,260 @@ +/* + * proto_srv.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" + + +struct osmo_e1dp_server { + void *ctx; + struct osmo_fd ctl_fd; + struct llist_head conns; + struct osmo_e1dp_server_handler *handlers; + void *handler_data; +}; + +struct osmo_e1dp_server_conn { + struct llist_head list; + struct osmo_e1dp_server *srv; + struct osmo_fd fd; +}; + + +static int +_e1dp_server_request(struct osmo_e1dp_server_conn *conn, struct msgb *msgb) +{ + struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb); + struct osmo_e1dp_msg_hdr *rhdr; + struct osmo_e1dp_server_handler *h; + struct msgb *rmsgb; + int rfd, rc; + + /* Find handler */ + h = conn->srv->handlers; + + while (h->fn) { + if (h->type == hdr->type) + break; + h++; + } + + if (!h->fn) { + LOGP(DE1D, LOGL_ERROR, "Unhandled message type: %d.\n", hdr->type); + return -1; + } + + /* Check flags */ + if (((hdr->intf == E1DP_INVALID) ? + (h->flags & E1DP_SF_INTF_REQ) : !(h->flags & (E1DP_SF_INTF_OPT | E1DP_SF_INTF_REQ))) || + ((hdr->line == E1DP_INVALID) ? + (h->flags & E1DP_SF_LINE_REQ) : !(h->flags & (E1DP_SF_LINE_OPT | E1DP_SF_LINE_REQ))) || + ((hdr->ts == E1DP_INVALID) ? + (h->flags & E1DP_SF_TS_REQ) : !(h->flags & (E1DP_SF_TS_OPT | E1DP_SF_TS_REQ)))) + { + LOGP(DE1D, LOGL_ERROR, "Invalid type/intf/line for message type: %d / (%d/%d/%d) %d.\n", + hdr->type, hdr->intf, hdr->line, hdr->ts, h->flags); + return -1; + } + + /* Check payload length */ + if ((h->payload_len >= 0) && + (h->payload_len != (msgb_length(msgb) - sizeof(struct osmo_e1dp_msg_hdr)))) + { + LOGP(DE1D, LOGL_ERROR, "Invalid payload for message type: %d / (%d/%d/%d).\n", + hdr->type, hdr->intf, hdr->line, hdr->ts); + return -1; + } + + /* Call handler */ + rmsgb = msgb_alloc(E1DP_MAX_LEN, "e1d proto tx message"); + rfd = -1; + + rmsgb->l1h = msgb_put(rmsgb, sizeof(struct osmo_e1dp_msg_hdr)); + rhdr = msgb_l1(rmsgb); + + rc = h->fn(conn->srv->handler_data, msgb, rmsgb, &rfd); + + if (rc) { + msgb_trim(rmsgb, msgb_l1len(rmsgb)); + rhdr->type = E1DP_ERR_TYPE | (rc & 0x3f); + } else { + rhdr->type = hdr->type | E1DP_RESP_TYPE; + } + + rhdr->magic = E1DP_MAGIC; + rhdr->len = msgb_length(rmsgb); + + /* Send response */ + rc = osmo_e1dp_send(&conn->fd, rmsgb, rfd); + rc = (rc <= 0) ? -EPIPE : 0; + + /* Done */ + msgb_free(rmsgb); + + return rc; +} + +static void +_e1dp_server_disconnect(struct osmo_e1dp_server_conn *conn) +{ + osmo_fd_close(&conn->fd); + llist_del(&conn->list); + talloc_free(conn); +} + +static int +_e1dp_server_read(struct osmo_fd *fd, unsigned int flags) +{ + struct osmo_e1dp_server_conn *conn = fd->data; + struct msgb *msgb; + int rc; + + msgb = osmo_e1dp_recv(fd, NULL); + if (!msgb) + goto err; + + rc = _e1dp_server_request(conn, msgb); + if (rc) + goto err; + + msgb_free(msgb); + + return 0; + +err: + /* Disconnect client */ + msgb_free(msgb); + _e1dp_server_disconnect(conn); + + return -1; +} + + +static int +_e1dp_server_accept(struct osmo_fd *fd, unsigned int flags) +{ + struct osmo_e1dp_server *srv = fd->data; + struct osmo_e1dp_server_conn *conn; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(fd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to accept a new connection.\n"); + return -1; + } + + conn = talloc_zero(srv->ctx, struct osmo_e1dp_server_conn); + if (!conn) { + LOGP(DE1D, LOGL_ERROR, "Failed to create incoming connection.\n"); + return -1; + } + + conn->srv = srv; + + conn->fd.fd = rc; + conn->fd.when = BSC_FD_READ; + conn->fd.cb = _e1dp_server_read; + conn->fd.data = conn; + + if (osmo_fd_register(&conn->fd) != 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to register incoming fd.\n"); + return -1; + } + + llist_add(&conn->list, &srv->conns); + + LOGP(DE1D, LOGL_DEBUG, "New incoming connection.\n"); + + return 0; +} + + +struct osmo_e1dp_server * +osmo_e1dp_server_create(void *ctx, const char *path, + struct osmo_e1dp_server_handler *handlers, void *handler_data) +{ + struct osmo_e1dp_server *srv; + int rc; + + /* Base structure init */ + srv = talloc_zero(ctx, struct osmo_e1dp_server); + OSMO_ASSERT(srv); + + srv->ctx = ctx; + srv->handlers = handlers; + srv->handler_data = handler_data; + + INIT_LLIST_HEAD(&srv->conns); + + /* Server socket */ + rc = osmo_sock_unix_init_ofd(&srv->ctl_fd, SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND); + if (rc < 0) + goto err; + + srv->ctl_fd.cb = _e1dp_server_accept; + srv->ctl_fd.data = srv; + + return srv; + +err: + talloc_free(srv); + return NULL; +} + +void +osmo_e1dp_server_destroy(struct osmo_e1dp_server *srv) +{ + struct osmo_e1dp_server_conn *conn, *tmp; + + if (!srv) + return; + + /* Disconnect all clients */ + llist_for_each_entry_safe(conn, tmp, &srv->conns, list) { + _e1dp_server_disconnect(conn); + } + + /* Resource release */ + osmo_fd_close(&srv->ctl_fd); + talloc_free(srv); +} diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..4968e41 --- /dev/null +++ b/src/usb.c @@ -0,0 +1,616 @@ +/* + * usb.c + * + * (C) 2019 by Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "e1d.h" +#include "log.h" + + +#define USB_VID 0x1d50 +#define USB_PID 0xe1e1 + +libusb_context *g_usb = NULL; + + +struct e1_usb_flow; + + +/* Driver data */ + +struct e1_usb_line_data { + /* Interface */ + uint8_t if_num; + + /* End Points */ + uint8_t ep_in; + uint8_t ep_out; + uint8_t ep_fb; + + /* Max packet size */ + int pkt_size; + + /* Flow */ + struct e1_usb_flow *flow_in; + struct e1_usb_flow *flow_out; + struct e1_usb_flow *flow_fb; + + /* Rate regulation */ + uint32_t r_acc; + uint32_t r_sw; +}; + +struct e1_usb_intf_data { + libusb_device_handle *devh; +}; + + +/* Flow */ + +struct e1_usb_flow_entry { + uint8_t *buf; + struct libusb_transfer *xfr; +}; + +typedef int (*xfer_cb_t)(struct e1_usb_flow *flow, uint8_t *buf, int size); + +struct e1_usb_flow { + struct e1_line *line; + xfer_cb_t cb; + + uint8_t ep; + + int count; + int size; + int ppx; + + struct e1_usb_flow_entry *entries; +}; + + + +// --------------------------------------------------------------------------- +// USB data transfer +// --------------------------------------------------------------------------- + +static int +_e1_rx_hdlcfs(struct e1_ts *ts, uint8_t *buf, int len) +{ + int rv, cl, oi; + + oi = 0; + + while (oi < len) { + rv = osmo_isdnhdlc_decode(&ts->hdlc_rx, + &buf[oi], len-oi, &cl, + ts->rx_buf, sizeof(ts->rx_buf) + ); + + if (rv > 0) { + printf("RX Message: %d %d [ %s]\n", ts->id, rv, osmo_hexdump(ts->rx_buf, rv)); + write(ts->fd, ts->rx_buf, rv); + } else if (rv < 0 && ts->id == 4) { + printf("ERR RX: %d %d %d [ %s]\n",rv,oi,cl, osmo_hexdump(buf, len)); + } + + oi += cl; + } + + return 0; +} + +static int +_e1_tx_hdlcfs(struct e1_ts *ts, uint8_t *buf, int len) +{ + int rv, oo, cl; + + oo = 0; + + while (oo < len) { + /* Pending message ? */ + if (!ts->tx_len) { + rv = read(ts->fd, ts->tx_buf, sizeof(ts->tx_buf)); + if (rv > 0) { + printf("TX Message: %d %d [ %s]\n", ts->id, rv, osmo_hexdump(ts->tx_buf, rv)); + ts->tx_len = rv; + ts->tx_ofs = 0; + } + } + + /* */ + rv = osmo_isdnhdlc_encode(&ts->hdlc_tx, + &ts->tx_buf[ts->tx_ofs], ts->tx_len - ts->tx_ofs, &cl, + &buf[oo], len - oo + ); + + if (rv < 0) + printf("ERR TX: %d\n", rv); + + if (ts->tx_ofs < ts->tx_len) + printf("TX chunk %d/%d %d [ %s]\n", ts->tx_ofs, ts->tx_len, cl, osmo_hexdump(&buf[ts->tx_ofs], rv)); + + if (rv > 0) + oo += rv; + + ts->tx_ofs += cl; + if (ts->tx_ofs >= ts->tx_len) { + ts->tx_len = 0; + ts->tx_ofs = 0; + } + } + + return len; +} + +static int +e1_usb_xfer_in(struct e1_usb_flow *flow, uint8_t *buf, int size) +{ + struct e1_line *line = flow->line; + int ftr; + + if (size <= 0) { + printf("IN ERROR: %d\n", size); + return -1; + } + + ftr = (size - 4) / 32; + + for (int tsn=1; tsn<32; tsn++) + { + struct e1_ts *ts = &line->ts[tsn]; + uint8_t buf_ts[32]; + + if (ts->mode == E1_TS_MODE_OFF) + continue; + + for (int i=0; imode) { + case E1_TS_MODE_RAW: + write(ts->fd, buf_ts, ftr); + break; + case E1_TS_MODE_HDLCFCS: + _e1_rx_hdlcfs(ts, buf_ts, ftr); + break; + default: + continue; + } + } + + return 0; +} + +static int +e1_usb_xfer_out(struct e1_usb_flow *flow, uint8_t *buf, int size) +{ + struct e1_line *line = flow->line; + struct e1_usb_line_data *ld = (struct e1_usb_line_data *) line->drv_data; + int fts, tsz; + + if (size <= 0) { + printf("OUT ERROR: %d\n", size); + return -1; + } + + /* Flow regulation */ + ld->r_acc += ld->r_sw; + + fts = ld->r_acc >> 10; + if (fts < 4) fts = 4; + else if (fts > 12) fts = 12; + + ld->r_acc -= fts << 10; + if (ld->r_acc & 0x80000000) + ld->r_acc = 0; + + /* Prepare */ + tsz = 4 + 32 * fts; + memset(buf, 0xff, tsz); + + /* Header */ + /* FIXME */ + + /* Scan timeslots */ + for (int tsn=1; tsn<32; tsn++) + { + struct e1_ts *ts = &line->ts[tsn]; + uint8_t buf_ts[32]; + int l; + + if (ts->mode == E1_TS_MODE_OFF) + continue; + + switch (ts->mode) { + case E1_TS_MODE_RAW: + l = read(ts->fd, buf_ts, fts); + break; + case E1_TS_MODE_HDLCFCS: + l = _e1_tx_hdlcfs(ts, buf_ts, fts); + break; + default: + continue; + } + + if (l <= 0) + continue; + + for (int i=0; iline->drv_data; + + if (size < 0) { + LOGP(DE1D, LOGL_ERROR, "Feedback transfer error\n"); + return 0; + } else if (size != 3) { + LOGP(DE1D, LOGL_ERROR, "Feedback packet invalid size (%d)\n", size); + return 0; + } + + ld->r_sw = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return 0; +} + + +// --------------------------------------------------------------------------- +// USB flow +// --------------------------------------------------------------------------- + +static void LIBUSB_CALL +_e1uf_xfr(struct libusb_transfer *xfr) +{ + struct e1_usb_flow *flow = (struct e1_usb_flow *) xfr->user_data; + struct e1_usb_intf_data *id = (struct e1_usb_intf_data *) flow->line->intf->drv_data; + int j, rv, len; + + len = 0; + + /* FIXME: Check transfer status ? Error handling ? */ + + if (flow->ep & 0x80) { + for (j=0; jppx; j++) { + flow->cb(flow, + libusb_get_iso_packet_buffer_simple(xfr, j), + (xfr->iso_packet_desc[j].status == LIBUSB_TRANSFER_COMPLETED) ? + xfr->iso_packet_desc[j].actual_length : -1 + ); + len += (xfr->iso_packet_desc[j].length = flow->size); + } + } else { + for (j=0; jppx; j++) + len += (xfr->iso_packet_desc[j].length = flow->cb(flow, &xfr->buffer[len], flow->size)); + } + + libusb_fill_iso_transfer(xfr, id->devh, flow->ep, + xfr->buffer, len, flow->ppx, + _e1uf_xfr, flow, 0 + ); + + rv = libusb_submit_transfer(xfr); + if (rv) + LOGP(DE1D, LOGL_ERROR, "Failed to resubmit buffer for transfer\n"); +} + +static struct e1_usb_flow * +e1uf_create(struct e1_line *line, xfer_cb_t cb, + int ep, int count, int size, int ppx) +{ + void *ctx = line->intf->e1d->ctx; + struct e1_usb_flow *flow; + + flow = talloc_zero(ctx, struct e1_usb_flow); + OSMO_ASSERT(flow); + + flow->line = line; + flow->cb = cb; + flow->ep = ep; + flow->count = count; + flow->size = size; + flow->ppx = ppx; + flow->entries = talloc_zero_size(ctx, count * sizeof(struct e1_usb_flow_entry)); + + for (int i=0; ientries[i].buf = talloc_zero_size(ctx, size * ppx); + + return flow; +} + +static void +e1uf_destroy(struct e1_usb_flow *flow) +{ + if (!flow) + return; + + /* FIXME: stop pending transfers */ + for (int i=0; icount; i++) + talloc_free(flow->entries[i].buf); + + talloc_free(flow->entries); + talloc_free(flow); +} + +static int +e1uf_start(struct e1_usb_flow *flow) +{ + struct e1_usb_intf_data *id = (struct e1_usb_intf_data *) flow->line->intf->drv_data; + struct libusb_transfer *xfr; + int i, j, rv, len; + + for (i=0; icount; i++) + { + xfr = libusb_alloc_transfer(flow->ppx); + if (!xfr) + return -ENOMEM; + + len = 0; + + if (flow->ep & 0x80) { + for (j=0; jppx; j++) + len += (xfr->iso_packet_desc[j].length = flow->size); + } else { + for (j=0; jppx; j++) + len += (xfr->iso_packet_desc[j].length = flow->cb(flow, &flow->entries[i].buf[len], flow->size)); + } + + libusb_fill_iso_transfer(xfr, id->devh, flow->ep, + flow->entries[i].buf, len, flow->ppx, + _e1uf_xfr, flow, 0 + ); + + rv = libusb_submit_transfer(xfr); + + if (rv) + return rv; + + flow->entries[i].xfr = xfr; + } + + return 0; +} + + +// --------------------------------------------------------------------------- +// e1d structures +// --------------------------------------------------------------------------- + +struct e1_intf * +_e1_intf_new(struct e1_daemon *e1d, void *drv_data) +{ + struct e1_intf *intf; + + intf = talloc_zero(e1d->ctx, struct e1_intf); + OSMO_ASSERT(intf); + + intf->e1d = e1d; + intf->drv_data = drv_data; + + INIT_LLIST_HEAD(&intf->list); + INIT_LLIST_HEAD(&intf->lines); + + if (!llist_empty(&e1d->interfaces)) { + struct e1_intf *f = llist_first_entry(&e1d->interfaces, struct e1_intf, list); + intf->id = f->id + 1; + } + + llist_add(&intf->list, &e1d->interfaces); + + return intf; +} + +struct e1_line * +_e1_line_new(struct e1_intf *intf, void *drv_data) +{ + struct e1_line *line; + + line = talloc_zero(intf->e1d->ctx, struct e1_line); + OSMO_ASSERT(line); + + line->intf = intf; + line->drv_data = drv_data; + + for (int i=0; i<32; i++) + line->ts[i].id = i; + + INIT_LLIST_HEAD(&line->list); + + if (!llist_empty(&intf->lines)) { + struct e1_line *l = llist_first_entry(&intf->lines, struct e1_line, list); + line->id = l->id + 1; + } + + llist_add(&line->list, &intf->lines); + + return line; +} + + + +// --------------------------------------------------------------------------- +// Init / Probing +// --------------------------------------------------------------------------- + +int +_e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev) +{ + struct e1_intf *intf; + struct e1_line *line; + struct e1_usb_intf_data *intf_data; + struct e1_usb_line_data *line_data; + struct libusb_config_descriptor *cd; + const struct libusb_interface_descriptor *id; + libusb_device_handle *devh; + int i, j, ret; + + ret = libusb_open(dev, &devh); + if (ret) { + LOGP(DE1D, LOGL_ERROR, "Failed to open usb device\n"); + return ret; + } + + intf_data = talloc_zero(e1d->ctx, struct e1_usb_intf_data); + intf_data->devh = devh; + + intf = _e1_intf_new(e1d, intf_data); + + ret = libusb_get_active_config_descriptor(dev, &cd); + if (ret) { + LOGP(DE1D, LOGL_ERROR, "Failed to talk to usb device\n"); + return ret; + } + + for (i=0; ibNumInterfaces; i++) { + /* Expect 2 altsettings with proper class/subclass/eps */ + if (cd->interface[i].num_altsetting != 2) + continue; + + id = &cd->interface[i].altsetting[1]; + if ((id->bInterfaceClass != 0xff) || (id->bInterfaceSubClass != 0xe1) || (id->bNumEndpoints != 3)) + continue; + + /* Get interface and set it up */ + ret = libusb_claim_interface(devh, id->bInterfaceNumber); + if (ret) { + LOGP(DE1D, LOGL_ERROR, "Failed to claim interface %d\n", id->bInterfaceNumber); + return ret; + } + + ret = libusb_set_interface_alt_setting(devh, id->bInterfaceNumber, 1); + if (ret) { + LOGP(DE1D, LOGL_ERROR, "Failed to set interface %d altsetting\n", id->bInterfaceNumber); + return ret; + } + + /* Setup driver data and find endpoints */ + line_data = talloc_zero(e1d->ctx, struct e1_usb_line_data); + + line_data->if_num = id->bInterfaceNumber; + line_data->r_acc = 0; + line_data->r_sw = 8192; + + for (j=0; jbNumEndpoints; j++) { + if (id->endpoint[j].bmAttributes == 0x11) { + line_data->ep_fb = id->endpoint[j].bEndpointAddress; + } else if (id->endpoint[j].bmAttributes == 0x05) { + if (id->endpoint[j].bEndpointAddress & 0x80) + line_data->ep_in = id->endpoint[j].bEndpointAddress; + else + line_data->ep_out = id->endpoint[j].bEndpointAddress; + + if (!line_data->pkt_size) + line_data->pkt_size = id->endpoint[j].wMaxPacketSize; + else if (line_data->pkt_size != id->endpoint[j].wMaxPacketSize) + LOGP(DE1D, LOGL_ERROR, "Inconsistent max packet size %d vs %d\n", + line_data->pkt_size, (int)id->endpoint[j].wMaxPacketSize); + } else { + LOGP(DE1D, LOGL_ERROR, "Invalid EP %02x\n", id->endpoint[j].bEndpointAddress); + } + } + + if (!line_data->ep_in || !line_data->ep_out || !line_data->ep_fb || !line_data->pkt_size) { + LOGP(DE1D, LOGL_ERROR, "Failed to use interface %d\n", id->bInterfaceNumber); + return -EINVAL; + } + + line = _e1_line_new(intf, line_data); + + line_data->flow_in = e1uf_create(line, e1_usb_xfer_in, line_data->ep_in, 2, line_data->pkt_size, 4); + line_data->flow_out = e1uf_create(line, e1_usb_xfer_out, line_data->ep_out, 2, line_data->pkt_size, 4); + line_data->flow_fb = e1uf_create(line, e1_usb_xfer_fb, line_data->ep_fb, 2, 8, 1); + + e1uf_start(line_data->flow_in); + e1uf_start(line_data->flow_out); + e1uf_start(line_data->flow_fb); + } + + return 0; +} + +int +e1_usb_probe(struct e1_daemon *e1d) +{ + struct libusb_device **dev_list; + ssize_t n_dev; + int i, ret; + + if (!g_usb) { + ret = libusb_init(&g_usb); + if (ret) { + LOGP(DE1D, LOGL_ERROR, "Failed to initialize libusb\n"); + return -EIO; + } + } + + n_dev = libusb_get_device_list(g_usb, &dev_list); + if (n_dev < 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to list devices\n"); + return -EIO; + } + + for (i=0; i