├── .gitignore ├── BUGS ├── COPYRIGHT.txt ├── INSTALL ├── LICENSE.txt ├── Makefile.in ├── README.txt ├── TODO.txt ├── app.c ├── app.h ├── at_command.c ├── at_command.h ├── at_parse.c ├── at_parse.h ├── at_queue.c ├── at_queue.h ├── at_read.c ├── at_read.h ├── at_response.c ├── at_response.h ├── chan_dongle.c ├── chan_dongle.h ├── channel.c ├── channel.h ├── char_conv.c ├── char_conv.h ├── cli.c ├── cli.h ├── config.h.in ├── configure.in ├── contrib └── openwrt │ ├── asterisk16-chan-dongle │ └── Makefile │ └── asterisk18-chan-dongle │ └── Makefile ├── cpvt.c ├── cpvt.h ├── dc_config.c ├── dc_config.h ├── etc ├── dongle.conf └── extensions.conf ├── export.h ├── helpers.c ├── helpers.h ├── manager.c ├── manager.h ├── memmem.c ├── memmem.h ├── mixbuffer.c ├── mixbuffer.h ├── mutils.h ├── pdiscovery.c ├── pdiscovery.h ├── pdu.c ├── pdu.h ├── ringbuffer.c ├── ringbuffer.h ├── single.c ├── stamp-h.in ├── test ├── parse.c └── test1.c └── tools ├── discovery.c ├── tty.c └── tty.h /.gitignore: -------------------------------------------------------------------------------- 1 | .INSTALL.swp 2 | .gitignore 3 | Makefile 4 | aclocal.m4 5 | autom4te.cache/ 6 | config.guess 7 | config.h 8 | config.log 9 | config.status 10 | config.sub 11 | configure 12 | install-sh 13 | missing 14 | stamp-h1 15 | 16 | *.o 17 | *.o.d 18 | *.so 19 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | Known bugs 2 | 3 | 1) SMS sending charset problem 4 | solved by PDU 5 | 2) HW: Terminate (from both side) active call break voice on all held 6 | 7 | 3) HW: Terminate held call locally break voice on all active 8 | 9 | 4) HW: Issue AT+CHLD=1x command before receiving ^CONF:call_idx after successfull ATDnum; 10 | kill device. Device not responding on commands but still sending ^BOOT: and ^RSSI: URCs 11 | 12 | 5) HW: device hangup after entering long PDU 13 | 14 | 6) white noise on x86_64 15 | 16 | 7) Some characters wrong encoded for SMS 17 | think iconv and localization trouble 18 | 19 | 8) command removed from queue by timeout when response already in read buffer 20 | 21 | 9) cpvt locking 22 | 23 | 10) HW device reboot and chan_dongle crash on receive long SMS in TEXT UCS2 mode (>150 symbols) 24 | 25 | 11) DTMF not send ? 26 | 27 | 12) zombi calls 28 | 29 | 13) HW: device still in sms prompt infinite (only 0x1B can help) if entered more 133 symbols in TEXT UCS-2 mode 30 | 31 | 14) Sometimes device stops responding to commands 32 | 33 | 15) SIGSEGV on hangup 34 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009 - 2010 2 | 3 | Artem Makhutov 4 | http://www.makhutov.org 5 | 6 | Dmitry Vagin 7 | 8 | bg 9 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | 1) For build and install from svn sources 2 | 1.1) Goto package source directory 3 | 4 | 1.2) Run aclocal 5 | 6 | 1.3) Run autoconf 7 | autoconf 8 | 1.4) Install missing files 9 | automake -a 10 | continue with step 2.3) 11 | 12 | 2) For build and install from package 13 | 2.1) Unpack sources 14 | tar xzvf package.tgz 15 | 2.2) Goto package source directory 16 | 2.3) Configure package 17 | simple 18 | ./configure 19 | explicite set install directory for module 20 | DESTDIR="/usr/lib/asterisk/modules" ./configure 21 | turn off some parts 22 | ./configure --disable-manager --disable-apps 23 | explicite set path to asterisk headers 24 | ./configure --with-asterisk=/usr/src/asterisk-1.6.2.13/include 25 | or 26 | CFLAGS="-I /usr/src/asterisk-1.6.2.13/include" ./configure 27 | enable debugging 28 | ./configure --enable-debug 29 | 2.4) Build package 30 | make 31 | 2.5) Install package 32 | make install 33 | 34 | More documentation on http://wiki.e1550.mobi/ 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | "This software program is licensed subject to the GNU General Public License 3 | (GPL). Version 2, June 1991, available at 4 | " 5 | 6 | GNU General Public License 7 | 8 | Version 2, June 1991 9 | 10 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 11 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 12 | 13 | Everyone is permitted to copy and distribute verbatim copies of this license 14 | document, but changing it is not allowed. 15 | 16 | Preamble 17 | 18 | The licenses for most software are designed to take away your freedom to 19 | share and change it. By contrast, the GNU General Public License is intended 20 | to guarantee your freedom to share and change free software--to make sure 21 | the software is free for all its users. This General Public License applies 22 | to most of the Free Software Foundation's software and to any other program 23 | whose authors commit to using it. (Some other Free Software Foundation 24 | software is covered by the GNU Library General Public License instead.) You 25 | can apply it to your programs, too. 26 | 27 | When we speak of free software, we are referring to freedom, not price. Our 28 | General Public Licenses are designed to make sure that you have the freedom 29 | to distribute copies of free software (and charge for this service if you 30 | wish), that you receive source code or can get it if you want it, that you 31 | can change the software or use pieces of it in new free programs; and that 32 | you know you can do these things. 33 | 34 | To protect your rights, we need to make restrictions that forbid anyone to 35 | deny you these rights or to ask you to surrender the rights. These 36 | restrictions translate to certain responsibilities for you if you distribute 37 | copies of the software, or if you modify it. 38 | 39 | For example, if you distribute copies of such a program, whether gratis or 40 | for a fee, you must give the recipients all the rights that you have. You 41 | must make sure that they, too, receive or can get the source code. And you 42 | must show them these terms so they know their rights. 43 | 44 | We protect your rights with two steps: (1) copyright the software, and (2) 45 | offer you this license which gives you legal permission to copy, distribute 46 | and/or modify the software. 47 | 48 | Also, for each author's protection and ours, we want to make certain that 49 | everyone understands that there is no warranty for this free software. If 50 | the software is modified by someone else and passed on, we want its 51 | recipients to know that what they have is not the original, so that any 52 | problems introduced by others will not reflect on the original authors' 53 | reputations. 54 | 55 | Finally, any free program is threatened constantly by software patents. We 56 | wish to avoid the danger that redistributors of a free program will 57 | individually obtain patent licenses, in effect making the program 58 | proprietary. To prevent this, we have made it clear that any patent must be 59 | licensed for everyone's free use or not licensed at all. 60 | 61 | The precise terms and conditions for copying, distribution and modification 62 | follow. 63 | 64 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 65 | 66 | 0. This License applies to any program or other work which contains a notice 67 | placed by the copyright holder saying it may be distributed under the 68 | terms of this General Public License. The "Program", below, refers to any 69 | such program or work, and a "work based on the Program" means either the 70 | Program or any derivative work under copyright law: that is to say, a 71 | work containing the Program or a portion of it, either verbatim or with 72 | modifications and/or translated into another language. (Hereinafter, 73 | translation is included without limitation in the term "modification".) 74 | Each licensee is addressed as "you". 75 | 76 | Activities other than copying, distribution and modification are not 77 | covered by this License; they are outside its scope. The act of running 78 | the Program is not restricted, and the output from the Program is covered 79 | only if its contents constitute a work based on the Program (independent 80 | of having been made by running the Program). Whether that is true depends 81 | on what the Program does. 82 | 83 | 1. You may copy and distribute verbatim copies of the Program's source code 84 | as you receive it, in any medium, provided that you conspicuously and 85 | appropriately publish on each copy an appropriate copyright notice and 86 | disclaimer of warranty; keep intact all the notices that refer to this 87 | License and to the absence of any warranty; and give any other recipients 88 | of the Program a copy of this License along with the Program. 89 | 90 | You may charge a fee for the physical act of transferring a copy, and you 91 | may at your option offer warranty protection in exchange for a fee. 92 | 93 | 2. You may modify your copy or copies of the Program or any portion of it, 94 | thus forming a work based on the Program, and copy and distribute such 95 | modifications or work under the terms of Section 1 above, provided that 96 | you also meet all of these conditions: 97 | 98 | * a) You must cause the modified files to carry prominent notices stating 99 | that you changed the files and the date of any change. 100 | 101 | * b) You must cause any work that you distribute or publish, that in 102 | whole or in part contains or is derived from the Program or any part 103 | thereof, to be licensed as a whole at no charge to all third parties 104 | under the terms of this License. 105 | 106 | * c) If the modified program normally reads commands interactively when 107 | run, you must cause it, when started running for such interactive 108 | use in the most ordinary way, to print or display an announcement 109 | including an appropriate copyright notice and a notice that there is 110 | no warranty (or else, saying that you provide a warranty) and that 111 | users may redistribute the program under these conditions, and 112 | telling the user how to view a copy of this License. (Exception: if 113 | the Program itself is interactive but does not normally print such 114 | an announcement, your work based on the Program is not required to 115 | print an announcement.) 116 | 117 | These requirements apply to the modified work as a whole. If identifiable 118 | sections of that work are not derived from the Program, and can be 119 | reasonably considered independent and separate works in themselves, then 120 | this License, and its terms, do not apply to those sections when you 121 | distribute them as separate works. But when you distribute the same 122 | sections as part of a whole which is a work based on the Program, the 123 | distribution of the whole must be on the terms of this License, whose 124 | permissions for other licensees extend to the entire whole, and thus to 125 | each and every part regardless of who wrote it. 126 | 127 | Thus, it is not the intent of this section to claim rights or contest 128 | your rights to work written entirely by you; rather, the intent is to 129 | exercise the right to control the distribution of derivative or 130 | collective works based on the Program. 131 | 132 | In addition, mere aggregation of another work not based on the Program 133 | with the Program (or with a work based on the Program) on a volume of a 134 | storage or distribution medium does not bring the other work under the 135 | scope of this License. 136 | 137 | 3. You may copy and distribute the Program (or a work based on it, under 138 | Section 2) in object code or executable form under the terms of Sections 139 | 1 and 2 above provided that you also do one of the following: 140 | 141 | * a) Accompany it with the complete corresponding machine-readable source 142 | code, which must be distributed under the terms of Sections 1 and 2 143 | above on a medium customarily used for software interchange; or, 144 | 145 | * b) Accompany it with a written offer, valid for at least three years, 146 | to give any third party, for a charge no more than your cost of 147 | physically performing source distribution, a complete machine- 148 | readable copy of the corresponding source code, to be distributed 149 | under the terms of Sections 1 and 2 above on a medium customarily 150 | used for software interchange; or, 151 | 152 | * c) Accompany it with the information you received as to the offer to 153 | distribute corresponding source code. (This alternative is allowed 154 | only for noncommercial distribution and only if you received the 155 | program in object code or executable form with such an offer, in 156 | accord with Subsection b above.) 157 | 158 | The source code for a work means the preferred form of the work for 159 | making modifications to it. For an executable work, complete source code 160 | means all the source code for all modules it contains, plus any 161 | associated interface definition files, plus the scripts used to control 162 | compilation and installation of the executable. However, as a special 163 | exception, the source code distributed need not include anything that is 164 | normally distributed (in either source or binary form) with the major 165 | components (compiler, kernel, and so on) of the operating system on which 166 | the executable runs, unless that component itself accompanies the 167 | executable. 168 | 169 | If distribution of executable or object code is made by offering access 170 | to copy from a designated place, then offering equivalent access to copy 171 | the source code from the same place counts as distribution of the source 172 | code, even though third parties are not compelled to copy the source 173 | along with the object code. 174 | 175 | 4. You may not copy, modify, sublicense, or distribute the Program except as 176 | expressly provided under this License. Any attempt otherwise to copy, 177 | modify, sublicense or distribute the Program is void, and will 178 | automatically terminate your rights under this License. However, parties 179 | who have received copies, or rights, from you under this License will not 180 | have their licenses terminated so long as such parties remain in full 181 | compliance. 182 | 183 | 5. You are not required to accept this License, since you have not signed 184 | it. However, nothing else grants you permission to modify or distribute 185 | the Program or its derivative works. These actions are prohibited by law 186 | if you do not accept this License. Therefore, by modifying or 187 | distributing the Program (or any work based on the Program), you 188 | indicate your acceptance of this License to do so, and all its terms and 189 | conditions for copying, distributing or modifying the Program or works 190 | based on it. 191 | 192 | 6. Each time you redistribute the Program (or any work based on the 193 | Program), the recipient automatically receives a license from the 194 | original licensor to copy, distribute or modify the Program subject to 195 | these terms and conditions. You may not impose any further restrictions 196 | on the recipients' exercise of the rights granted herein. You are not 197 | responsible for enforcing compliance by third parties to this License. 198 | 199 | 7. If, as a consequence of a court judgment or allegation of patent 200 | infringement or for any other reason (not limited to patent issues), 201 | conditions are imposed on you (whether by court order, agreement or 202 | otherwise) that contradict the conditions of this License, they do not 203 | excuse you from the conditions of this License. If you cannot distribute 204 | so as to satisfy simultaneously your obligations under this License and 205 | any other pertinent obligations, then as a consequence you may not 206 | distribute the Program at all. For example, if a patent license would 207 | not permit royalty-free redistribution of the Program by all those who 208 | receive copies directly or indirectly through you, then the only way you 209 | could satisfy both it and this License would be to refrain entirely from 210 | distribution of the Program. 211 | 212 | If any portion of this section is held invalid or unenforceable under any 213 | particular circumstance, the balance of the section is intended to apply 214 | and the section as a whole is intended to apply in other circumstances. 215 | 216 | It is not the purpose of this section to induce you to infringe any 217 | patents or other property right claims or to contest validity of any 218 | such claims; this section has the sole purpose of protecting the 219 | integrity of the free software distribution system, which is implemented 220 | by public license practices. Many people have made generous contributions 221 | to the wide range of software distributed through that system in 222 | reliance on consistent application of that system; it is up to the 223 | author/donor to decide if he or she is willing to distribute software 224 | through any other system and a licensee cannot impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to be 227 | a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in certain 230 | countries either by patents or by copyrighted interfaces, the original 231 | copyright holder who places the Program under this License may add an 232 | explicit geographical distribution limitation excluding those countries, 233 | so that distribution is permitted only in or among countries not thus 234 | excluded. In such case, this License incorporates the limitation as if 235 | written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions of 238 | the General Public License from time to time. Such new versions will be 239 | similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and 245 | conditions either of that version or of any later version published by 246 | the Free Software Foundation. If the Program does not specify a version 247 | number of this License, you may choose any version ever published by the 248 | Free Software Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free programs 251 | whose distribution conditions are different, write to the author to ask 252 | for permission. For software which is copyrighted by the Free Software 253 | Foundation, write to the Free Software Foundation; we sometimes make 254 | exceptions for this. Our decision will be guided by the two goals of 255 | preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 264 | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 265 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 266 | ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH 267 | YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL 268 | NECESSARY SERVICING, REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR 273 | DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL 274 | DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM 275 | (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED 276 | INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF 277 | THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR 278 | OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it free 286 | software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest to 289 | attach them to the start of each source file to most effectively convey the 290 | exclusion of warranty; and each file should have at least the "copyright" 291 | line and a pointer to where the full notice is found. 292 | 293 | one line to give the program's name and an idea of what it does. 294 | Copyright (C) yyyy name of author 295 | 296 | This program is free software; you can redistribute it and/or modify it 297 | under the terms of the GNU General Public License as published by the Free 298 | Software Foundation; either version 2 of the License, or (at your option) 299 | any later version. 300 | 301 | This program is distributed in the hope that it will be useful, but WITHOUT 302 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 303 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 304 | more details. 305 | 306 | You should have received a copy of the GNU General Public License along with 307 | this program; if not, write to the Free Software Foundation, Inc., 59 308 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this when 313 | it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 316 | with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free 317 | software, and you are welcome to redistribute it under certain conditions; 318 | type 'show c' for details. 319 | 320 | The hypothetical commands 'show w' and 'show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may be 322 | called something other than 'show w' and 'show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | 'Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | signature of Ty Coon, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Library General Public 339 | License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | PROJM = chan_dongle.so 2 | PROJS = chan_dongles.so 3 | 4 | chan_donglem_so_OBJS = app.o at_command.o at_parse.o at_queue.o at_read.o at_response.o \ 5 | chan_dongle.o channel.o char_conv.o cli.o helpers.o manager.o \ 6 | memmem.o ringbuffer.o cpvt.o dc_config.o pdu.o mixbuffer.o pdiscovery.o 7 | 8 | chan_dongles_so_OBJS = single.o 9 | 10 | test1_OBJS = test/test1.o ringbuffer.o mixbuffer.o 11 | parse_OBJS = test/parse.o at_parse.o char_conv.o pdu.o 12 | discovery_OBJS = tools/discovery.o tools/tty.o 13 | 14 | SOURCES = app.c at_command.c at_parse.c at_queue.c at_read.c at_response.c \ 15 | chan_dongle.c channel.c char_conv.c cli.c cpvt.c dc_config.c helpers.c \ 16 | manager.c memmem.c ringbuffer.c single.c pdu.c mixbuffer.c pdiscovery.c 17 | 18 | test_SOURCES = test/test1.c test/parse.c 19 | tools_SOURCES = tools/discovery.c tools/tty.c 20 | 21 | HEADERS = app.h at_command.h at_parse.h at_queue.h at_read.h at_response.h \ 22 | chan_dongle.h channel.h char_conv.h cli.h cpvt.h dc_config.h export.h \ 23 | helpers.h manager.h memmem.h ringbuffer.h pdu.h mixbuffer.h pdiscovery.h \ 24 | mutils.h 25 | 26 | tools_HEADERS = tools/tty.h 27 | 28 | EXTRA_DIST = BUGS COPYRIGHT.txt LICENSE.txt README.txt TODO.txt INSTALL \ 29 | Makefile.in config.h.in configure.in stamp-h.in etc contrib 30 | 31 | BUILD_TOOLS = configure config.sub install-sh missing config.guess 32 | 33 | CC = @CC@ 34 | LD = @CC@ 35 | STRIP = @STRIP@ 36 | RM = @RM@ -fr 37 | INSTALL = @INSTALL@ 38 | CHMOD = chmod 39 | 40 | DEFS = -DASTERISK_VERSION_NUM=130000 41 | CFLAGS = @CFLAGS@ -I$(srcdir) @CPPFLAGS@ $(DEFS) @DEFS@ @AC_CFLAGS@ 42 | LDFLAGS = @LDFLAGS@ 43 | SOLINK = @SOLINK@ 44 | LIBS = @LIBS@ 45 | DISTNAME= @PACKAGE_TARNAME@-@PACKAGE_VERSION@.r@PACKAGE_REVISION@ 46 | 47 | srcdir = @srcdir@ 48 | VPATH = @srcdir@ 49 | 50 | all: @TARGET@ 51 | 52 | install: all 53 | $(STRIP) $(PROJM) 54 | $(INSTALL) -m 755 $(PROJM) @DESTDIR@ 55 | 56 | $(PROJM): $(chan_donglem_so_OBJS) Makefile 57 | $(LD) $(LDFLAGS) $(SOLINK) -o $@ $(chan_donglem_so_OBJS) $(LIBS) 58 | 59 | $(PROJS): $(chan_dongles_so_OBJS) Makefile 60 | $(LD) $(LDFLAGS) $(SOLINK) -o $@ $(chan_dongles_so_OBJS) $(LIBS) 61 | $(CHMOD) 755 $@ 62 | mv $@ chan_dongle.so 63 | 64 | .c.o: 65 | $(CC) $(CFLAGS) $(MAKE_DEPS) -o $@ -c $< 66 | 67 | tests: test/test1 test/parse 68 | 69 | test/test1: $(test1_OBJS) 70 | $(LD) $(LDFLAGS) -o $@ $(test1_OBJS) $(LIBS) 71 | 72 | test/parse: $(parse_OBJS) 73 | $(LD) $(LDFLAGS) -o $@ $(parse_OBJS) $(LIBS) 74 | 75 | tools: tools/discovery 76 | 77 | tools/discovery: $(discovery_OBJS) 78 | $(LD) $(LDFLAGS) -o $@ $(discovery_OBJS) $(LIBS) 79 | 80 | clean: 81 | $(RM) $(PROJM) $(PROJS) *.o *.core .*.d autom4te.cache test/test1 test/*.o tools/discovery test/*.o 82 | 83 | dist: $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) 84 | @mkdir $(DISTNAME) $(DISTNAME)/test $(DISTNAME)/tools 85 | @cp -a $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) $(DISTNAME) 86 | @cp -a $(test_SOURCES) $(DISTNAME)/test 87 | @cp -a $(tools_SOURCES) $(tools_HEADERS) $(DISTNAME)/tools 88 | tar czf $(DISTNAME).tgz $(DISTNAME) --exclude .svn -h 89 | @$(RM) $(DISTNAME) 90 | 91 | ${srcdir}/configure: configure.in 92 | cd ${srcdir} && autoconf 93 | 94 | config.h: stamp-h 95 | stamp-h: config.h.in config.status 96 | ./config.status 97 | 98 | Makefile: Makefile.in config.status 99 | ./config.status 100 | 101 | config.status: configure 102 | ./config.status --recheck 103 | 104 | ifneq ($(wildcard .*.d),) 105 | include .*.d 106 | endif 107 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------- 2 | chan_dongle channel driver for Huawei UMTS cards 3 | -------------------------------------------------- 4 | 5 | WARNING: 6 | 7 | This channel driver is in alpha stage. 8 | I am not responsible if this channel driver will eat your money on 9 | your SIM card or do any unpredicted things. 10 | 11 | Please use a recent Linux kernel, 2.6.33+ recommended. 12 | If you use FreeBSD, 8.0+ recommended. 13 | 14 | This channel driver should work with the folowing UMTS cards: 15 | * Huawei K3715 16 | * Huawei E169 / K3520 17 | * Huawei E155X 18 | * Huawei E175X 19 | * Huawei K3765 20 | 21 | Check complete list in: 22 | http://code.google.com/p/asteris-chan-dongle/wiki/Supported_devices_eng 23 | 24 | Before using the channel driver make sure to: 25 | 26 | * Disable PIN code on your SIM card 27 | 28 | Supported features: 29 | * Place voice calls and terminate voice calls 30 | * Send SMS and receive SMS 31 | * Send and receive USSD commands / messages 32 | 33 | Some useful AT commands: 34 | AT+CCWA=0,0,1 #disable call-waiting 35 | AT+CFUN=1,1 #reset dongle 36 | AT^CARDLOCK="" #unlock code 37 | AT^SYSCFG=13,0,3FFFFFFF,0,3 #modem 2G only, automatic search any band, no roaming 38 | AT^U2DIAG=0 #enable modem function 39 | 40 | Here is an example for the dialplan: 41 | 42 | [dongle-incoming] 43 | exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${BASE64_DECODE(${SMS_BASE64})}) 44 | exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: ${BASE64_DECODE(${SMS_BASE64})}' >> /var/log/asterisk/sms.txt) 45 | exten => sms,n,Hangup() 46 | 47 | exten => ussd,1,Verbose(Incoming USSD: ${BASE64_DECODE(${USSD_BASE64})}) 48 | exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}: ${BASE64_DECODE(${USSD_BASE64})}' >> /var/log/asterisk/ussd.txt) 49 | exten => ussd,n,Hangup() 50 | 51 | exten => s,1,Dial(SIP/2001@othersipserver) 52 | exten => s,n,Hangup() 53 | 54 | [othersipserver-incoming] 55 | 56 | exten => _X.,1,Dial(Dongle/r1/${EXTEN}) 57 | exten => _X.,n,Hangup 58 | 59 | you can also use this: 60 | 61 | Call using a specific group: 62 | exten => _X.,1,Dial(Dongle/g1/${EXTEN}) 63 | 64 | Call using a specific group in round robin: 65 | exten => _X.,1,Dial(Dongle/r1/${EXTEN}) 66 | 67 | Call using a specific dongle: 68 | exten => _X.,1,Dial(Dongle/dongle0/${EXTEN}) 69 | 70 | Call using a specific provider name: 71 | exten => _X.,1,Dial(Dongle/p:PROVIDER NAME/${EXTEN}) 72 | 73 | Call using a specific IMEI: 74 | exten => _X.,1,Dial(Dongle/i:123456789012345/${EXTEN}) 75 | 76 | Call using a specific IMSI prefix: 77 | exten => _X.,1,Dial(Dongle/s:25099203948/${EXTEN}) 78 | 79 | How to store your own number: 80 | 81 | dongle cmd dongle0 AT+CPBS=\"ON\" 82 | dongle cmd dongle0 AT+CPBW=1,\"+123456789\",145 83 | 84 | 85 | Other CLI commands: 86 | 87 | dongle reset 88 | dongle restart gracefully 89 | dongle restart now 90 | dongle restart when convenient 91 | dongle show device 92 | dongle show devices 93 | dongle show version 94 | dongle sms number message 95 | dongle ussd ussd 96 | dongle stop gracefully 97 | dongle stop now 98 | dongle stop when convenient 99 | dongle start 100 | dongle restart gracefully 101 | dongle restart now 102 | dongle restart when convenient 103 | dongle remove gracefully 104 | dongle remove now 105 | dongle remove when convenient 106 | dongle reload gracefully 107 | dongle reload now 108 | dongle reload when convenient 109 | 110 | For reading installation notes please look to INSTALL file. 111 | 112 | For additional information about Huawei dongle usage 113 | look to chan_dongle Wiki at http://wiki.e1550.mobi 114 | and chan_dongle project home at http://code.google.com/p/asterisk-chan-dongle/ 115 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | TODO list 2 | 3 | added 20.11.2010 4 | 1) Command queue or serialization of write operations to device 5 | done at 27.11.2010 r45 6 | 2) Handling call waiting 7 | done at 27.11.2010 r45 8 | 9 | 3) Extended device name 'i:' 's:' for DEVICE_STATE() and DongleStatus() 10 | 11 | 4) automatic gain control for rxgain/txgain 12 | exists solution: 13 | download speex library http://www.speex.org 14 | build/install 15 | rebuild asterisk with speex and func_speex 16 | Set(AGC(rx)=8000) in dialplan 17 | 18 | 5) Make context setting optional with 'default' as default value 19 | 20.11.2010 already exists in base 175 rev. exported 20 | 21 | 6) Conference support 22 | added 16.12.2010 with Dial() 'conference' option 23 | 24 | 7) Remove warning 'Don't know how to indicate condition 20' 25 | done at 20.11.2010 26 | 27 | 8) LED control 28 | 29 | 9) SMS receive in Unicode 30 | send in PDU added at 27.11.2010 r45 31 | send in UCS-2 added at 05.12.2010 r72 32 | receive side done at 06.12.2010 r75 33 | 34 | 10) Automatic device discovery by IMEI or IMSI 35 | added 08.01.2011 in r150 36 | Added tool for discover of modems 37 | Intergation to main code planing 38 | 39 | 11) Complete fix of DTMF duplication 40 | added dtmf = off | inband | relax settings at 08.01.2011 in r145 41 | 42 | 12) Full support of SMS receive with active voice call 43 | feel satisfied at 16.12.2010 44 | 45 | added 24.11.2010 46 | 47 | 13) Device files locking 48 | done at 17.12.2010 in r106 49 | 50 | 14) SMS PDU mode 51 | duplicate for 9) 52 | added from internal source at 25.11.2010 53 | 54 | 15) Do a lot of testing with the channel driver 55 | 56 | 15.1) Added test for mixbuffer 57 | 15.2) Added uncomplete test for parser 58 | 59 | 16) Test sending SMS with a new line character in it 60 | done at 05.12.2010 61 | 62 | 17) Find a way how to proper detect remote side alerting (GSM 02.40) 63 | 64 | 18) Add PIN code detection 65 | 66 | 19) Cleanup code 67 | 68 | 20) Make a better Makefile 69 | switch to autoconf at 05.12.2010 70 | 71 | 21) Write a better documentation 72 | done paritally with samples 73 | at 04.01.2010 http://wiki.e1550.mobi/ published 74 | 75 | 22) Add more API commands 76 | 77 | 22.1) 'disable' option in device section 78 | done 27.11.2010 at r46 with [defaults] 79 | 80 | 22.2) [global] with template settings 81 | done 27.11.2010 at r46 with [defaults]. Also available asterisk template feature for config files 82 | 83 | 22.3) exten 84 | added 06.12.2010 r83 85 | 86 | 23) 87 | 88 | 24) reconfigure on fly 89 | added 07.01.2011 in r140 90 | 91 | 25) implement command "dongle show version" 92 | done at 06.12.2010 in r77 93 | 94 | 26) implement command "dongle restart device" 95 | done at 06.12.2010 in r77 96 | 97 | added 06.12.2010 98 | 99 | 27) Remove Call waiting status duplicated messages 100 | now only for registration event check CCWA status 101 | 102 | 28) outgoing SMS: set SRR, validity, SC address, on screen mode 103 | SRR and validity done 06.12.2010 in r75 104 | 105 | 29) incoming SMS: pass up raw PDU, raw message, SCA, SCTS, PID, UDH, fields of DCS 106 | 107 | 30) incoming SMS reports 108 | 109 | 31) outgoing SMS: send in 7Bit or 8Bit if possible 110 | done at 08.12.2010 in r81 111 | 112 | 31) Handling of response errors, CLIR for example 113 | 114 | 32) Control SMS receiving from dialplan 115 | (when message received channel Local created rules read message, delete or some other) 116 | 117 | added 17.12.2010 118 | 119 | 33) Start conference from any channel 120 | 121 | 34) SMS deletion function 122 | 123 | 35) receive on-screen SMS 124 | 125 | added 08.01.2011 126 | 127 | 36) dtmf detection control from dialplan 128 | 129 | 37) volume settings 130 | 131 | 38) response timeout handling 132 | 133 | 39) use ast_exists_extension for sms and ussd 134 | -------------------------------------------------------------------------------- /app.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include 3 | #endif /* HAVE_CONFIG_H */ 4 | 5 | #ifdef BUILD_APPLICATIONS 6 | /* 7 | Copyright (C) 2009 - 2010 8 | 9 | Artem Makhutov 10 | http://www.makhutov.org 11 | 12 | Dmitry Vagin 13 | 14 | bg 15 | */ 16 | 17 | #include 18 | #include /* AST_DECLARE_APP_ARGS() ... */ 19 | #include /* pbx_builtin_setvar_helper() */ 20 | #include /* ast_register_application2() ast_unregister_application() */ 21 | #include /* ASTERISK_VERSION_NUM */ 22 | 23 | #include "app.h" /* app_register() app_unregister() */ 24 | #include "chan_dongle.h" /* struct pvt */ 25 | #include "helpers.h" /* send_sms() ITEMS_OF() */ 26 | 27 | struct ast_channel; 28 | 29 | static int app_status_exec (struct ast_channel* channel, const char* data) 30 | { 31 | struct pvt * pvt; 32 | char * parse; 33 | int stat; 34 | char status[2]; 35 | int exists = 0; 36 | 37 | AST_DECLARE_APP_ARGS (args, 38 | AST_APP_ARG (resource); 39 | AST_APP_ARG (variable); 40 | ); 41 | 42 | if (ast_strlen_zero (data)) 43 | { 44 | return -1; 45 | } 46 | 47 | parse = ast_strdupa (data); 48 | 49 | AST_STANDARD_APP_ARGS (args, parse); 50 | 51 | if (ast_strlen_zero (args.resource) || ast_strlen_zero (args.variable)) 52 | { 53 | return -1; 54 | } 55 | 56 | /* TODO: including options number */ 57 | pvt = find_device_by_resource(args.resource, 0, NULL, &exists); 58 | if(pvt) 59 | { 60 | /* ready for outgoing call */ 61 | ast_mutex_unlock (&pvt->lock); 62 | stat = 2; 63 | } 64 | else 65 | { 66 | stat = exists ? 3 : 1; 67 | } 68 | 69 | snprintf (status, sizeof (status), "%d", stat); 70 | pbx_builtin_setvar_helper (channel, args.variable, status); 71 | 72 | return 0; 73 | } 74 | 75 | static int app_send_sms_exec (attribute_unused struct ast_channel* channel, const char* data) 76 | { 77 | char* parse; 78 | const char* msg; 79 | int status; 80 | void * msgid; 81 | 82 | AST_DECLARE_APP_ARGS (args, 83 | AST_APP_ARG (device); 84 | AST_APP_ARG (number); 85 | AST_APP_ARG (message); 86 | AST_APP_ARG (validity); 87 | AST_APP_ARG (report); 88 | ); 89 | 90 | if (ast_strlen_zero (data)) 91 | { 92 | return -1; 93 | } 94 | 95 | parse = ast_strdupa (data); 96 | 97 | AST_STANDARD_APP_ARGS (args, parse); 98 | 99 | if (ast_strlen_zero (args.device)) 100 | { 101 | ast_log (LOG_ERROR, "NULL device for message -- SMS will not be sent\n"); 102 | return -1; 103 | } 104 | 105 | if (ast_strlen_zero (args.number)) 106 | { 107 | ast_log (LOG_ERROR, "NULL destination for message -- SMS will not be sent\n"); 108 | return -1; 109 | } 110 | 111 | msg = send_sms(args.device, args.number, args.message, args.validity, args.report, &status, &msgid); 112 | if(!status) 113 | ast_log (LOG_ERROR, "[%s] %s with id %p\n", args.device, msg, msgid); 114 | return !status; 115 | } 116 | 117 | 118 | 119 | static const struct dongle_application 120 | { 121 | const char* name; 122 | 123 | int (*func)(struct ast_channel* channel, const char* data); 124 | const char* synopsis; 125 | const char* desc; 126 | } dca[] = 127 | { 128 | { 129 | "DongleStatus", 130 | app_status_exec, 131 | "DongleStatus(Resource,Variable)", 132 | "DongleStatus(Resource,Variable)\n" 133 | " Resource - Resource string as for Dial()\n" 134 | " Variable - Variable to store status in will be 1-3.\n" 135 | " In order, Disconnected, Connected & Free, Connected & Busy.\n" 136 | }, 137 | { 138 | "DongleSendSMS", 139 | app_send_sms_exec, 140 | "DongleSendSMS(Device,Dest,Message,Validity,Report)", 141 | "DongleSendSMS(Device,Dest,Message,Validity,Report)\n" 142 | " Device - Id of device from dongle.conf\n" 143 | " Dest - destination\n" 144 | " Message - text of the message\n" 145 | " Validity - Validity period in minutes\n" 146 | " Report - Boolean flag for report request\n" 147 | } 148 | }; 149 | 150 | #if ASTERISK_VERSION_NUM >= 10800 151 | typedef int (*app_func_t)(struct ast_channel* channel, const char * data); 152 | #else 153 | typedef int (*app_func_t)(struct ast_channel* channel, void * data); 154 | #endif 155 | 156 | #/* */ 157 | EXPORT_DEF void app_register() 158 | { 159 | unsigned i; 160 | for(i = 0; i < ITEMS_OF(dca); i++) 161 | { 162 | ast_register_application2 (dca[i].name, (app_func_t)(dca[i].func), dca[i].synopsis, dca[i].desc, self_module()); 163 | } 164 | } 165 | 166 | #/* */ 167 | EXPORT_DEF void app_unregister() 168 | { 169 | int i; 170 | for(i = ITEMS_OF(dca)-1; i >= 0; i--) 171 | { 172 | ast_unregister_application(dca[i].name); 173 | } 174 | } 175 | 176 | #endif /* BUILD_APPLICATIONS */ 177 | -------------------------------------------------------------------------------- /app.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_APP_H_INCLUDED 5 | #define CHAN_DONGLE_APP_H_INCLUDED 6 | 7 | #ifdef BUILD_APPLICATIONS 8 | 9 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 10 | 11 | EXPORT_DECL void app_register(); 12 | EXPORT_DECL void app_unregister(); 13 | 14 | #else /* BUILD_APPLICATIONS */ 15 | 16 | #define app_register() 17 | #define app_unregister() 18 | 19 | #endif /* BUILD_APPLICATIONS */ 20 | #endif /* CHAN_DONGLE_APP_H_INCLUDED */ 21 | -------------------------------------------------------------------------------- /at_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_AT_SEND_H_INCLUDED 5 | #define CHAN_DONGLE_AT_SEND_H_INCLUDED 6 | 7 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 8 | #include "dc_config.h" /* call_waiting_t */ 9 | #include "mutils.h" /* enum2str_def() ITEMS_OF() */ 10 | 11 | #define CCWA_CLASS_VOICE 1 12 | 13 | /* magic order !!! keep order of this values like in at_cmd2str() 14 | */ 15 | typedef enum { 16 | CMD_USER = 0, 17 | 18 | CMD_AT, 19 | CMD_AT_A, 20 | CMD_AT_CCWA_STATUS, 21 | CMD_AT_CCWA_SET, 22 | CMD_AT_CFUN, 23 | 24 | CMD_AT_CGMI, 25 | CMD_AT_CGMM, 26 | CMD_AT_CGMR, 27 | CMD_AT_CGSN, 28 | 29 | CMD_AT_CHUP, 30 | CMD_AT_CIMI, 31 | // CMD_AT_CLIP, 32 | CMD_AT_CLIR, 33 | 34 | CMD_AT_CLVL, 35 | CMD_AT_CMGD, 36 | CMD_AT_CMGF, 37 | CMD_AT_CMGR, 38 | 39 | CMD_AT_CMGS, 40 | CMD_AT_SMSTEXT, 41 | CMD_AT_CNMI, 42 | CMD_AT_CNUM, 43 | 44 | CMD_AT_COPS, 45 | CMD_AT_COPS_INIT, 46 | CMD_AT_CPIN, 47 | CMD_AT_CPMS, 48 | 49 | CMD_AT_CREG, 50 | CMD_AT_CREG_INIT, 51 | CMD_AT_CSCS, 52 | CMD_AT_CSQ, 53 | 54 | CMD_AT_CSSN, 55 | CMD_AT_CUSD, 56 | CMD_AT_CVOICE, 57 | CMD_AT_D, 58 | 59 | CMD_AT_DDSETEX, 60 | CMD_AT_DTMF, 61 | CMD_AT_E, 62 | 63 | CMD_AT_U2DIAG, 64 | CMD_AT_Z, 65 | CMD_AT_CMEE, 66 | CMD_AT_CSCA, 67 | 68 | CMD_AT_CHLD_1x, 69 | CMD_AT_CHLD_2x, 70 | CMD_AT_CHLD_2, 71 | CMD_AT_CHLD_3, 72 | CMD_AT_CLCC 73 | } at_cmd_t; 74 | 75 | /*! 76 | * \brief Get the string representation of the given AT command 77 | * \param cmd -- the command to process 78 | * \return a string describing the given command 79 | */ 80 | 81 | INLINE_DECL const char* at_cmd2str (at_cmd_t cmd) 82 | { 83 | /* magic!!! must be in same order as elements of enums in at_cmd_t */ 84 | static const char * const cmds[] = { 85 | "USER'S", 86 | 87 | "AT", 88 | "ATA", 89 | "AT+CCWA?", 90 | "AT+CCWA=", 91 | "AT+CFUN", 92 | 93 | "AT+CGMI", 94 | "AT+CGMM", 95 | "AT+CGMR", 96 | "AT+CGSN", 97 | 98 | "AT+CHUP", 99 | "AT+CIMI", 100 | // "AT+CLIP", 101 | "AT+CLIR", 102 | 103 | "AT+CLVL", 104 | "AT+CMGD", 105 | "AT+CMGF", 106 | "AT+CMGR", 107 | 108 | "AT+CMGS", 109 | "SMSTEXT", 110 | "AT+CNMI", 111 | "AT+CNUM", 112 | 113 | "AT+COPS?", 114 | "AT+COPS=", 115 | "AT+CPIN?", 116 | "AT+CPMS", 117 | 118 | "AT+CREG?", 119 | "AT+CREG=", 120 | "AT+CSCS", 121 | "AT+CSQ", 122 | 123 | "AT+CSSN", 124 | "AT+CUSD", 125 | "AT^CVOICE", 126 | "ATD", 127 | 128 | "AT^DDSETEX", 129 | "AT^DTMF", 130 | "ATE", 131 | 132 | "AT^U2DIAG", 133 | "ATZ", 134 | "AT+CMEE", 135 | "AT+CSCA", 136 | 137 | "AT+CHLD=1x", 138 | "AT+CHLD=2x", 139 | "AT+CHLD=2", 140 | "AT+CHLD=3", 141 | "AT+CLCC" 142 | }; 143 | return enum2str_def(cmd, cmds, ITEMS_OF(cmds), "UNDEFINED"); 144 | } 145 | 146 | 147 | struct cpvt; 148 | 149 | EXPORT_DECL const char* at_cmd2str (at_cmd_t cmd); 150 | EXPORT_DECL int at_enque_initialization(struct cpvt * cpvt, at_cmd_t from_command); 151 | EXPORT_DECL int at_enque_ping (struct cpvt * cpvt); 152 | EXPORT_DECL int at_enque_cops (struct cpvt * cpvt); 153 | EXPORT_DECL int at_enque_sms (struct cpvt * cpvt, const char * number, const char * msg, unsigned validity_min, int report_req, void ** id); 154 | EXPORT_DECL int at_enque_pdu (struct cpvt * cpvt, const char * pdu, attribute_unused const char *, attribute_unused unsigned, attribute_unused int, void ** id); 155 | EXPORT_DECL int at_enque_ussd (struct cpvt * cpvt, const char * code, attribute_unused const char *, attribute_unused unsigned, attribute_unused int, void ** id); 156 | EXPORT_DECL int at_enque_dtmf (struct cpvt * cpvt, char digit); 157 | EXPORT_DECL int at_enque_set_ccwa (struct cpvt * cpvt, attribute_unused const char * unused1, attribute_unused const char * unused2, unsigned call_waiting); 158 | EXPORT_DECL int at_enque_reset (struct cpvt * cpvt); 159 | EXPORT_DECL int at_enque_dial(struct cpvt * cpvt, const char * number, int clir); 160 | EXPORT_DECL int at_enque_answer(struct cpvt * cpvt); 161 | EXPORT_DECL int at_enque_user_cmd(struct cpvt * cpvt, const char * input); 162 | EXPORT_DECL int at_enque_retrive_sms(struct cpvt * cpvt, int index, int delete); 163 | EXPORT_DECL int at_enque_hangup (struct cpvt * cpvt, int call_idx); 164 | EXPORT_DECL int at_enque_volsync (struct cpvt * cpvt); 165 | EXPORT_DECL int at_enque_clcc (struct cpvt * cpvt); 166 | EXPORT_DECL int at_enque_activate (struct cpvt * cpvt); 167 | EXPORT_DECL int at_enque_flip_hold (struct cpvt * cpvt); 168 | EXPORT_DECL int at_enque_conference (struct cpvt * cpvt); 169 | EXPORT_DECL void at_hangup_immediality(struct cpvt * cpvt); 170 | 171 | #endif /* CHAN_DONGLE_AT_SEND_H_INCLUDED */ 172 | -------------------------------------------------------------------------------- /at_parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | 9 | bg 10 | */ 11 | #ifdef HAVE_CONFIG_H 12 | #include 13 | #endif /* HAVE_CONFIG_H */ 14 | 15 | #include "memmem.h" 16 | 17 | #include /* NULL */ 18 | #include /* errno */ 19 | #include /* strtol */ 20 | 21 | #include "at_parse.h" 22 | #include "mutils.h" /* ITEMS_OF() */ 23 | #include "chan_dongle.h" 24 | #include "pdu.h" /* pdu_parse() */ 25 | 26 | #/* */ 27 | static unsigned mark_line(char * line, const char * delimiters, char * pointers[]) 28 | { 29 | unsigned found = 0; 30 | 31 | for(; line[0] && delimiters[found]; line++) 32 | { 33 | if(line[0] == delimiters[found]) 34 | { 35 | pointers[found] = line; 36 | found++; 37 | } 38 | } 39 | return found; 40 | } 41 | 42 | /*! 43 | * \brief Parse a CNUM response 44 | * \param str -- string to parse (null terminated) 45 | * @note str will be modified when the CNUM message is parsed 46 | * \return NULL on error (parse error) or a pointer to the subscriber number 47 | */ 48 | 49 | EXPORT_DEF char * at_parse_cnum (char* str) 50 | { 51 | /* 52 | * parse CNUM response in the following format: 53 | * +CNUM: ,, 54 | * example 55 | * +CNUM: "Subscriber Number","+79139131234",145 56 | * +CNUM: "Subscriber Number","",145 57 | * +CNUM: "Subscriber Number",,145 58 | */ 59 | 60 | char delimiters[] = ":,,"; 61 | char * marks[STRLEN(delimiters)]; 62 | 63 | /* parse URC only here */ 64 | if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) 65 | { 66 | marks[1]++; 67 | if(marks[1][0] == '"') 68 | marks[1]++; 69 | if(marks[2][-1] == '"') 70 | marks[2]--; 71 | marks[2][0] = 0; 72 | return marks[1]; 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | /*! 79 | * \brief Parse a COPS response 80 | * \param str -- string to parse (null terminated) 81 | * \param len -- string lenght 82 | * @note str will be modified when the COPS message is parsed 83 | * \return NULL on error (parse error) or a pointer to the provider name 84 | */ 85 | 86 | EXPORT_DEF char* at_parse_cops (char* str) 87 | { 88 | /* 89 | * parse COPS response in the following format: 90 | * +COPS: [,,,] 91 | * 92 | * example 93 | * +COPS: 0,0,"TELE2",0 94 | */ 95 | 96 | char delimiters[] = ":,,,"; 97 | char * marks[STRLEN(delimiters)]; 98 | 99 | /* parse URC only here */ 100 | if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) 101 | { 102 | marks[2]++; 103 | if(marks[2][0] == '"') 104 | marks[2]++; 105 | if(marks[3][-1] == '"') 106 | marks[3]--; 107 | marks[3][0] = 0; 108 | return marks[2]; 109 | } 110 | 111 | return NULL; 112 | } 113 | 114 | /*! 115 | * \brief Parse a CREG response 116 | * \param str -- string to parse (null terminated) 117 | * \param len -- string lenght 118 | * \param gsm_reg -- a pointer to a int 119 | * \param gsm_reg_status -- a pointer to a int 120 | * \param lac -- a pointer to a char pointer which will store the location area code in hex format 121 | * \param ci -- a pointer to a char pointer which will store the cell id in hex format 122 | * @note str will be modified when the CREG message is parsed 123 | * \retval 0 success 124 | * \retval -1 parse error 125 | */ 126 | 127 | EXPORT_DEF int at_parse_creg (char* str, unsigned len, int* gsm_reg, int* gsm_reg_status, char** lac, char** ci) 128 | { 129 | unsigned i; 130 | int state; 131 | char* p1 = NULL; 132 | char* p2 = NULL; 133 | char* p3 = NULL; 134 | char* p4 = NULL; 135 | 136 | *gsm_reg = 0; 137 | *gsm_reg_status = -1; 138 | *lac = NULL; 139 | *ci = NULL; 140 | 141 | /* 142 | * parse CREG response in the following format: 143 | * +CREG: [,][,,] 144 | */ 145 | 146 | for (i = 0, state = 0; i < len && state < 8; i++) 147 | { 148 | switch (state) 149 | { 150 | case 0: 151 | if (str[i] == ':') 152 | { 153 | state++; 154 | } 155 | break; 156 | 157 | case 1: 158 | if (str[i] != ' ') 159 | { 160 | p1 = &str[i]; 161 | state++; 162 | } 163 | /* fall through */ 164 | 165 | case 2: 166 | if (str[i] == ',') 167 | { 168 | str[i] = '\0'; 169 | state++; 170 | } 171 | break; 172 | 173 | case 3: 174 | if (str[i] != ' ') 175 | { 176 | p2 = &str[i]; 177 | state++; 178 | } 179 | /* fall through */ 180 | case 4: 181 | if (str[i] == ',') 182 | { 183 | str[i] = '\0'; 184 | state++; 185 | } 186 | break; 187 | 188 | case 5: 189 | if (str[i] != ' ') 190 | { 191 | p3 = &str[i]; 192 | state++; 193 | } 194 | /* fall through */ 195 | 196 | case 6: 197 | if (str[i] == ',') 198 | { 199 | str[i] = '\0'; 200 | state++; 201 | } 202 | break; 203 | 204 | case 7: 205 | if (str[i] != ' ') 206 | { 207 | p4 = &str[i]; 208 | state++; 209 | } 210 | break; 211 | } 212 | } 213 | 214 | if (state < 2) 215 | { 216 | return -1; 217 | } 218 | 219 | if ((p2 && !p3 && !p4) || (p2 && p3 && p4)) 220 | { 221 | p1 = p2; 222 | } 223 | 224 | if (p1) 225 | { 226 | errno = 0; 227 | *gsm_reg_status = (int) strtol (p1, (char**) NULL, 10); 228 | if (*gsm_reg_status == 0 && errno == EINVAL) 229 | { 230 | *gsm_reg_status = -1; 231 | return -1; 232 | } 233 | 234 | if (*gsm_reg_status == 1 || *gsm_reg_status == 5) 235 | { 236 | *gsm_reg = 1; 237 | } 238 | } 239 | 240 | if (p2 && p3 && !p4) 241 | { 242 | *lac = p2; 243 | *ci = p3; 244 | } 245 | else if (p3 && p4) 246 | { 247 | *lac = p3; 248 | *ci = p4; 249 | } 250 | 251 | return 0; 252 | } 253 | 254 | /*! 255 | * \brief Parse a CMTI notification 256 | * \param str -- string to parse (null terminated) 257 | * \param len -- string lenght 258 | * @note str will be modified when the CMTI message is parsed 259 | * \return -1 on error (parse error) or the index of the new sms message 260 | */ 261 | 262 | EXPORT_DEF int at_parse_cmti (const char* str) 263 | { 264 | int index; 265 | 266 | /* 267 | * parse cmti info in the following format: 268 | * +CMTI: , 269 | */ 270 | 271 | return sscanf (str, "+CMTI: %*[^,],%u", &index) == 1 ? index : -1; 272 | } 273 | 274 | 275 | static const char * parse_cmgr_text(char ** str, size_t len, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc) 276 | { 277 | /* 278 | * parse cmgr info in the following TEXT format: 279 | * +CMGR: "","+123456789",,timestamp 280 | * 281 | * OK 282 | * or 283 | * +CMGR: "","002B....",,timestamp 284 | * 285 | * OK 286 | */ 287 | 288 | char delimiters[] = ",,,\n"; 289 | char * marks[STRLEN(delimiters)]; 290 | size_t length; 291 | 292 | unsigned count = mark_line(*str, delimiters, marks); 293 | if(count == ITEMS_OF(marks)) 294 | { 295 | /* unquote number */ 296 | marks[0]++; 297 | if(marks[0][0] == '"') 298 | marks[0]++; 299 | if(marks[1][-1] == '"') 300 | marks[1]--; 301 | length = marks[1] - marks[0] + 1; 302 | if(oa_len < length) 303 | return "Not enought space for store number"; 304 | *oa_enc = get_encoding(RECODE_DECODE, marks[0], length - 1); 305 | marks[1][0] = 0; 306 | memcpy(oa, marks[0], length); 307 | 308 | *msg = marks[3] + 1; 309 | length = len - (*msg - *str); 310 | *msg_enc = get_encoding(RECODE_DECODE, *msg, length); 311 | return NULL; 312 | } 313 | else if(count > 0) 314 | *str = marks[count - 1]; 315 | 316 | return "Can't parse +CMGR response text"; 317 | } 318 | 319 | static const char* parse_cmgr_pdu(char** str, attribute_unused size_t len, char* oa, size_t oa_len, str_encoding_t* oa_enc, char** msg, str_encoding_t* msg_enc) 320 | { 321 | /* 322 | * parse cmgr info in the following PDU format 323 | * +CMGR: message_status,[address_text],TPDU_length 324 | * SMSC_number_and_TPDU 325 | * OK 326 | * 327 | * sample 328 | * +CMGR: 1,,31 329 | * 07911234567890F3040B911234556780F20008012150220040210C041F04400438043204350442 330 | * OK 331 | */ 332 | 333 | char delimiters[] = ",,\n"; 334 | char * marks[STRLEN(delimiters)]; 335 | char * end; 336 | size_t tpdu_length; 337 | 338 | if(mark_line(*str, delimiters, marks) == ITEMS_OF(marks)) 339 | { 340 | tpdu_length = strtol(marks[1] + 1, &end, 10); 341 | if(tpdu_length <= 0 || end[0] != '\r') 342 | return "Invalid TPDU length in CMGR PDU status line"; 343 | *str = marks[2] + 1; 344 | return pdu_parse(str, tpdu_length, oa, oa_len, oa_enc, msg, msg_enc); 345 | } 346 | 347 | return "Can't parse +CMGR response"; 348 | } 349 | 350 | /*! 351 | * \brief Parse a CMGR message 352 | * \param str -- pointer to pointer of string to parse (null terminated) 353 | * \param len -- string lenght 354 | * \param number -- a pointer to a char pointer which will store the from number 355 | * \param text -- a pointer to a char pointer which will store the message text 356 | * @note str will be modified when the CMGR message is parsed 357 | * \retval 0 success 358 | * \retval -1 parse error 359 | */ 360 | 361 | EXPORT_DEF const char * at_parse_cmgr(char ** str, size_t len, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc) 362 | { 363 | const char* rv = "Can't parse +CMGR response line"; 364 | 365 | /* skip "+CMGR:" */ 366 | *str += 6; 367 | len -= 6; 368 | 369 | /* skip leading spaces */ 370 | while(len > 0 && str[0][0] == ' ') 371 | { 372 | (*str)++; 373 | len--; 374 | } 375 | 376 | if(len > 0) 377 | { 378 | /* check PDU or TEXT mode */ 379 | const char* (*fptr)(char** str, size_t len, char* num, size_t num_len, str_encoding_t * oa_enc, char** msg, str_encoding_t * msg_enc); 380 | fptr = str[0][0] == '"' ? parse_cmgr_text : parse_cmgr_pdu; 381 | 382 | rv = (*fptr)(str, len, oa, oa_len, oa_enc, msg, msg_enc); 383 | } 384 | 385 | return rv; 386 | } 387 | 388 | /*! 389 | * \brief Parse a CUSD answer 390 | * \param str -- string to parse (null terminated) 391 | * \param len -- string lenght 392 | * @note str will be modified when the CUSD string is parsed 393 | * \retval 0 success 394 | * \retval -1 parse error 395 | */ 396 | 397 | EXPORT_DEF int at_parse_cusd (char* str, int * type, char** cusd, int * dcs) 398 | { 399 | /* 400 | * parse cusd message in the following format: 401 | * +CUSD: ,[,] 402 | * 403 | * examples 404 | * +CUSD: 5 405 | * +CUSD: 0,"100,00 EURO, valid till 01.01.2010, you are using tariff "Mega Tariff". More informations *111#.",15 406 | */ 407 | 408 | char delimiters[] = ":,,"; 409 | char * marks[STRLEN(delimiters)]; 410 | unsigned count; 411 | 412 | *type = -1; 413 | *cusd = ""; 414 | *dcs = -1; 415 | 416 | count = mark_line(str, delimiters, marks); 417 | // 0, 1, 2, 3 418 | if(count > 0) 419 | { 420 | if(sscanf(marks[0] + 1, "%u", type) == 1) 421 | { 422 | if(count > 1) 423 | { 424 | marks[1]++; 425 | if(marks[1][0] == '"') 426 | marks[1]++; 427 | *cusd = marks[1]; 428 | 429 | if(count > 2) { 430 | sscanf(marks[2] + 1, "%u", dcs); 431 | if(marks[2][-1] == '"') 432 | marks[2]--; 433 | marks[2][0] = 0; 434 | } else { 435 | int len = strlen(*cusd); 436 | if(len > 0 && (*cusd)[len - 1] == '"') 437 | (*cusd)[len-1] = 0; 438 | } 439 | } 440 | return 0; 441 | } 442 | } 443 | return -1; 444 | } 445 | 446 | /*! 447 | * \brief Parse a CPIN notification 448 | * \param str -- string to parse (null terminated) 449 | * \param len -- string lenght 450 | * \return 2 if PUK required 451 | * \return 1 if PIN required 452 | * \return 0 if no PIN required 453 | * \return -1 on error (parse error) or card lock 454 | */ 455 | 456 | EXPORT_DEF int at_parse_cpin (char* str, size_t len) 457 | { 458 | static const struct { 459 | const char * value; 460 | unsigned length; 461 | } resp[] = { 462 | { "READY", 5 }, 463 | { "SIM PIN", 7 }, 464 | { "SIM PUK", 7 }, 465 | }; 466 | 467 | unsigned idx; 468 | for(idx = 0; idx < ITEMS_OF(resp); idx++) 469 | { 470 | if(memmem (str, len, resp[idx].value, resp[idx].length) != NULL) 471 | return idx; 472 | } 473 | return -1; 474 | } 475 | 476 | /*! 477 | * \brief Parse +CSQ response 478 | * \param str -- string to parse (null terminated) 479 | * \param len -- string lenght 480 | * \retval 0 success 481 | * \retval -1 error 482 | */ 483 | 484 | EXPORT_DEF int at_parse_csq (const char* str, int* rssi) 485 | { 486 | /* 487 | * parse +CSQ response in the following format: 488 | * +CSQ: , 489 | */ 490 | 491 | return sscanf (str, "+CSQ:%2d,", rssi) == 1 ? 0 : -1; 492 | } 493 | 494 | /*! 495 | * \brief Parse a ^RSSI notification 496 | * \param str -- string to parse (null terminated) 497 | * \param len -- string lenght 498 | * \return -1 on error (parse error) or the rssi value 499 | */ 500 | 501 | EXPORT_DEF int at_parse_rssi (const char* str) 502 | { 503 | int rssi = -1; 504 | 505 | /* 506 | * parse RSSI info in the following format: 507 | * ^RSSI: 508 | */ 509 | 510 | sscanf (str, "^RSSI:%d", &rssi); 511 | return rssi; 512 | } 513 | 514 | /*! 515 | * \brief Parse a ^MODE notification (link mode) 516 | * \param str -- string to parse (null terminated) 517 | * \param len -- string lenght 518 | * \return -1 on error (parse error) or the the link mode value 519 | */ 520 | 521 | EXPORT_DEF int at_parse_mode (char * str, int * mode, int * submode) 522 | { 523 | /* 524 | * parse RSSI info in the following format: 525 | * ^MODE:, 526 | */ 527 | 528 | return sscanf (str, "^MODE:%d,%d", mode, submode) == 2 ? 0 : -1; 529 | } 530 | 531 | #/* */ 532 | EXPORT_DEF int at_parse_csca(char* str, char ** csca) 533 | { 534 | /* 535 | * parse CSCA info in the following format: 536 | * +CSCA: , 537 | * +CSCA: "+79139131234",145 538 | * +CSCA: "",145 539 | */ 540 | char delimiters[] = "\"\""; 541 | char * marks[STRLEN(delimiters)]; 542 | 543 | if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) 544 | { 545 | *csca = marks[0] + 1; 546 | marks[1][0] = 0; 547 | return 0; 548 | } 549 | 550 | return -1; 551 | } 552 | 553 | #/* */ 554 | EXPORT_DEF int at_parse_clcc(char* str, unsigned * call_idx, unsigned * dir, unsigned * state, unsigned * mode, unsigned * mpty, char ** number, unsigned * toa) 555 | { 556 | /* 557 | * +CLCC:,,,,[,,[,[,]]]\r\n 558 | * ... 559 | * +CLCC:,,,,[,,[,[,]]]\r\n 560 | * examples 561 | * +CLCC: 1,1,4,0,0,"",145 562 | * +CLCC: 1,1,4,0,0,"+79139131234",145 563 | * +CLCC: 1,1,4,0,0,"0079139131234",145 564 | * +CLCC: 1,1,4,0,0,"+7913913ABCA",145 565 | */ 566 | char delimiters[] = ":,,,,,,"; 567 | char * marks[STRLEN(delimiters)]; 568 | 569 | *call_idx = 0; 570 | *dir = 0; 571 | *state = 0; 572 | *mode = 0; 573 | *mpty = 0; 574 | *number = ""; 575 | *toa = 0; 576 | 577 | if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) 578 | { 579 | if( sscanf(marks[0] + 1, "%u", call_idx) == 1 580 | && 581 | sscanf(marks[1] + 1, "%u", dir) == 1 582 | && 583 | sscanf(marks[2] + 1, "%u", state) == 1 584 | && 585 | sscanf(marks[3] + 1, "%u", mode) == 1 586 | && 587 | sscanf(marks[4] + 1, "%u", mpty) == 1 588 | && 589 | sscanf(marks[6] + 1, "%u", toa) == 1) 590 | { 591 | marks[5]++; 592 | if(marks[5][0] == '"') 593 | marks[5]++; 594 | if(marks[6][-1] == '"') 595 | marks[6]--; 596 | *number = marks[5]; 597 | marks[6][0] = 0; 598 | 599 | return 0; 600 | } 601 | } 602 | 603 | return -1; 604 | } 605 | 606 | #/* */ 607 | EXPORT_DEF int at_parse_ccwa(char* str, unsigned * class) 608 | { 609 | /* 610 | * CCWA may be in form: 611 | * in response of AT+CCWA=? 612 | * +CCWA: (0,1) 613 | * in response of AT+CCWA=? 614 | * +CCWA: 615 | * in response of "AT+CCWA=[[,[,]]]" 616 | * +CCWA: , 617 | * 618 | * unsolicited result code 619 | * +CCWA: ,,,[][,[,,[,]]] 620 | */ 621 | char delimiters[] = ":,,"; 622 | char * marks[STRLEN(delimiters)]; 623 | 624 | /* parse URC only here */ 625 | if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) 626 | { 627 | if(sscanf(marks[2] + 1, "%u", class) == 1) 628 | return 0; 629 | } 630 | 631 | return -1; 632 | } 633 | -------------------------------------------------------------------------------- /at_parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_AT_PARSE_H_INCLUDED 5 | #define CHAN_DONGLE_AT_PARSE_H_INCLUDED 6 | 7 | #include /* size_t */ 8 | 9 | #include "export.h" /* EXPORT_DECL EXPORT_DECL */ 10 | #include "char_conv.h" /* str_encoding_t */ 11 | struct pvt; 12 | 13 | EXPORT_DECL char* at_parse_cnum (char* str); 14 | EXPORT_DECL char* at_parse_cops (char* str); 15 | EXPORT_DECL int at_parse_creg (char* str, unsigned len, int* gsm_reg, int* gsm_reg_status, char** lac, char** ci); 16 | EXPORT_DECL int at_parse_cmti (const char* str); 17 | EXPORT_DECL const char* at_parse_cmgr (char** str, size_t len, char* oa, size_t oa_len, str_encoding_t* oa_enc, char** msg, str_encoding_t* msg_enc); 18 | EXPORT_DECL int at_parse_cusd (char* str, int * type, char ** cusd, int * dcs); 19 | EXPORT_DECL int at_parse_cpin (char* str, size_t len); 20 | EXPORT_DECL int at_parse_csq (const char* str, int* rssi); 21 | EXPORT_DECL int at_parse_rssi (const char* str); 22 | EXPORT_DECL int at_parse_mode (char* str, int * mode, int * submode); 23 | EXPORT_DECL int at_parse_csca (char* str, char ** csca); 24 | EXPORT_DECL int at_parse_clcc (char* str, unsigned * call_idx, unsigned * dir, unsigned * state, unsigned * mode, unsigned * mpty, char ** number, unsigned * toa); 25 | EXPORT_DECL int at_parse_ccwa(char* str, unsigned * class); 26 | 27 | 28 | 29 | #endif /* CHAN_DONGLE_AT_PARSE_H_INCLUDED */ 30 | -------------------------------------------------------------------------------- /at_queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | 9 | Copyright (C) 2010 - 2011 10 | bg 11 | */ 12 | #ifdef HAVE_CONFIG_H 13 | #include 14 | #endif /* HAVE_CONFIG_H */ 15 | 16 | #include 17 | #include /* ast_free() */ 18 | 19 | #include "at_queue.h" 20 | #include "chan_dongle.h" /* struct pvt */ 21 | 22 | /*! 23 | * \brief Free an item data 24 | * \param cmd - struct at_queue_cmd 25 | */ 26 | #/* */ 27 | static void at_queue_free_data(at_queue_cmd_t * cmd) 28 | { 29 | if(cmd->data) 30 | { 31 | if((cmd->flags & ATQ_CMD_FLAG_STATIC) == 0) 32 | { 33 | ast_free (cmd->data); 34 | cmd->data = NULL; 35 | } 36 | /* right because work with copy of static data */ 37 | } 38 | cmd->length = 0; 39 | } 40 | 41 | /*! 42 | * \brief Free an item 43 | * \param e -- struct at_queue_task structure 44 | */ 45 | #/* */ 46 | static void at_queue_free (at_queue_task_t * task) 47 | { 48 | unsigned no; 49 | for(no = 0; no < task->cmdsno; no++) 50 | { 51 | at_queue_free_data(&task->cmds[no]); 52 | } 53 | ast_free (task); 54 | } 55 | 56 | 57 | /*! 58 | * \brief Remove an job item from the front of the queue, and free it 59 | * \param pvt -- pvt structure 60 | */ 61 | #/* */ 62 | static void at_queue_remove (struct pvt * pvt) 63 | { 64 | at_queue_task_t * task = AST_LIST_REMOVE_HEAD (&pvt->at_queue, entry); 65 | 66 | if (task) 67 | { 68 | PVT_STATE(pvt, at_tasks)--; 69 | PVT_STATE(pvt, at_cmds) -= task->cmdsno - task->cindex; 70 | ast_debug (4, "[%s] remove task with %u command(s) begin with '%s' expected response '%s' from queue\n", 71 | PVT_ID(pvt), task->cmdsno, at_cmd2str (task->cmds[0].cmd), 72 | at_res2str (task->cmds[0].res)); 73 | 74 | at_queue_free(task); 75 | } 76 | } 77 | 78 | #/* */ 79 | static at_queue_cmd_t* at_queue_head_cmd_nc (const struct pvt * pvt) 80 | { 81 | at_queue_task_t * e = AST_LIST_FIRST (&pvt->at_queue); 82 | if(e) 83 | return &e->cmds[e->cindex]; 84 | return NULL; 85 | } 86 | 87 | 88 | /*! 89 | * \brief Add an list of commands (task) to the back of the queue 90 | * \param cpvt -- cpvt structure 91 | * \param cmds -- the commands that was sent to generate the response 92 | * \param cmdsno -- number of commands 93 | * \param prio -- priority 0 mean put at tail 94 | * \return task on success, NULL on error 95 | */ 96 | #/* */ 97 | static at_queue_task_t * at_queue_add (struct cpvt * cpvt, const at_queue_cmd_t * cmds, unsigned cmdsno, int prio) 98 | { 99 | at_queue_task_t * e = NULL; 100 | if(cmdsno > 0) 101 | { 102 | e = ast_malloc (sizeof(*e) + cmdsno * sizeof(*cmds)); 103 | if(e) 104 | { 105 | pvt_t * pvt = cpvt->pvt; 106 | at_queue_task_t * first; 107 | 108 | e->entry.next = 0; 109 | e->cmdsno = cmdsno; 110 | e->cindex = 0; 111 | e->cpvt = cpvt; 112 | 113 | memcpy(&e->cmds[0], cmds, cmdsno * sizeof(*cmds)); 114 | 115 | 116 | if(prio && (first = AST_LIST_FIRST (&pvt->at_queue))) 117 | AST_LIST_INSERT_AFTER (&pvt->at_queue, first, e, entry); 118 | else 119 | AST_LIST_INSERT_TAIL (&pvt->at_queue, e, entry); 120 | 121 | PVT_STATE(pvt, at_tasks) ++; 122 | PVT_STATE(pvt, at_cmds) += cmdsno; 123 | 124 | PVT_STAT(pvt, at_tasks) ++; 125 | PVT_STAT(pvt, at_cmds) += cmdsno; 126 | 127 | ast_debug (4, "[%s] insert task with %u commands begin with '%s' expected response '%s' %s of queue\n", 128 | PVT_ID(pvt), e->cmdsno, at_cmd2str (e->cmds[0].cmd), 129 | at_res2str (e->cmds[0].res), prio ? "after head" : "at tail"); 130 | } 131 | } 132 | return e; 133 | } 134 | 135 | 136 | /*! 137 | * \brief Write to fd 138 | * \param fd -- file descriptor 139 | * \param buf -- buffer to write 140 | * \param count -- number of bytes to write 141 | * 142 | * This function will write count characters from buf. It will always write 143 | * count chars unless it encounters an error. 144 | * 145 | * \retval number of bytes wrote 146 | */ 147 | 148 | #/* */ 149 | EXPORT_DEF size_t write_all (int fd, const char* buf, size_t count) 150 | { 151 | ssize_t out_count; 152 | size_t total = 0; 153 | unsigned errs = 10; 154 | 155 | while (count > 0) 156 | { 157 | out_count = write (fd, buf, count); 158 | if (out_count <= 0) 159 | { 160 | if(errno == EINTR || errno == EAGAIN) 161 | { 162 | errs--; 163 | if(errs != 0) 164 | continue; 165 | } 166 | break; 167 | } 168 | errs = 10; 169 | count -= out_count; 170 | buf += out_count; 171 | total += out_count; 172 | } 173 | return total; 174 | } 175 | 176 | /*! 177 | * \brief Write to fd 178 | * \param pvt -- pvt structure 179 | * \param buf -- buffer to write 180 | * \param count -- number of bytes to write 181 | * 182 | * This function will write count characters from buf. It will always write 183 | * count chars unless it encounters an error. 184 | * 185 | * \retval !0 on error 186 | * \retval 0 success 187 | */ 188 | 189 | #/* */ 190 | EXPORT_DEF int at_write (struct pvt* pvt, const char* buf, size_t count) 191 | { 192 | size_t wrote; 193 | 194 | ast_debug (5, "[%s] [%.*s]\n", PVT_ID(pvt), (int) count, buf); 195 | 196 | wrote = write_all(pvt->data_fd, buf, count); 197 | PVT_STAT(pvt, d_write_bytes) += wrote; 198 | if(wrote != count) 199 | { 200 | ast_debug (1, "[%s] write() error: %d\n", PVT_ID(pvt), errno); 201 | } 202 | 203 | return wrote != count; 204 | } 205 | 206 | /*! 207 | * \brief Remove an cmd item from the front of the queue 208 | * \param pvt -- pvt structure 209 | */ 210 | #/* */ 211 | EXPORT_DEF void at_queue_remove_cmd (struct pvt* pvt, at_res_t res) 212 | { 213 | at_queue_task_t * task = AST_LIST_FIRST (&pvt->at_queue); 214 | 215 | if (task) 216 | { 217 | unsigned index = task->cindex; 218 | 219 | task->cindex++; 220 | PVT_STATE(pvt, at_cmds)--; 221 | ast_debug (4, "[%s] remove command '%s' expected response '%s' real '%s' cmd %u/%u flags 0x%02x from queue\n", 222 | PVT_ID(pvt), at_cmd2str (task->cmds[index].cmd), 223 | at_res2str (task->cmds[index].res), at_res2str (res), 224 | task->cindex, task->cmdsno, task->cmds[index].flags); 225 | 226 | if((task->cindex >= task->cmdsno) || (task->cmds[index].res != res && (task->cmds[index].flags & ATQ_CMD_FLAG_IGNORE) == 0)) 227 | { 228 | at_queue_remove(pvt); 229 | } 230 | } 231 | } 232 | 233 | /*! 234 | * \brief Try real write first command on queue 235 | * \param pvt -- pvt structure 236 | * \return 0 on success, non-0 on error 237 | */ 238 | #/* */ 239 | EXPORT_DEF int at_queue_run (struct pvt * pvt) 240 | { 241 | int fail = 0; 242 | at_queue_cmd_t * cmd = at_queue_head_cmd_nc(pvt); 243 | 244 | if(cmd) 245 | { 246 | if(cmd->length > 0) 247 | { 248 | ast_debug (4, "[%s] write command '%s' expected response '%s' length %u\n", 249 | PVT_ID(pvt), at_cmd2str (cmd->cmd), at_res2str (cmd->res), cmd->length); 250 | 251 | fail = at_write(pvt, cmd->data, cmd->length); 252 | if(fail) 253 | { 254 | ast_log (LOG_ERROR, "[%s] Error write command '%s' expected response '%s' length %u, cancel\n", PVT_ID(pvt), at_cmd2str (cmd->cmd), at_res2str (cmd->res), cmd->length); 255 | at_queue_remove_cmd(pvt, cmd->res + 1); 256 | } 257 | else 258 | { 259 | /* set expire time */ 260 | cmd->timeout = ast_tvadd (ast_tvnow(), cmd->timeout); 261 | 262 | /* free data and mark as written */ 263 | at_queue_free_data(cmd); 264 | } 265 | } 266 | #if 0 267 | else 268 | { 269 | /* check expiration */ 270 | if(ast_tvcmp (ast_tvnow(), cmd->timeout) > 0) 271 | { 272 | ast_log (LOG_ERROR, "[%s] Error command '%s' expected response '%s' expired, cancel\n", PVT_ID(pvt), at_cmd2str (cmd->cmd), at_res2str (cmd->res)); 273 | at_queue_remove_cmd(pvt, cmd->res + 1); 274 | fail = -1; 275 | } 276 | } 277 | #endif /* 0 */ 278 | } 279 | /* else empty nothing todo */ 280 | return fail; 281 | } 282 | 283 | /*! 284 | * \brief Write commands with queue 285 | * \param pvt -- pvt structure 286 | * \return 0 on success non-0 on error 287 | */ 288 | #/* */ 289 | EXPORT_DEF int at_queue_insert_const (struct cpvt * cpvt, const at_queue_cmd_t * cmds, unsigned cmdsno, int athead) 290 | { 291 | return at_queue_add(cpvt, cmds, cmdsno, athead) == NULL || at_queue_run(cpvt->pvt); 292 | } 293 | 294 | #/* */ 295 | EXPORT_DEF int at_queue_insert_task (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, at_queue_task_t ** task) 296 | { 297 | unsigned idx; 298 | task[0] = at_queue_add(cpvt, cmds, cmdsno, athead); 299 | 300 | if(!task[0]) 301 | { 302 | for(idx = 0; idx < cmdsno; idx++) 303 | { 304 | at_queue_free_data(&cmds[idx]); 305 | } 306 | } 307 | 308 | if(at_queue_run(cpvt->pvt)) 309 | task[0] = NULL; 310 | 311 | return task[0] == NULL; 312 | } 313 | 314 | #/* */ 315 | EXPORT_DEF int at_queue_insert(struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead) 316 | { 317 | at_queue_task_t * task; 318 | 319 | return at_queue_insert_task(cpvt, cmds, cmdsno, athead, &task); 320 | } 321 | 322 | 323 | 324 | #/* */ 325 | EXPORT_DEF void at_queue_handle_result (struct pvt* pvt, at_res_t res) 326 | { 327 | /* move queue */ 328 | at_queue_remove_cmd(pvt, res); 329 | } 330 | 331 | /*! 332 | * \brief Remove all itmes from the queue and free them 333 | * \param pvt -- pvt structure 334 | */ 335 | 336 | #/* */ 337 | EXPORT_DEF void at_queue_flush (struct pvt* pvt) 338 | { 339 | struct at_queue_task* task; 340 | 341 | while ((task = AST_LIST_FIRST (&pvt->at_queue))) 342 | { 343 | at_queue_remove(pvt); 344 | } 345 | } 346 | 347 | /*! 348 | * \brief Get the first task on queue 349 | * \param pvt -- pvt structure 350 | * \return a pointer to the first command of the given queue 351 | */ 352 | #/* */ 353 | EXPORT_DEF const struct at_queue_task* at_queue_head_task (const struct pvt * pvt) 354 | { 355 | return AST_LIST_FIRST (&pvt->at_queue); 356 | } 357 | 358 | 359 | /*! 360 | * \brief Get the first command of a queue 361 | * \param pvt -- pvt structure 362 | * \return a pointer to the first command of the given queue 363 | */ 364 | #/* */ 365 | EXPORT_DEF const at_queue_cmd_t * at_queue_head_cmd(const struct pvt * pvt) 366 | { 367 | return at_queue_task_cmd(at_queue_head_task(pvt)); 368 | } 369 | 370 | #/* */ 371 | EXPORT_DEF int at_queue_timeout(const struct pvt * pvt) 372 | { 373 | int ms_timeout = -1; 374 | const at_queue_cmd_t * cmd = at_queue_head_cmd(pvt); 375 | 376 | if(cmd) 377 | { 378 | if(cmd->length == 0) 379 | { 380 | ms_timeout = ast_tvdiff_ms(cmd->timeout, ast_tvnow()); 381 | } 382 | } 383 | 384 | return ms_timeout; 385 | } 386 | -------------------------------------------------------------------------------- /at_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED 5 | #define CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED 6 | 7 | #include /* struct timeval */ 8 | 9 | #include 10 | #include /* AST_LIST_ENTRY */ 11 | 12 | #include "at_command.h" /* at_cmd_t */ 13 | #include "at_response.h" /* at_res_t */ 14 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 15 | 16 | 17 | typedef struct at_queue_cmd 18 | { 19 | at_cmd_t cmd; /*!< command code */ 20 | at_res_t res; /*!< expected responce code, can be RES_OK, RES_CMGR, RES_SMS_PROMPT */ 21 | 22 | unsigned flags; /*!< flags */ 23 | #define ATQ_CMD_FLAG_DEFAULT 0x00 /*!< empty flags */ 24 | #define ATQ_CMD_FLAG_STATIC 0x01 /*!< data is static no try deallocate */ 25 | #define ATQ_CMD_FLAG_IGNORE 0x02 /*!< ignore response non match condition */ 26 | 27 | struct timeval timeout; /*!< timeout value, started at time when command actually written on device */ 28 | #define ATQ_CMD_TIMEOUT_1S 1 /*!< timeout value 1 sec */ 29 | #define ATQ_CMD_TIMEOUT_2S 2 /*!< timeout value 2 sec */ 30 | #define ATQ_CMD_TIMEOUT_5S 5 /*!< timeout value 5 sec */ 31 | #define ATQ_CMD_TIMEOUT_10S 10 /*!< timeout value 10 sec */ 32 | #define ATQ_CMD_TIMEOUT_15S 15 /*!< timeout value 15 ses */ 33 | #define ATQ_CMD_TIMEOUT_40S 40 /*!< timeout value 40 ses */ 34 | 35 | char* data; /*!< command and data to send in device */ 36 | unsigned length; /*!< data length */ 37 | } at_queue_cmd_t; 38 | 39 | /* initializers */ 40 | #define ATQ_CMD_INIT_STF(e,icmd,iflags,idata) do { \ 41 | (e).cmd = (icmd); \ 42 | (e).res = RES_OK; \ 43 | (e).flags = iflags | ATQ_CMD_FLAG_STATIC; \ 44 | (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_2S; \ 45 | (e).timeout.tv_usec = 0; \ 46 | (e).data = (char*)(idata); \ 47 | (e).length = STRLEN(idata); \ 48 | } while(0) 49 | #define ATQ_CMD_INIT_ST(e,icmd,idata) ATQ_CMD_INIT_STF(e, icmd, ATQ_CMD_FLAG_DEFAULT, idata) 50 | 51 | #define ATQ_CMD_INIT_DYNF(e,icmd,iflags) do { \ 52 | (e).cmd = (icmd); \ 53 | (e).res = RES_OK; \ 54 | (e).flags = iflags & ~ATQ_CMD_FLAG_STATIC; \ 55 | (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_2S; \ 56 | (e).timeout.tv_usec = 0; \ 57 | } while(0) 58 | #define ATQ_CMD_INIT_DYN(e,icmd) ATQ_CMD_INIT_DYNF(e, icmd, ATQ_CMD_FLAG_DEFAULT) 59 | #define ATQ_CMD_INIT_DYNI(e,icmd) ATQ_CMD_INIT_DYNF(e, icmd, ATQ_CMD_FLAG_IGNORE) 60 | 61 | /* static initializers */ 62 | #define ATQ_CMD_DECLARE_STFT(cmd,res,data,flags,s,u) { (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {(s), (u)}, (char*)(data), STRLEN(data) } 63 | #define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) ATQ_CMD_DECLARE_STFT(cmd,res,data,flags,ATQ_CMD_TIMEOUT_2S,0) 64 | //#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) { (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {ATQ_CMD_TIMEOUT_2S, 0}, (char*)(data), STRLEN(data) } 65 | #define ATQ_CMD_DECLARE_ST(cmd,data) ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_DEFAULT) 66 | #define ATQ_CMD_DECLARE_STI(cmd,data) ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE) 67 | #define ATQ_CMD_DECLARE_STIT(cmd,data,s,u) ATQ_CMD_DECLARE_STFT(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE,s,u) 68 | 69 | #define ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,s,u) { (cmd), (res), flags & ~ATQ_CMD_FLAG_STATIC, {(s), (u)}, 0, 0 } 70 | #define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,ATQ_CMD_TIMEOUT_2S,0) 71 | //#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) { (cmd), (res), flags & ~ATQ_CMD_FLAG_STATIC, {ATQ_CMD_TIMEOUT_2S, 0}, 0, 0 } 72 | #define ATQ_CMD_DECLARE_DYN(cmd) ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_DEFAULT) 73 | #define ATQ_CMD_DECLARE_DYNI(cmd) ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE) 74 | #define ATQ_CMD_DECLARE_DYNIT(cmd,s,u) ATQ_CMD_DECLARE_DYNFT(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE,s,u) 75 | #define ATQ_CMD_DECLARE_DYN2(cmd,res) ATQ_CMD_DECLARE_DYNF(cmd, res, ATQ_CMD_FLAG_DEFAULT) 76 | 77 | typedef struct at_queue_task 78 | { 79 | AST_LIST_ENTRY (at_queue_task) entry; 80 | 81 | unsigned cmdsno; 82 | unsigned cindex; 83 | struct cpvt* cpvt; 84 | 85 | at_queue_cmd_t cmds[0]; 86 | } at_queue_task_t; 87 | 88 | 89 | EXPORT_DECL int at_queue_insert_const (struct cpvt * cpvt, const at_queue_cmd_t * cmds, unsigned cmdsno, int athead); 90 | EXPORT_DECL int at_queue_insert (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead); 91 | EXPORT_DECL int at_queue_insert_task (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, at_queue_task_t ** task); 92 | EXPORT_DECL void at_queue_handle_result (struct pvt * pvt, at_res_t res); 93 | EXPORT_DECL void at_queue_flush (struct pvt * pvt); 94 | EXPORT_DECL const at_queue_task_t * at_queue_head_task (const struct pvt * pvt); 95 | EXPORT_DECL const at_queue_cmd_t * at_queue_head_cmd(const struct pvt * pvt); 96 | EXPORT_DECL int at_queue_timeout(const struct pvt * pvt); 97 | EXPORT_DECL int at_queue_run (struct pvt * pvt); 98 | 99 | static inline const at_queue_cmd_t * at_queue_task_cmd (const at_queue_task_t * task) 100 | { 101 | return task ? &task->cmds[task->cindex] : NULL; 102 | } 103 | 104 | /* direct device write, dangerouse */ 105 | /* TODO: move */ 106 | EXPORT_DECL int at_write (struct pvt * pvt, const char * buf, size_t count); 107 | EXPORT_DECL size_t write_all (int fd, const char * buf, size_t count); 108 | #endif /* CHAN_DONGLE_AT_CMD_QUEUE_H_INCLUDED */ 109 | -------------------------------------------------------------------------------- /at_read.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | 9 | Copyright (C) 2010 - 2011 10 | bg 11 | */ 12 | #ifdef HAVE_CONFIG_H 13 | #include 14 | #endif /* HAVE_CONFIG_H */ 15 | 16 | #ifndef _GNU_SOURCE 17 | #define _GNU_SOURCE /* vasprintf() in asterisk/utils.h */ 18 | #endif /* #ifndef _GNU_SOURCE */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include /* ast_waitfor_n_fd() */ 25 | #include /* ast_debug() */ 26 | 27 | #include "chan_dongle.h" 28 | #include "at_read.h" 29 | #include "ringbuffer.h" 30 | 31 | 32 | /*! 33 | * \brief Wait for activity on an socket 34 | * \param fd -- file descriptor 35 | * \param ms -- pointer to an int containing a timeout in ms 36 | * \return 0 on timeout and the socket fd (non-zero) otherwise 37 | * \retval 0 timeout 38 | */ 39 | 40 | EXPORT_DEF int at_wait (int fd, int* ms) 41 | { 42 | int exception, outfd; 43 | 44 | outfd = ast_waitfor_n_fd (&fd, 1, ms, &exception); 45 | 46 | if (outfd < 0) 47 | { 48 | outfd = 0; 49 | } 50 | 51 | return outfd; 52 | } 53 | 54 | #/* return number of bytes readed */ 55 | EXPORT_DEF ssize_t at_read (int fd, const char * dev, struct ringbuffer* rb) 56 | { 57 | struct iovec iov[2]; 58 | int iovcnt; 59 | ssize_t n = -1; 60 | 61 | /* TODO: read until major error */ 62 | iovcnt = rb_write_iov (rb, iov); 63 | 64 | if (iovcnt > 0) 65 | { 66 | n = readv (fd, iov, iovcnt); 67 | 68 | if (n < 0) 69 | { 70 | if (errno != EINTR && errno != EAGAIN) 71 | { 72 | ast_debug (1, "[%s] readv() error: %d\n", dev, errno); 73 | return n; 74 | } 75 | 76 | return 0; 77 | } 78 | else if (n > 0) 79 | { 80 | rb_write_upd (rb, n); 81 | 82 | ast_debug (5, "[%s] receive %zu byte, used %zu, free %zu, read %zu, write %zu\n", 83 | dev, n, rb_used (rb), rb_free (rb), rb->read, rb->write); 84 | 85 | iovcnt = rb_read_all_iov (rb, iov); 86 | 87 | if (iovcnt > 0) 88 | { 89 | if (iovcnt == 2) 90 | { 91 | ast_debug (5, "[%s] [%.*s%.*s]\n", dev, 92 | (int) iov[0].iov_len, (char*) iov[0].iov_base, 93 | (int) iov[1].iov_len, (char*) iov[1].iov_base); 94 | } 95 | else 96 | { 97 | ast_debug (5, "[%s] [%.*s]\n", dev, 98 | (int) iov[0].iov_len, (char*) iov[0].iov_base); 99 | } 100 | } 101 | } 102 | } 103 | else 104 | ast_log (LOG_ERROR, "[%s] at cmd receive buffer overflow\n", dev); 105 | return n; 106 | } 107 | 108 | EXPORT_DEF int at_read_result_iov (const char * dev, int * read_result, struct ringbuffer* rb, struct iovec iov[2]) 109 | { 110 | int iovcnt = 0; 111 | int res; 112 | size_t s; 113 | 114 | s = rb_used (rb); 115 | if (s > 0) 116 | { 117 | /* ast_debug (5, "[%s] d_read_result %d len %d input [%.*s]\n", dev, *read_result, s, MIN(s, rb->size - rb->read), (char*)rb->buffer + rb->read); 118 | */ 119 | 120 | if (*read_result == 0) 121 | { 122 | res = rb_memcmp (rb, "\r\n", 2); 123 | if (res == 0) 124 | { 125 | rb_read_upd (rb, 2); 126 | *read_result = 1; 127 | 128 | return at_read_result_iov (dev, read_result, rb, iov); 129 | } 130 | else if (res > 0) 131 | { 132 | if (rb_memcmp (rb, "\n", 1) == 0) 133 | { 134 | ast_debug (5, "[%s] multiline response\n", dev); 135 | rb_read_upd (rb, 1); 136 | 137 | return at_read_result_iov (dev, read_result, rb, iov); 138 | } 139 | 140 | if (rb_read_until_char_iov (rb, iov, '\r') > 0) 141 | { 142 | s = iov[0].iov_len + iov[1].iov_len + 1; 143 | } 144 | 145 | rb_read_upd (rb, s); 146 | 147 | return at_read_result_iov (dev, read_result, rb, iov); 148 | } 149 | 150 | return 0; 151 | } 152 | else 153 | { 154 | if (rb_memcmp (rb, "+CSSI:", 6) == 0) 155 | { 156 | iovcnt = rb_read_n_iov (rb, iov, 8); 157 | if (iovcnt > 0) 158 | { 159 | *read_result = 0; 160 | } 161 | 162 | return iovcnt; 163 | } 164 | else if (rb_memcmp (rb, "\r\n+CSSU:", 8) == 0 || rb_memcmp (rb, "\r\n+CMS ERROR:", 13) == 0 || rb_memcmp (rb, "\r\n+CMGS:", 8) == 0) 165 | { 166 | rb_read_upd (rb, 2); 167 | return at_read_result_iov (dev, read_result, rb, iov); 168 | } 169 | else if (rb_memcmp (rb, "> ", 2) == 0) 170 | { 171 | *read_result = 0; 172 | return rb_read_n_iov (rb, iov, 2); 173 | } 174 | else if (rb_memcmp (rb, "+CMGR:", 6) == 0 || rb_memcmp (rb, "+CNUM:", 6) == 0 || rb_memcmp (rb, "ERROR+CNUM:", 11) == 0 || rb_memcmp (rb, "+CLCC:", 6) == 0) 175 | { 176 | iovcnt = rb_read_until_mem_iov (rb, iov, "\n\r\nOK\r\n", 7); 177 | if (iovcnt > 0) 178 | { 179 | *read_result = 0; 180 | } 181 | 182 | return iovcnt; 183 | } 184 | else 185 | { 186 | iovcnt = rb_read_until_mem_iov (rb, iov, "\r\n", 2); 187 | if (iovcnt > 0) 188 | { 189 | *read_result = 0; 190 | s = iov[0].iov_len + iov[1].iov_len + 1; 191 | 192 | return rb_read_n_iov (rb, iov, s); 193 | } 194 | } 195 | } 196 | } 197 | 198 | return 0; 199 | } 200 | 201 | EXPORT_DEF at_res_t at_read_result_classification (struct ringbuffer * rb, size_t len) 202 | { 203 | at_res_t at_res = RES_UNKNOWN; 204 | unsigned idx; 205 | 206 | for(idx = at_responses.ids_first; idx < at_responses.ids; idx++) 207 | { 208 | if (rb_memcmp (rb, at_responses.responses[idx].id, at_responses.responses[idx].idlen) == 0) 209 | { 210 | at_res = at_responses.responses[idx].res; 211 | break; 212 | } 213 | } 214 | 215 | switch (at_res) 216 | { 217 | case RES_SMS_PROMPT: 218 | len = 2; 219 | break; 220 | 221 | case RES_CMGR: 222 | len += 7; 223 | break; 224 | 225 | case RES_CSSI: 226 | len = 8; 227 | break; 228 | default: 229 | len += 1; 230 | break; 231 | } 232 | 233 | rb_read_upd (rb, len); 234 | 235 | /* ast_debug (5, "receive result '%s'\n", at_res2str (at_res)); 236 | */ 237 | 238 | return at_res; 239 | } 240 | -------------------------------------------------------------------------------- /at_read.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_AT_READ_H_INCLUDED 5 | #define CHAN_DONGLE_AT_READ_H_INCLUDED 6 | 7 | #include "at_response.h" /* at_res_t */ 8 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 9 | 10 | struct pvt; 11 | struct ringbuffer; 12 | struct iovec; 13 | 14 | EXPORT_DECL int at_wait (int fd, int* ms); 15 | EXPORT_DECL ssize_t at_read (int fd, const char * dev, struct ringbuffer* rb); 16 | EXPORT_DECL int at_read_result_iov (const char * dev, int * read_result, struct ringbuffer* rb, struct iovec * iov); 17 | EXPORT_DECL at_res_t at_read_result_classification (struct ringbuffer * rb, size_t len); 18 | 19 | #endif /* CHAN_DONGLE_AT_READ_H_INCLUDED */ 20 | -------------------------------------------------------------------------------- /at_response.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_AT_RESPONSE_H_INCLUDED 5 | #define CHAN_DONGLE_AT_RESPONSE_H_INCLUDED 6 | 7 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 8 | 9 | struct pvt; 10 | struct iovec; 11 | 12 | /* magic order!!! keep this enum order same as in at_responses_list */ 13 | typedef enum { 14 | RES_PARSE_ERROR = -1, 15 | RES_MIN = RES_PARSE_ERROR, 16 | RES_UNKNOWN = 0, 17 | 18 | RES_BOOT, 19 | RES_BUSY, 20 | RES_CEND, 21 | 22 | RES_CMGR, 23 | RES_CMS_ERROR, 24 | RES_CMTI, 25 | RES_CNUM, 26 | 27 | RES_CONF, 28 | RES_CONN, 29 | RES_COPS, 30 | RES_CPIN, 31 | 32 | RES_CREG, 33 | RES_CSQ, 34 | RES_CSSI, 35 | RES_CSSU, 36 | 37 | RES_CUSD, 38 | RES_ERROR, 39 | RES_MODE, 40 | RES_NO_CARRIER, 41 | 42 | RES_NO_DIALTONE, 43 | RES_OK, 44 | RES_ORIG, 45 | RES_RING, 46 | 47 | RES_RSSI, 48 | RES_SMMEMFULL, 49 | RES_SMS_PROMPT, 50 | RES_SRVST, 51 | 52 | RES_CVOICE, 53 | RES_CMGS, 54 | RES_CPMS, 55 | RES_CSCA, 56 | RES_CLCC, 57 | RES_CCWA, 58 | RES_MAX = RES_CCWA, 59 | } at_res_t; 60 | 61 | /*! response description */ 62 | typedef struct at_response_t 63 | { 64 | at_res_t res; 65 | const char* name; 66 | const char* id; 67 | unsigned idlen; 68 | } at_response_t; 69 | 70 | /*! responses control */ 71 | typedef struct at_responses_t 72 | { 73 | const at_response_t* responses; 74 | unsigned ids_first; /*!< index of first id */ 75 | unsigned ids; /*!< number of ids */ 76 | int name_first; /*!< value of enum for first name */ 77 | int name_last; /*!< value of enum for last name */ 78 | } at_responses_t; 79 | 80 | /*! responses description */ 81 | EXPORT_DECL const at_responses_t at_responses; 82 | EXPORT_DECL const char* at_res2str (at_res_t res); 83 | EXPORT_DECL int at_response (struct pvt* pvt, const struct iovec * iov, int iovcnt, at_res_t at_res); 84 | 85 | #endif /* CHAN_DONGLE_AT_RESPONSE_H_INCLUDED */ 86 | -------------------------------------------------------------------------------- /chan_dongle.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | */ 9 | #ifndef CHAN_DONGLE_H_INCLUDED 10 | #define CHAN_DONGLE_H_INCLUDED 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "mixbuffer.h" /* struct mixbuffer */ 17 | //#include "ringbuffer.h" /* struct ringbuffer */ 18 | #include "cpvt.h" /* struct cpvt */ 19 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 20 | #include "dc_config.h" /* pvt_config_t */ 21 | 22 | #define MODULE_DESCRIPTION "Huawei 3G Dongle Channel Driver" 23 | #define MAXDONGLEDEVICES 256 24 | 25 | INLINE_DECL const char * dev_state2str(dev_state_t state) 26 | { 27 | return enum2str(state, dev_state_strs, ITEMS_OF(dev_state_strs)); 28 | } 29 | 30 | INLINE_DECL const char * dev_state2str_msg(dev_state_t state) 31 | { 32 | static const char * const states[] = { "Stop scheduled", "Restart scheduled", "Removal scheduled", "Start scheduled" }; 33 | return enum2str(state, states, ITEMS_OF(states)); 34 | } 35 | 36 | /* Only linear is allowed */ 37 | #if ASTERISK_VERSION_NUM < 130000 /* -13 */ 38 | EXPORT_DEF struct ast_format chan_dongle_format; 39 | EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; 40 | #endif 41 | 42 | typedef enum { 43 | RESTATE_TIME_NOW = 0, 44 | RESTATE_TIME_GRACEFULLY, 45 | RESTATE_TIME_CONVENIENT, 46 | } restate_time_t; 47 | 48 | /* state */ 49 | typedef struct pvt_state 50 | { 51 | char audio_tty[DEVPATHLEN]; /*!< tty for audio connection */ 52 | char data_tty[DEVPATHLEN]; /*!< tty for AT commands */ 53 | uint32_t at_tasks; /*!< number of active tasks in at_queue */ 54 | uint32_t at_cmds; /*!< number of active commands in at_queue */ 55 | uint32_t chansno; /*!< number of channels in channels list */ 56 | uint8_t chan_count[CALL_STATES_NUMBER]; /*!< channel number grouped by state */ 57 | } pvt_state_t; 58 | 59 | #define PVT_STATE_T(state, name) ((state)->name) 60 | 61 | /* statictics */ 62 | typedef struct pvt_stat 63 | { 64 | uint32_t at_tasks; /*!< number of tasks added to queue */ 65 | uint32_t at_cmds; /*!< number of commands added to queue */ 66 | uint32_t at_responces; /*!< number of responses handled */ 67 | 68 | uint32_t d_read_bytes; /*!< number of bytes of commands actually readed from device */ 69 | uint32_t d_write_bytes; /*!< number of bytes of commands actually written to device */ 70 | 71 | uint64_t a_read_bytes; /*!< number of bytes of audio readed from device */ 72 | uint64_t a_write_bytes; /*!< number of bytes of audio written to device */ 73 | 74 | uint32_t read_frames; /*!< number of frames readed from device */ 75 | uint32_t read_sframes; /*!< number of truncated frames readed from device */ 76 | 77 | uint32_t write_frames; /*!< number of tries to frame write */ 78 | uint32_t write_tframes; /*!< number of truncated frames to write */ 79 | uint32_t write_sframes; /*!< number of silence frames to write */ 80 | 81 | uint64_t write_rb_overflow_bytes; /*!< number of overflow bytes */ 82 | uint32_t write_rb_overflow; /*!< number of times when a_write_rb overflowed */ 83 | 84 | uint32_t in_calls; /*!< number of incoming calls not including waiting */ 85 | uint32_t cw_calls; /*!< number of waiting calls */ 86 | uint32_t out_calls; /*!< number of all outgoing calls attempts */ 87 | uint32_t in_calls_handled; /*!< number of ncoming/waiting calls passed to dialplan */ 88 | uint32_t in_pbx_fails; /*!< number of start_pbx fails */ 89 | 90 | uint32_t calls_answered[2]; /*!< number of outgoing and incoming/waiting calls answered */ 91 | uint32_t calls_duration[2]; /*!< seconds of outgoing and incoming/waiting calls */ 92 | } pvt_stat_t; 93 | 94 | #define PVT_STAT_T(stat, name) ((stat)->name) 95 | 96 | struct at_queue_task; 97 | 98 | typedef struct pvt 99 | { 100 | AST_LIST_ENTRY (pvt) entry; /*!< linked list pointers */ 101 | 102 | ast_mutex_t lock; /*!< pvt lock */ 103 | AST_LIST_HEAD_NOLOCK (, at_queue_task) at_queue; /*!< queue for commands to modem */ 104 | 105 | AST_LIST_HEAD_NOLOCK (, cpvt) chans; /*!< list of channels */ 106 | struct cpvt sys_chan; /*!< system channel */ 107 | struct cpvt *last_dialed_cpvt; /*!< channel what last call successfully set ATDnum; leave until ^ORIG received; need because real call idx of dialing call unknown until ^ORIG */ 108 | 109 | pthread_t monitor_thread; /*!< monitor (at commands reader) thread handle */ 110 | 111 | int audio_fd; /*!< audio descriptor */ 112 | int data_fd; /*!< data descriptor */ 113 | char * alock; /*!< name of lockfile for audio */ 114 | char * dlock; /*!< name of lockfile for data */ 115 | 116 | struct ast_dsp* dsp; /*!< silence/DTMF detector - FIXME: must be in cpvt */ 117 | dc_dtmf_setting_t real_dtmf; /*!< real DTMF setting */ 118 | 119 | struct ast_timer* a_timer; /*!< audio write timer */ 120 | 121 | char a_write_buf[FRAME_SIZE * 5]; /*!< audio write buffer */ 122 | struct mixbuffer a_write_mixb; /*!< audio mix buffer */ 123 | // struct ringbuffer a_write_rb; /*!< audio ring buffer */ 124 | 125 | // char a_read_buf[FRAME_SIZE + AST_FRIENDLY_OFFSET]; /*!< audio read buffer */ 126 | // struct ast_frame a_read_frame; /*!< readed frame buffer */ 127 | 128 | 129 | char dtmf_digit; /*!< last DTMF digit */ 130 | struct timeval dtmf_begin_time; /*!< time of begin of last DTMF digit */ 131 | struct timeval dtmf_end_time; /*!< time of end of last DTMF digit */ 132 | 133 | int timeout; /*!< used to set the timeout for data */ 134 | #define DATA_READ_TIMEOUT 10000 /* 10 seconds */ 135 | 136 | unsigned long channel_instanse; /*!< number of channels created on this device */ 137 | unsigned int rings; /*!< ring/ccwa number distributed to at_response_clcc() */ 138 | 139 | /* device caps */ 140 | unsigned int use_ucs2_encoding:1; 141 | unsigned int cusd_use_7bit_encoding:1; 142 | unsigned int cusd_use_ucs2_decoding:1; 143 | 144 | /* device state */ 145 | int gsm_reg_status; 146 | int rssi; 147 | int linkmode; 148 | int linksubmode; 149 | char provider_name[32]; 150 | char manufacturer[32]; 151 | char model[32]; 152 | char firmware[32]; 153 | char imei[17]; 154 | char imsi[17]; 155 | char subscriber_number[128]; 156 | char location_area_code[8]; 157 | char cell_id[8]; 158 | char sms_scenter[20]; 159 | 160 | volatile unsigned int connected:1; /*!< do we have an connection to a device */ 161 | unsigned int initialized:1; /*!< whether a service level connection exists or not */ 162 | unsigned int gsm_registered:1; /*!< do we have an registration to a GSM */ 163 | unsigned int dialing; /*!< HW state; true from ATD response OK until CEND or CONN for this call idx */ 164 | unsigned int ring:1; /*!< HW state; true if has incoming call from first RING until CEND or CONN */ 165 | unsigned int cwaiting:1; /*!< HW state; true if has incoming call waiting from first CCWA until CEND or CONN for */ 166 | unsigned int outgoing_sms:1; /*!< outgoing sms */ 167 | unsigned int incoming_sms:1; /*!< incoming sms */ 168 | unsigned int volume_sync_step:2; /*!< volume synchronized stage */ 169 | #define VOLUME_SYNC_BEGIN 0 170 | #define VOLUME_SYNC_DONE 3 171 | 172 | unsigned int use_pdu:1; /*!< PDU SMS mode in force */ 173 | unsigned int has_sms:1; /*!< device has SMS support */ 174 | unsigned int has_voice:1; /*!< device has voice call support */ 175 | unsigned int has_call_waiting:1; /*!< call waiting enabled on device */ 176 | 177 | unsigned int group_last_used:1; /*!< mark the last used device */ 178 | unsigned int prov_last_used:1; /*!< mark the last used device */ 179 | unsigned int sim_last_used:1; /*!< mark the last used device */ 180 | 181 | unsigned int terminate_monitor:1; /*!< non-zero if we want terminate monitor thread i.e. restart, stop, remove */ 182 | // unsigned int off:1; /*!< device not used */ 183 | // unsigned int prevent_new:1; /*!< prevent new usage */ 184 | 185 | unsigned int has_subscriber_number:1; /*!< subscriber_number field is valid */ 186 | // unsigned int monitor_running:1; /*!< true if monitor thread is running */ 187 | unsigned int must_remove:1; /*!< mean must removed from list: NOT FULLY THREADSAFE */ 188 | 189 | volatile dev_state_t desired_state; /*!< desired state */ 190 | volatile restate_time_t restart_time; /*!< time when change state */ 191 | volatile dev_state_t current_state; /*!< current state */ 192 | 193 | pvt_config_t settings; /*!< all device settings from config file */ 194 | pvt_state_t state; /*!< state */ 195 | pvt_stat_t stat; /*!< various statistics */ 196 | } pvt_t; 197 | 198 | #define CONF_GLOBAL(name) (gpublic->global_settings.name) 199 | #define SCONF_GLOBAL(state, name) ((state)->global_settings.name) 200 | 201 | #define CONF_SHARED(pvt, name) SCONFIG(&((pvt)->settings), name) 202 | #define CONF_UNIQ(pvt, name) UCONFIG(&((pvt)->settings), name) 203 | #define PVT_ID(pvt) UCONFIG(&((pvt)->settings), id) 204 | 205 | #define PVT_STATE(pvt, name) PVT_STATE_T(&(pvt)->state, name) 206 | #define PVT_STAT(pvt, name) PVT_STAT_T(&(pvt)->stat, name) 207 | 208 | typedef struct public_state 209 | { 210 | AST_RWLIST_HEAD(devices, pvt) devices; 211 | ast_mutex_t discovery_lock; 212 | pthread_t discovery_thread; /* The discovery thread handler */ 213 | volatile int unloading_flag; /* no need mutex or other locking for protect this variable because no concurent r/w and set non-0 atomically */ 214 | ast_mutex_t round_robin_mtx; 215 | struct pvt * round_robin[MAXDONGLEDEVICES]; 216 | struct dc_gconfig global_settings; 217 | } public_state_t; 218 | 219 | EXPORT_DECL public_state_t * gpublic; 220 | 221 | EXPORT_DECL void clean_read_data(const char * devname, int fd); 222 | EXPORT_DECL int pvt_get_pseudo_call_idx(const struct pvt * pvt); 223 | EXPORT_DECL int ready4voice_call(const struct pvt* pvt, const struct cpvt * current_cpvt, int opts); 224 | EXPORT_DECL int is_dial_possible(const struct pvt * pvt, int opts); 225 | 226 | EXPORT_DECL const char * pvt_str_state(const struct pvt* pvt); 227 | EXPORT_DECL struct ast_str * pvt_str_state_ex(const struct pvt* pvt); 228 | EXPORT_DECL const char * GSM_regstate2str(int gsm_reg_status); 229 | EXPORT_DECL const char * sys_mode2str(int sys_mode); 230 | EXPORT_DECL const char * sys_submode2str(int sys_submode); 231 | EXPORT_DECL char* rssi2dBm(int rssi, char* buf, unsigned len); 232 | 233 | EXPORT_DECL void pvt_on_create_1st_channel(struct pvt* pvt); 234 | EXPORT_DECL void pvt_on_remove_last_channel(struct pvt* pvt); 235 | EXPORT_DECL void pvt_reload(restate_time_t when); 236 | EXPORT_DECL int pvt_enabled(const struct pvt * pvt); 237 | EXPORT_DECL void pvt_try_restate(struct pvt * pvt); 238 | 239 | EXPORT_DECL int opentty (const char* dev, char ** lockfile); 240 | EXPORT_DECL void closetty(int fd, char ** lockfname); 241 | EXPORT_DECL int lock_try(const char * devname, char ** lockname); 242 | EXPORT_DECL struct pvt * find_device_ex(struct public_state * state, const char * name); 243 | 244 | INLINE_DECL struct pvt * find_device (const char* name) 245 | { 246 | return find_device_ex(gpublic, name); 247 | } 248 | 249 | EXPORT_DECL struct pvt * find_device_ext(const char* name, const char ** reason); 250 | EXPORT_DECL struct pvt * find_device_by_resource_ex(struct public_state * state, const char * resource, int opts, const struct ast_channel * requestor, int * exists); 251 | EXPORT_DECL void pvt_dsp_setup(struct pvt * pvt, const char * id, dc_dtmf_setting_t dtmf_new); 252 | 253 | INLINE_DECL struct pvt * find_device_by_resource(const char * resource, int opts, const struct ast_channel * requestor, int * exists) 254 | { 255 | return find_device_by_resource_ex(gpublic, resource, opts, requestor, exists); 256 | } 257 | 258 | EXPORT_DECL struct ast_module * self_module(); 259 | 260 | #define PVT_NO_CHANS(pvt) (PVT_STATE(pvt, chansno) == 0) 261 | 262 | #endif /* CHAN_DONGLE_H_INCLUDED */ 263 | -------------------------------------------------------------------------------- /channel.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_CHANNEL_H_INCLUDED 5 | #define CHAN_DONGLE_CHANNEL_H_INCLUDED 6 | 7 | #include 8 | #include /* enum ast_control_frame_type */ 9 | 10 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 11 | 12 | 13 | typedef struct channel_var 14 | { 15 | const char * name; 16 | char * value; 17 | } channel_var_t; 18 | 19 | struct pvt; 20 | struct cpvt; 21 | 22 | EXPORT_DECL struct ast_channel_tech channel_tech; 23 | #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ 24 | EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_assigned_ids *assignedids, const struct ast_channel * requestor); 25 | #else 26 | EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_channel * requestor); 27 | #endif 28 | EXPORT_DECL int queue_control_channel (struct cpvt * cpvt, enum ast_control_frame_type control); 29 | EXPORT_DECL int queue_hangup (struct ast_channel * channel, int hangupcause); 30 | EXPORT_DECL void start_local_channel (struct pvt * pvt, const char * exten, const char * number, channel_var_t * vars); 31 | EXPORT_DECL void change_channel_state(struct cpvt * cpvt, unsigned newstate, int cause); 32 | EXPORT_DECL int channels_loop(struct pvt * pvt, const struct ast_channel * requestor); 33 | 34 | 35 | #endif /* CHAN_DONGLE_CHANNEL_H_INCLUDED */ 36 | -------------------------------------------------------------------------------- /char_conv.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | Artem Makhutov 4 | http://www.makhutov.org 5 | 6 | Dmitry Vagin 7 | 8 | Copyright (C) 2010 - 2011 9 | bg 10 | */ 11 | #ifdef HAVE_CONFIG_H 12 | #include 13 | #endif /* HAVE_CONFIG_H */ 14 | 15 | #include 16 | 17 | #include /* iconv_t iconv() */ 18 | #include /* memcpy() */ 19 | #include /* sscanf() snprintf() */ 20 | #include /* EINVAL */ 21 | 22 | #include "char_conv.h" 23 | #include "mutils.h" /* ITEMS_OF() */ 24 | 25 | static ssize_t convert_string (const char* in, size_t in_length, char* out, size_t out_size, char* from, char* to) 26 | { 27 | ICONV_CONST char* in_ptr = (ICONV_CONST char*) in; 28 | size_t in_bytesleft = in_length; 29 | char* out_ptr = out; 30 | size_t out_bytesleft = out_size - 1; 31 | ICONV_T cd = (ICONV_T) -1; 32 | ssize_t res; 33 | 34 | cd = iconv_open (to, from); 35 | if (cd == (ICONV_T) -1) 36 | { 37 | return -2; 38 | } 39 | 40 | res = iconv (cd, &in_ptr, &in_bytesleft, &out_ptr, &out_bytesleft); 41 | if (res < 0) 42 | { 43 | iconv_close (cd); 44 | return -3; 45 | } 46 | 47 | iconv_close (cd); 48 | 49 | *out_ptr = '\0'; 50 | 51 | return (out_ptr - out); 52 | } 53 | 54 | #/* convert 1 hex digits of PDU to byte, return < 0 on error */ 55 | EXPORT_DEF int parse_hexdigit(int hex) 56 | { 57 | if(hex >= '0' && hex <= '9') 58 | return hex - '0'; 59 | if(hex >= 'a' && hex <= 'f') 60 | return hex - 'a' + 10; 61 | if(hex >= 'A' && hex <= 'F') 62 | return hex - 'A' + 10; 63 | return -1; 64 | } 65 | 66 | static ssize_t hexstr_to_8bitchars (const char* in, size_t in_length, char* out, size_t out_size) 67 | { 68 | int d1, d2; 69 | 70 | /* odd number of chars check */ 71 | if (in_length & 0x1) 72 | return -EINVAL; 73 | 74 | in_length = in_length >> 1; 75 | 76 | if (out_size - 1 < in_length) 77 | { 78 | return -ENOMEM; 79 | } 80 | out_size = in_length; 81 | 82 | for (; in_length; --in_length) 83 | { 84 | d1 = parse_hexdigit(*in++); 85 | if(d1 < 0) 86 | return -EINVAL; 87 | d2 = parse_hexdigit(*in++); 88 | if(d2 < 0) 89 | return -EINVAL; 90 | *out++ = (d1 << 4) | d2; 91 | } 92 | 93 | *out = 0; 94 | 95 | return out_size; 96 | } 97 | 98 | static ssize_t chars8bit_to_hexstr (const char* in, size_t in_length, char* out, size_t out_size) 99 | { 100 | static const char hex_table[] = "0123456789ABCDEF"; 101 | const unsigned char *in2 = (const unsigned char *)in; /* for save time of first & 0x0F */ 102 | 103 | if (out_size - 1 < in_length * 2) 104 | { 105 | return -1; 106 | } 107 | out_size = in_length * 2; 108 | 109 | for (; in_length; --in_length, ++in2) 110 | { 111 | *out++ = hex_table[*in2 >> 4]; 112 | *out++ = hex_table[*in2 & 0xF]; 113 | } 114 | 115 | *out = 0; 116 | 117 | return out_size; 118 | } 119 | 120 | static ssize_t hexstr_ucs2_to_utf8 (const char* in, size_t in_length, char* out, size_t out_size) 121 | { 122 | char buf[out_size]; 123 | ssize_t res; 124 | 125 | if (out_size - 1 < in_length / 2) 126 | { 127 | return -1; 128 | } 129 | 130 | res = hexstr_to_8bitchars (in, in_length, buf, out_size); 131 | if (res < 0) 132 | { 133 | return res; 134 | } 135 | 136 | res = convert_string (buf, res, out, out_size, "UCS-2BE", "UTF-8"); 137 | 138 | return res; 139 | } 140 | 141 | static ssize_t utf8_to_hexstr_ucs2 (const char* in, size_t in_length, char* out, size_t out_size) 142 | { 143 | char buf[out_size]; 144 | ssize_t res; 145 | 146 | if (out_size - 1 < in_length * 4) 147 | { 148 | return -1; 149 | } 150 | 151 | res = convert_string (in, in_length, buf, out_size, "UTF-8", "UCS-2BE"); 152 | if (res < 0) 153 | { 154 | return res; 155 | } 156 | 157 | res = chars8bit_to_hexstr (buf, res, out, out_size); 158 | 159 | return res; 160 | } 161 | 162 | static ssize_t char_to_hexstr_7bit (const char* in, size_t in_length, char* out, size_t out_size) 163 | { 164 | size_t i; 165 | size_t x = 0; 166 | size_t s; 167 | unsigned char c; 168 | unsigned char b; 169 | char buf[] = { 0x0, 0x0, 0x0 }; 170 | 171 | x = (in_length - in_length / 8) * 2; 172 | if (out_size - 1 < x) 173 | { 174 | return -1; 175 | } 176 | 177 | if(in_length > 0) 178 | { 179 | in_length--; 180 | for (i = 0, x = 0, s = 0; i < in_length; i++) 181 | { 182 | if (s == 7) 183 | { 184 | s = 0; 185 | continue; 186 | } 187 | 188 | c = in[i] >> s; 189 | b = in[i + 1] << (7 - s); 190 | c = c | b; 191 | s++; 192 | 193 | snprintf (buf, sizeof(buf), "%.2X", c); 194 | 195 | memcpy (out + x, buf, 2); 196 | x = x + 2; 197 | } 198 | 199 | c = in[i] >> s; 200 | snprintf (buf, sizeof(buf), "%.2X", c); 201 | memcpy (out + x, buf, 2); 202 | x = x + 2; 203 | } 204 | out[x] = '\0'; 205 | 206 | return x; 207 | } 208 | 209 | static ssize_t hexstr_7bit_to_char (const char* in, size_t in_length, char* out, size_t out_size) 210 | { 211 | size_t i; 212 | size_t x; 213 | size_t s; 214 | int hexval; 215 | unsigned char c; 216 | unsigned char b; 217 | char buf[] = { 0x0, 0x0, 0x0 }; 218 | 219 | in_length = in_length / 2; 220 | x = in_length + in_length / 7; 221 | if (out_size - 1 < x) 222 | { 223 | return -1; 224 | } 225 | 226 | for (i = 0, x = 0, s = 1, b = 0; i < in_length; i++) 227 | { 228 | memcpy (buf, in + i * 2, 2); 229 | if (sscanf (buf, "%x", &hexval) != 1) 230 | { 231 | return -1; 232 | } 233 | 234 | c = ((unsigned char) hexval) << s; 235 | c = (c >> 1) | b; 236 | b = ((unsigned char) hexval) >> (8 - s); 237 | 238 | out[x] = c; 239 | x++; s++; 240 | 241 | if (s == 8) 242 | { 243 | out[x] = b; 244 | s = 1; b = 0; 245 | x++; 246 | } 247 | } 248 | 249 | out[x] = '\0'; 250 | 251 | return x; 252 | } 253 | 254 | #/* */ 255 | ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) 256 | { 257 | // FIXME: or copy out_size-1 bytes only ? 258 | if (in_length <= out_size - 1) 259 | { 260 | memcpy(out, in, in_length); 261 | out[in_length] = 0; 262 | return in_length; 263 | } 264 | return -ENOMEM; 265 | } 266 | 267 | typedef ssize_t (*coder) (const char* in, size_t in_length, char* out, size_t out_size); 268 | 269 | /* array in order of values RECODE_* */ 270 | static const coder recoders[][2] = 271 | { 272 | /* in order of values STR_ENCODING_* */ 273 | { hexstr_7bit_to_char, char_to_hexstr_7bit }, /* STR_ENCODING_7BIT_HEX */ 274 | { hexstr_to_8bitchars, chars8bit_to_hexstr }, /* STR_ENCODING_8BIT_HEX */ 275 | { hexstr_ucs2_to_utf8, utf8_to_hexstr_ucs2 }, /* STR_ENCODING_UCS2_HEX */ 276 | { just_copy, just_copy }, /* STR_ENCODING_7BIT */ 277 | }; 278 | 279 | #/* */ 280 | EXPORT_DEF ssize_t str_recode(recode_direction_t dir, str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size) 281 | { 282 | unsigned idx = encoding; 283 | if((dir == RECODE_DECODE || dir == RECODE_ENCODE) && idx < ITEMS_OF(recoders)) 284 | return (recoders[idx][dir])(in, in_length, out, out_size); 285 | return -EINVAL; 286 | } 287 | 288 | #/* */ 289 | EXPORT_DEF str_encoding_t get_encoding(recode_direction_t hint, const char* in, size_t length) 290 | { 291 | if(hint == RECODE_ENCODE) 292 | { 293 | for(; length; --length, ++in) 294 | if(*in & 0x80) 295 | return STR_ENCODING_UCS2_HEX; 296 | return STR_ENCODING_7BIT_HEX; 297 | } 298 | else 299 | { 300 | size_t x; 301 | for(x = 0; x < length; ++x) 302 | { 303 | if(parse_hexdigit(in[x]) < 0) { 304 | return STR_ENCODING_7BIT; 305 | } 306 | } 307 | // TODO: STR_ENCODING_7BIT_HEX or STR_ENCODING_8BIT_HEX or STR_ENCODING_UCS2_HEX 308 | } 309 | 310 | return STR_ENCODING_UNKNOWN; 311 | } 312 | -------------------------------------------------------------------------------- /char_conv.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_CHAR_CONV_H_INCLUDED 5 | #define CHAN_DONGLE_CHAR_CONV_H_INCLUDED 6 | 7 | #include /* ssize_t size_t */ 8 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 9 | 10 | /* encoding types of strings to/from device */ 11 | /* for simplefy first 3 values same as in PDU DCS bits 3..2 */ 12 | /* NOTE: order is magic see definition of recoders in char_conv.c */ 13 | typedef enum { 14 | STR_ENCODING_7BIT_HEX = 0, /* 7bit encoding */ 15 | STR_ENCODING_8BIT_HEX, /* 8bit encoding */ 16 | STR_ENCODING_UCS2_HEX, /* UCS-2 in hex like PDU */ 17 | /* TODO: check its really 7bit input from device */ 18 | STR_ENCODING_7BIT, /* 7bit ASCII no need recode to utf-8 */ 19 | // STR_ENCODING_8BIT, /* 8bit */ 20 | // STR_ENCODING_UCS2, /* UCS2 */ 21 | STR_ENCODING_UNKNOWN, /* still unknown */ 22 | } str_encoding_t; 23 | 24 | typedef enum { 25 | RECODE_DECODE = 0, /* from encoded to UTF-8 */ 26 | RECODE_ENCODE /* from UTF-8 to encoded */ 27 | } recode_direction_t; 28 | 29 | /* recode in both directions */ 30 | EXPORT_DECL ssize_t str_recode(recode_direction_t dir, str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size); 31 | 32 | EXPORT_DECL int parse_hexdigit(int hex); 33 | EXPORT_DECL str_encoding_t get_encoding(recode_direction_t hint, const char * in, size_t in_length); 34 | 35 | #endif /* CHAN_DONGLE_CHAR_CONV_H_INCLUDED */ 36 | -------------------------------------------------------------------------------- /cli.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_CLI_H_INCLUDED 5 | #define CHAN_DONGLE_CLI_H_INCLUDED 6 | 7 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 8 | 9 | EXPORT_DECL void cli_register(); 10 | EXPORT_DECL void cli_unregister(); 11 | 12 | #endif /* CHAN_DONGLE_CLI_H_INCLUDED */ 13 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_INCLUDED 2 | #define CONFIG_H_INCLUDED 3 | 4 | /* name of asterisk module */ 5 | #undef AST_MODULE 6 | 7 | /* Build extention applications */ 8 | #undef BUILD_APPLICATIONS 9 | 10 | /* Build Manager extentions */ 11 | #undef BUILD_MANAGER 12 | 13 | /* Define to 1 if you have the header file. */ 14 | #undef HAVE_FCNTL_H 15 | 16 | /* Define to 1 if you have the header file. */ 17 | #undef HAVE_ICONV_H 18 | 19 | /* Define to 1 if you have the header file. */ 20 | #undef HAVE_INTTYPES_H 21 | 22 | /* Define to 1 if you have the `memchr' function. */ 23 | #undef HAVE_MEMCHR 24 | 25 | /* Define to 1 if you have the `memmem' function. */ 26 | #undef HAVE_MEMMEM 27 | 28 | /* Define to 1 if you have the `memmove' function. */ 29 | #undef HAVE_MEMMOVE 30 | 31 | /* Define to 1 if you have the header file. */ 32 | #undef HAVE_MEMORY_H 33 | 34 | /* Define to 1 if you have the `memset' function. */ 35 | #undef HAVE_MEMSET 36 | 37 | /* Define to 1 if you have the header file. */ 38 | #undef HAVE_STDINT_H 39 | 40 | /* Define to 1 if you have the header file. */ 41 | #undef HAVE_STDLIB_H 42 | 43 | /* Define to 1 if you have the `strcasecmp' function. */ 44 | #undef HAVE_STRCASECMP 45 | 46 | /* Define to 1 if you have the `strchr' function. */ 47 | #undef HAVE_STRCHR 48 | 49 | /* Define to 1 if you have the `realpath' function. */ 50 | #undef HAVE_REALPATH 51 | 52 | /* Define to 1 if you have the header file. */ 53 | #undef HAVE_STRINGS_H 54 | 55 | /* Define to 1 if you have the header file. */ 56 | #undef HAVE_STRING_H 57 | 58 | /* Define to 1 if you have the `strncasecmp' function. */ 59 | #undef HAVE_STRNCASECMP 60 | 61 | /* Define to 1 if you have the `strtol' function. */ 62 | #undef HAVE_STRTOL 63 | 64 | /* Define to 1 if you have the header file. */ 65 | #undef HAVE_SYS_STAT_H 66 | 67 | /* Define to 1 if you have the header file. */ 68 | #undef HAVE_SYS_TIME_H 69 | 70 | /* Define to 1 if you have the header file. */ 71 | #undef HAVE_SYS_TYPES_H 72 | 73 | /* Define to 1 if you have the header file. */ 74 | #undef HAVE_TERMIOS_H 75 | 76 | /* Define to 1 if you have the header file. */ 77 | #undef HAVE_UNISTD_H 78 | 79 | /* Define to const if you has iconv() const declaration of input buffer */ 80 | #undef ICONV_CONST 81 | 82 | /* Define to iconv_t if you has iconv_t in iconv.h */ 83 | #undef ICONV_T 84 | 85 | /* Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h */ 86 | #undef HAVE_AST_CONTROL_SRCCHANGE 87 | 88 | /* Define to the address where bug reports for this package should be sent. */ 89 | #undef MODULE_BUGREPORT 90 | 91 | /* Define to the full name of this package. */ 92 | //#undef PACKAGE_NAME 93 | 94 | /* Revision of package */ 95 | #undef PACKAGE_REVISION 96 | 97 | /* Define to the full name and version of this package. */ 98 | //#undef PACKAGE_STRING 99 | 100 | /* Define to the one symbol short name of this package. */ 101 | //#undef PACKAGE_TARNAME 102 | 103 | /* Define to the home page for this package. */ 104 | #undef MODULE_URL 105 | 106 | /* Define to the version of this package. */ 107 | #undef MODULE_VERSION 108 | 109 | /* Define to 1 if you have the ANSI C header files. */ 110 | #undef STDC_HEADERS 111 | 112 | /* Define for Solaris 2.5.1 so the uint64_t typedef from , 113 | , or is not used. If the typedef were allowed, the 114 | #define below would cause a syntax error. */ 115 | #undef _UINT64_T 116 | 117 | /* Build with debugging */ 118 | #undef __DEBUG__ 119 | 120 | /* Define to empty if `const' does not conform to ANSI C. */ 121 | #undef const 122 | 123 | /* Define to `__inline__' or `__inline' if that's what the C compiler 124 | calls it, or to nothing if 'inline' is not supported under any name. */ 125 | #ifndef __cplusplus 126 | #undef inline 127 | #endif 128 | 129 | /* Define to `unsigned int' if does not define. */ 130 | #undef size_t 131 | 132 | /* Define to `int' if does not define. */ 133 | #undef ssize_t 134 | 135 | /* Define to the type of an unsigned integer type of width exactly 64 bits if 136 | such a type exists and the standard includes do not define it. */ 137 | #undef uint64_t 138 | 139 | #endif /* CONFIG_H_INCLUDED */ 140 | -------------------------------------------------------------------------------- /configure.in: -------------------------------------------------------------------------------- 1 | dnl init 2 | dnl AC_REVISION($Revision: 1.30 $) 3 | AC_PREREQ([2.60]) 4 | AC_INIT([chan_dongle],[1.1],[http://code.google.com/p/asterisk-chan-dongle/issues/list],[chan_dongle],[http://code.google.com/p/asterisk-chan-dongle]) 5 | PACKAGE_REVISION="34" 6 | AC_CANONICAL_TARGET 7 | AM_INIT_AUTOMAKE 8 | AC_CONFIG_HEADERS([config.h]) 9 | AC_CONFIG_SRCDIR([chan_dongle.c]) 10 | AC_CANONICAL_SYSTEM 11 | AC_CANONICAL_HOST 12 | 13 | dnl AM_INIT_AUTOMAKE 14 | 15 | CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" 16 | 17 | dnl Checks for user settings. 18 | dnl Set asterisk headers patch 19 | AC_ARG_WITH( 20 | [asterisk], 21 | AS_HELP_STRING([--with-asterisk=path], [set asterisk headers location]), 22 | [ if test "x$with_asterisk" = "xyes" -o "x$with_asterisk" = "xno" ; then AC_MSG_ERROR([Invalid --with-asterisk=path value]); fi ], 23 | [ with_asterisk="../include /usr/include /usr/local/include /opt/local/include" ] 24 | ) 25 | 26 | AC_ARG_ENABLE( 27 | [debug], 28 | AS_HELP_STRING([--enable-debug], [enable code debugging]), 29 | [ if test "x$enable_debug" != "xyes" ; then enable_debug="no" ; fi], 30 | [ enable_debug="no"] 31 | ) 32 | 33 | dnl Optionally disable manager. 34 | AC_ARG_ENABLE( 35 | [manager], 36 | AS_HELP_STRING([--enable-manager], [enable manager code]), 37 | [ if test "x$enable_manager" != "xyes" ; then enable_manager="no" ; fi ], 38 | [ enable_manager="yes" ] 39 | ) 40 | 41 | dnl Optionally disable applications 42 | AC_ARG_ENABLE( 43 | [apps], 44 | AS_HELP_STRING([--enable-apps], [enable applications code]), 45 | [ if test "x$enable_apps" != "xyes" ; then enable_apps="no" ; fi], 46 | [ enable_apps="yes" ] 47 | ) 48 | 49 | dnl Checks for programs. 50 | AC_PROG_CC([gcc cl cc]) 51 | AC_PROG_CPP 52 | AC_PROG_INSTALL 53 | 54 | AC_CHECK_PROG(STRIP,strip,strip) 55 | if test -z "$STRIP" ; then 56 | AC_MSG_ERROR([Can't find strip]) 57 | fi 58 | AC_CHECK_PROG(RM,rm,rm) 59 | if test -z "$RM" ; then 60 | AC_MSG_ERROR([Can't find rm]) 61 | fi 62 | 63 | dnl Checks for libraries. 64 | dnl AC_CHECK_LIB([pthread], [pthread_create]) 65 | dnl AC_CHECK_LIB([iconv], [iconv]) 66 | AC_SEARCH_LIBS([iconv], [c iconv]) 67 | 68 | dnl Checks for header files. 69 | AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h termios.h]) 70 | AC_DEFUN([AC_HEADER_FIND], [ 71 | file=$1 72 | for path in $2 ; do 73 | AC_MSG_CHECKING([whether $file in $path]) 74 | if test -f $path/$file ; then 75 | found="yes" 76 | CPPFLAGS="$CPPFLAGS -I${path}" 77 | AC_MSG_RESULT([yes]) 78 | AC_CHECK_HEADER([$file]) 79 | break; 80 | else 81 | AC_MSG_RESULT([no]) 82 | fi 83 | done 84 | if test -z "$found" ; then 85 | AC_MSG_ERROR([Can't find "$file"]) 86 | fi 87 | ] 88 | ) 89 | 90 | AC_HEADER_FIND([asterisk.h], $with_asterisk) 91 | AC_HEADER_FIND([iconv.h], /usr/include /usr/local/include /opt/local/include) 92 | 93 | AC_DEFINE([ICONV_CONST],[], [Define to const if you has iconv() const declaration of input buffer]) 94 | AC_MSG_CHECKING([for iconv use const inbuf]) 95 | AC_EGREP_HEADER([^extern.+iconv[[:space:]]*\(.+const], [iconv.h], 96 | [ 97 | AC_DEFINE([ICONV_CONST],[const]) 98 | AC_MSG_RESULT([yes]) 99 | ], 100 | [AC_MSG_RESULT([no])] 101 | ) 102 | 103 | AC_MSG_CHECKING([for iconv_t in iconv.h]) 104 | AC_EGREP_HEADER([iconv_t], [iconv.h], 105 | [ 106 | AC_DEFINE([ICONV_T], [iconv_t], , [Define to iconv_t if you has iconv_t in iconv.h]) 107 | AC_MSG_RESULT(yes) 108 | ], 109 | [ 110 | 111 | AC_DEFINE([ICONV_T], [int], [Define to iconv_t if you has iconv_t in iconv.h]) 112 | AC_MSG_RESULT(no) 113 | ]) 114 | 115 | AC_MSG_CHECKING([for AST_CONTROL_SRCCHANGE in asterisk/frame.h]) 116 | AC_EGREP_HEADER([AST_CONTROL_SRCCHANGE], [asterisk/frame.h], 117 | [ 118 | AC_DEFINE([HAVE_AST_CONTROL_SRCCHANGE], [], [Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h]) 119 | AC_MSG_RESULT([yes]) 120 | ], 121 | [AC_MSG_RESULT([no])] 122 | ) 123 | 124 | 125 | dnl Checking for library options 126 | 127 | dnl Checks for typedefs, structures, and compiler characteristics. 128 | AC_C_CONST 129 | AC_C_INLINE 130 | AC_TYPE_SIZE_T 131 | AC_TYPE_SSIZE_T 132 | AC_TYPE_UINT64_T 133 | dnl AC_CHECK_TYPE(size_t, unsigned long) 134 | dnl AC_CHECK_TYPE(ssize_t, long) 135 | dnl AC_CHECK_TYPE(uint64_t, unsigned long long) 136 | 137 | dnl checking compiler options 138 | AC_DEFUN([AC_CC_OPT], [ 139 | my_save_cflags="$CFLAGS" 140 | CFLAGS="$1" 141 | AC_MSG_CHECKING([whether CC supports $1]) 142 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], 143 | [AC_MSG_RESULT([yes])] 144 | [AC_CFLAGS="$AC_CFLAGS $2"], 145 | [AC_MSG_RESULT([no])] 146 | ) 147 | CFLAGS="$my_save_cflags"] 148 | ) 149 | 150 | AC_CFLAGS= 151 | 152 | my_save_cflags="$CFLAGS" 153 | CFLAGS=-fvisibility=hidden 154 | AC_MSG_CHECKING([whether CC supports -fvisibility=hidden]) 155 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], 156 | [AC_MSG_RESULT([yes])] 157 | [TARGET="chan_dongle.so"] 158 | [AC_CFLAGS="$AC_CFLAGS -fvisibility=hidden"], 159 | [AC_MSG_RESULT([no])] 160 | [TARGET="chan_dongles.so"] 161 | ) 162 | CFLAGS="$my_save_cflags" 163 | AC_SUBST([TARGET]) 164 | 165 | AC_CC_OPT([-fPIC],[-fPIC]) 166 | AC_CC_OPT([-Wall],[-Wall]) 167 | AC_CC_OPT([-Wextra],[-Wextra]) 168 | AC_CC_OPT([-MD -MT conftest.o -MF /dev/null -MP],[-MD -MT \$@ -MF .\$(subst /,_,\$@).d -MP]) 169 | 170 | AC_DEFUN([AC_CHECK_DESTDIR], [ 171 | if test -z "$DESTDIR" ; then 172 | for path in $1 ; do 173 | AC_MSG_CHECKING([whether DESTDIR is $path]) 174 | if test -f $path/pbx_config.so ; then 175 | AC_MSG_RESULT([yes]) 176 | found="yes" 177 | DESTDIR=$path 178 | break; 179 | else 180 | AC_MSG_RESULT([no]) 181 | fi 182 | done 183 | if test -z "$found" ; then 184 | AC_MSG_ERROR(DESTDIR is unknown, please explicite set like DESTDIR=/usr/lib/asterisk/modules ./configure) 185 | fi 186 | fi 187 | ]) 188 | 189 | AC_CHECK_DESTDIR([/usr/lib/asterisk/modules /usr/local/lib/asterisk/modules /opt/local/lib/asterisk/modules]) 190 | 191 | dnl Checks for library functions. 192 | AC_FUNC_MEMCMP 193 | AC_CHECK_FUNCS([memchr memmove memset memmem strcasecmp strchr strncasecmp strtol realpath]) 194 | 195 | 196 | dnl Apply options to defines 197 | if test "x$enable_debug" = "xyes" ; then 198 | CFLAGS="$CFLAGS -O0 -g" 199 | AC_DEFINE([__DEBUG__], [1], [Build with debugging]) 200 | else 201 | CFLAGS="$CFLAGS -O6" 202 | fi 203 | 204 | if test "x$enable_manager" = "xyes" ; then 205 | AC_DEFINE([BUILD_MANAGER],[1], [Build Manager extentions]) 206 | fi 207 | 208 | if test "x$enable_apps" = "xyes" ; then 209 | AC_DEFINE([BUILD_APPLICATIONS],[1],[Build extention applications]) 210 | fi 211 | 212 | case "$target_os" in 213 | linux*) 214 | SOLINK="-shared -Xlinker -x" 215 | DC_LDFLAGS="" 216 | ;; 217 | freebsd*) 218 | SOLINK="-shared -Xlinker -x" 219 | DC_LDFLAGS="-L/usr/local/lib" 220 | ;; 221 | openbsd*) 222 | SOLINK="-shared -Xlinker -x" 223 | DC_LDFLAGS="-L/usr/local/lib -pthread" 224 | AC_CFLAGS="$AC_CFLAGS -pthread" 225 | ;; 226 | darwin*) 227 | SOLINK="-dynamic -bundle -Xlinker -macosx_version_min -Xlinker 10.4 -Xlinker -undefined -Xlinker dynamic_lookup -force_flat_namespace" 228 | DC_LDFLAGS="-L/opt/local/lib" 229 | [ `/usr/bin/sw_vers -productVersion | cut -c1-4` == "10.6" ] && SOLINK="$SOLINK /usr/lib/bundle1.o" 230 | esac 231 | 232 | LDFLAGS="$LDFLAGS $DC_LDFLAGS" 233 | 234 | dnl AC_DEFINE_UNQUOTED([MODULE_], "$PACKAGE_", []) 235 | dnl AC_DEFINE_UNQUOTED([MODULE_], "$PACKAGE_", []) 236 | AC_DEFINE_UNQUOTED([MODULE_BUGREPORT], "$PACKAGE_BUGREPORT", [Define to the address where bug reports for this package should be sent]) 237 | AC_DEFINE_UNQUOTED([MODULE_URL], "$PACKAGE_URL", [Define to the home page for this package]) 238 | AC_DEFINE_UNQUOTED([MODULE_VERSION], "$PACKAGE_VERSION", [Define to the version of this package]) 239 | AC_DEFINE_UNQUOTED([AST_MODULE],"$PACKAGE_NAME",[name of asterisk module]) 240 | AC_DEFINE_UNQUOTED([PACKAGE_REVISION], "$PACKAGE_REVISION",[Revision of package]) 241 | 242 | AC_SUBST([SOLINK]) 243 | AC_SUBST([PACKAGE_TARNAME]) 244 | AC_SUBST([PACKAGE_REVISION]) 245 | AC_SUBST([PACKAGE_VERSION]) 246 | AC_SUBST([DESTDIR]) 247 | AC_SUBST([AC_CFLAGS]) 248 | AC_CONFIG_FILES([Makefile]) 249 | AC_OUTPUT 250 | -------------------------------------------------------------------------------- /contrib/openwrt/asterisk16-chan-dongle/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2011 OpenWrt.org 3 | # 4 | # This is free software, licensed under the GNU General Public License v2. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | include $(TOPDIR)/rules.mk 9 | 10 | PKG_NAME:=asterisk16-chan-dongle 11 | PKG_REV:=10 12 | PKG_VERSION:=1.1.r$(PKG_REV) 13 | PKG_RELEASE:=18 14 | 15 | #PKG_SOURCE_URL:=http://asterisk-chan-dongle.googlecode.com/svn/trunk/ 16 | #PKG_SOURCE_VERSION:=$(PKG_RELEASE) 17 | #PKG_SOURCE:=chan_dongle-$(PKG_VERSION).tar.gz 18 | #PKG_SOURCE_PROTO:=svn 19 | #PKG_FIXUP:=autoreconf 20 | # or 21 | PKG_SOURCE:=chan_dongle-$(PKG_VERSION).tgz 22 | PKG_SOURCE_URL=http://asterisk-chan-dongle.googlecode.com/files/ 23 | PKG_MD5SUM:=4ced3ab7094b62be2a0e843a2d4b4111 24 | 25 | PKG_SOURCE_SUBDIR:=chan_dongle-$(PKG_VERSION) 26 | PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) 27 | 28 | WITH_ASTERISK=asterisk-1.6.2.14 29 | 30 | include $(INCLUDE_DIR)/package.mk 31 | 32 | define Package/asterisk16-chan-dongle 33 | SUBMENU:=asterisk16 (Complete Open Source PBX), v1.6.x 34 | SECTION:=net 35 | CATEGORY:=Network 36 | URL:=http://www.asterisk.org/ 37 | MAINTAINER:=Hans Zandbelt 38 | DEPENDS:= +asterisk16 +libiconv-full 39 | TITLE:=Huawei UMTS 3G dongle support 40 | endef 41 | 42 | define Package/asterisk16-chan-dongle/description 43 | Asterisk channel driver for Huawei UMTS 3G dongle 44 | endef 45 | 46 | MAKE_ARGS:= \ 47 | CC="$(TARGET_CC)" \ 48 | LD="$(TARGET_CC)" \ 49 | CFLAGS="$(TARGET_CFLAGS) -DLOW_MEMORY $(TARGET_CPPFLAGS) -I$(STAGING_DIR)/usr/lib/libiconv-full/include -I$(BUILD_DIR)/$(WITH_ASTERISK)/include -DHAVE_CONFIG_H -I. -fPIC" \ 50 | LDFLAGS="$(TARGET_LDFLAGS) -L$(STAGING_DIR)/usr/lib/libiconv-full/lib -liconv" \ 51 | DESTDIR="$(PKG_INSTALL_DIR)/usr/lib/asterisk/modules" 52 | 53 | define Build/Configure 54 | $(call Build/Configure/Default, \ 55 | --with-asterisk=$(BUILD_DIR)/$(WITH_ASTERISK)/include \ 56 | $(MAKE_ARGS) \ 57 | ) 58 | endef 59 | 60 | define Build/Compile 61 | mkdir -p $(PKG_INSTALL_DIR)/usr/lib/asterisk/modules 62 | $(MAKE) -C "$(PKG_BUILD_DIR)" $(MAKE_ARGS) all install 63 | endef 64 | 65 | define Package/asterisk16-chan-dongle/conffiles 66 | /etc/asterisk/dongle.conf 67 | endef 68 | 69 | define Package/asterisk16-chan-dongle/install 70 | $(INSTALL_DIR) $(1)/etc/asterisk 71 | $(INSTALL_DATA) $(PKG_BUILD_DIR)/etc/dongle.conf $(1)/etc/asterisk/ 72 | $(INSTALL_DIR) $(1)/usr/lib/asterisk/modules 73 | $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/asterisk/modules/chan_dongle.so $(1)/usr/lib/asterisk/modules/ 74 | endef 75 | 76 | $(eval $(call BuildPackage,asterisk16-chan-dongle)) 77 | -------------------------------------------------------------------------------- /contrib/openwrt/asterisk18-chan-dongle/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2011 OpenWrt.org 3 | # 4 | # This is free software, licensed under the GNU General Public License v2. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | include $(TOPDIR)/rules.mk 9 | 10 | PKG_NAME:=asterisk18-chan-dongle 11 | PKG_REV:=10 12 | PKG_VERSION:=1.1.r$(PKG_REV) 13 | PKG_RELEASE:=18 14 | 15 | #PKG_SOURCE_URL:=http://asterisk-chan-dongle.googlecode.com/svn/trunk/ 16 | #PKG_SOURCE_VERSION:=$(PKG_RELEASE) 17 | #PKG_SOURCE:=chan_dongle-$(PKG_VERSION).tar.gz 18 | #PKG_SOURCE_PROTO:=svn 19 | #PKG_FIXUP:=autoreconf 20 | # or 21 | PKG_SOURCE:=chan_dongle-$(PKG_VERSION).tgz 22 | PKG_SOURCE_URL=http://asterisk-chan-dongle.googlecode.com/files/ 23 | PKG_MD5SUM:=4ced3ab7094b62be2a0e843a2d4b4111 24 | 25 | PKG_SOURCE_SUBDIR:=chan_dongle-$(PKG_VERSION) 26 | PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) 27 | 28 | WITH_ASTERISK=asterisk-1.8.3.2 29 | 30 | include $(INCLUDE_DIR)/package.mk 31 | 32 | define Package/asterisk18-chan-dongle 33 | SUBMENU:=asterisk18 (Complete Open Source PBX), v1.8.x 34 | SECTION:=net 35 | CATEGORY:=Network 36 | URL:=http://www.asterisk.org/ 37 | MAINTAINER:=Hans Zandbelt 38 | DEPENDS:= +asterisk18 +libiconv-full 39 | TITLE:=Huawei UMTS 3G dongle support 40 | endef 41 | 42 | define Package/asterisk18-chan-dongle/description 43 | Asterisk channel driver for Huawei UMTS 3G dongle 44 | endef 45 | 46 | MAKE_ARGS:= \ 47 | CC="$(TARGET_CC)" \ 48 | LD="$(TARGET_CC)" \ 49 | CFLAGS="$(TARGET_CFLAGS) -DLOW_MEMORY -D_XOPEN_SOURCE=600 $(TARGET_CPPFLAGS) -I$(STAGING_DIR)/usr/lib/libiconv-full/include -I$(BUILD_DIR)/$(WITH_ASTERISK)/include -DHAVE_CONFIG_H -I. -fPIC" \ 50 | LDFLAGS="$(TARGET_LDFLAGS) -L$(STAGING_DIR)/usr/lib/libiconv-full/lib -liconv" \ 51 | DESTDIR="$(PKG_INSTALL_DIR)/usr/lib/asterisk/modules" 52 | 53 | define Build/Configure 54 | $(call Build/Configure/Default, \ 55 | --with-asterisk=$(BUILD_DIR)/$(WITH_ASTERISK)/include \ 56 | $(MAKE_ARGS) \ 57 | ) 58 | endef 59 | 60 | define Build/Compile 61 | mkdir -p $(PKG_INSTALL_DIR)/usr/lib/asterisk/modules 62 | $(MAKE) -C "$(PKG_BUILD_DIR)" $(MAKE_ARGS) all install 63 | endef 64 | 65 | define Package/asterisk18-chan-dongle/conffiles 66 | /etc/asterisk/dongle.conf 67 | endef 68 | 69 | define Package/asterisk18-chan-dongle/install 70 | $(INSTALL_DIR) $(1)/etc/asterisk 71 | $(INSTALL_DATA) $(PKG_BUILD_DIR)/etc/dongle.conf $(1)/etc/asterisk/ 72 | $(INSTALL_DIR) $(1)/usr/lib/asterisk/modules 73 | $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/asterisk/modules/chan_dongle.so $(1)/usr/lib/asterisk/modules/ 74 | endef 75 | 76 | $(eval $(call BuildPackage,asterisk18-chan-dongle)) 77 | -------------------------------------------------------------------------------- /cpvt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010,2011 bg 3 | */ 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif /* HAVE_CONFIG_H */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "cpvt.h" 15 | #include "chan_dongle.h" /* struct pvt */ 16 | #include "at_queue.h" /* struct at_queue_task */ 17 | #include "mutils.h" /* ITEMS_OF() */ 18 | 19 | #/* return 0 on success */ 20 | // TODO: move to activation time, save resouces 21 | static int init_pipe(int filedes[2]) 22 | { 23 | int x; 24 | int rv; 25 | int flags; 26 | 27 | rv = pipe(filedes); 28 | if(rv == 0) { 29 | for(x = 0; x < 2; ++x) { 30 | rv = fcntl(filedes[x], F_GETFL); 31 | flags = fcntl(filedes[x], F_GETFD); 32 | if(rv == -1 || flags == -1 || (rv = fcntl(filedes[x], F_SETFL, O_NONBLOCK | rv)) == -1 || (rv = fcntl(filedes[x], F_SETFD, flags | FD_CLOEXEC)) == -1) 33 | goto bad; 34 | } 35 | return 0; 36 | bad: 37 | close(filedes[0]); 38 | close(filedes[1]); 39 | } 40 | return rv; 41 | } 42 | 43 | #/* */ 44 | EXPORT_DEF struct cpvt * cpvt_alloc(struct pvt * pvt, int call_idx, unsigned dir, call_state_t state) 45 | { 46 | int filedes[2]; 47 | struct cpvt * cpvt = NULL; 48 | 49 | if(init_pipe(filedes) == 0) 50 | { 51 | cpvt = ast_calloc (1, sizeof (*cpvt)); 52 | if(cpvt) 53 | { 54 | cpvt->pvt = pvt; 55 | cpvt->call_idx = call_idx; 56 | cpvt->state = state; 57 | cpvt->dir = dir; 58 | cpvt->rd_pipe[0] = filedes[0]; 59 | cpvt->rd_pipe[1] = filedes[1]; 60 | 61 | // rb_init (&cpvt->a_write_rb, cpvt->a_write_buf, sizeof (cpvt->a_write_buf)); 62 | 63 | AST_LIST_INSERT_TAIL(&pvt->chans, cpvt, entry); 64 | if(PVT_NO_CHANS(pvt)) 65 | pvt_on_create_1st_channel(pvt); 66 | PVT_STATE(pvt, chansno)++; 67 | PVT_STATE(pvt, chan_count[cpvt->state])++; 68 | 69 | 70 | 71 | ast_debug (3, "[%s] create cpvt for call_idx %d dir %d state '%s'\n", PVT_ID(pvt), call_idx, dir, call_state2str(state)); 72 | return cpvt; 73 | } 74 | close(filedes[0]); 75 | close(filedes[1]); 76 | } 77 | 78 | return cpvt; 79 | } 80 | 81 | #/* */ 82 | EXPORT_DEF void cpvt_free(struct cpvt* cpvt) 83 | { 84 | pvt_t * pvt = cpvt->pvt; 85 | struct cpvt * found; 86 | struct at_queue_task * task; 87 | 88 | close(cpvt->rd_pipe[1]); 89 | close(cpvt->rd_pipe[0]); 90 | 91 | ast_debug (3, "[%s] destroy cpvt for call_idx %d dir %d state '%s' flags %d has%s channel\n", PVT_ID(pvt), cpvt->call_idx, cpvt->dir, call_state2str(cpvt->state), cpvt->flags, cpvt->channel ? "" : "'t"); 92 | AST_LIST_TRAVERSE_SAFE_BEGIN(&pvt->chans, found, entry) { 93 | if(found == cpvt) 94 | { 95 | AST_LIST_REMOVE_CURRENT(entry); 96 | PVT_STATE(pvt, chan_count[cpvt->state])--; 97 | PVT_STATE(pvt, chansno) --; 98 | break; 99 | } 100 | } 101 | AST_LIST_TRAVERSE_SAFE_END; 102 | 103 | /* relink task to sys_chan */ 104 | AST_LIST_TRAVERSE(&pvt->at_queue, task, entry) { 105 | if(task->cpvt == cpvt) 106 | { 107 | task->cpvt = &pvt->sys_chan; 108 | } 109 | } 110 | /* drop last_dialed_cpvt if need */ 111 | if(pvt->last_dialed_cpvt == cpvt) 112 | pvt->last_dialed_cpvt = NULL; 113 | 114 | if(PVT_NO_CHANS(pvt)) { 115 | pvt_on_remove_last_channel(pvt); 116 | pvt_try_restate(pvt); 117 | } 118 | 119 | ast_free(cpvt); 120 | } 121 | 122 | #/* */ 123 | EXPORT_DEF struct cpvt * pvt_find_cpvt(struct pvt * pvt, int call_idx) 124 | { 125 | struct cpvt * cpvt; 126 | AST_LIST_TRAVERSE(&pvt->chans, cpvt, entry) { 127 | if(call_idx == cpvt->call_idx) 128 | return cpvt; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | #/* */ 135 | EXPORT_DEF const char * pvt_call_dir(const struct pvt * pvt) 136 | { 137 | static const char * dirs[] = { 138 | "Active", 139 | "Outgoing", 140 | "Incoming", 141 | "Both" 142 | }; 143 | 144 | int index = 0; 145 | struct cpvt * cpvt; 146 | AST_LIST_TRAVERSE(&pvt->chans, cpvt, entry) { 147 | if(cpvt->dir == CALL_DIR_OUTGOING) 148 | index |= 0x1; 149 | else 150 | index |= 0x2; 151 | } 152 | 153 | return dirs[index]; 154 | } 155 | -------------------------------------------------------------------------------- /cpvt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_CPVT_H_INCLUDED 5 | #define CHAN_DONGLE_CPVT_H_INCLUDED 6 | 7 | #include 8 | #include /* AST_LIST_ENTRY() */ 9 | #include /* AST_FRIENDLY_OFFSET */ 10 | 11 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 12 | #include "mixbuffer.h" /* struct mixstream */ 13 | #include "mutils.h" /* enum2str() ITEMS_OF() */ 14 | 15 | #define FRAME_SIZE 320 16 | 17 | typedef enum { 18 | CALL_STATE_MIN = 0, 19 | 20 | /* values from CLCC */ 21 | CALL_STATE_ACTIVE = CALL_STATE_MIN, /*!< comes from CLCC */ 22 | CALL_STATE_ONHOLD, /*!< comes from CLCC */ 23 | CALL_STATE_DIALING, /*!< comes from CLCC */ 24 | CALL_STATE_ALERTING, /*!< comes from CLCC */ 25 | CALL_STATE_INCOMING, /*!< comes from CLCC */ 26 | CALL_STATE_WAITING, /*!< comes from CLCC */ 27 | 28 | CALL_STATE_RELEASED, /*!< on CEND or channel_hangup() called */ 29 | CALL_STATE_INIT, /*!< channel_call() called */ 30 | CALL_STATE_MAX = CALL_STATE_INIT 31 | } call_state_t; 32 | #define CALL_STATES_NUMBER (CALL_STATE_MAX - CALL_STATE_MIN + 1) 33 | 34 | typedef enum { 35 | CALL_FLAG_NONE = 0, 36 | CALL_FLAG_HOLD_OTHER = 1, /*!< external, from channel_call() hold other calls and dial this number */ 37 | CALL_FLAG_NEED_HANGUP = 2, /*!< internal, require issue AT+CHUP or AT+CHLD=1x for call */ 38 | CALL_FLAG_ACTIVATED = 4, /*!< internal, fd attached to channel fds list */ 39 | CALL_FLAG_ALIVE = 8, /*!< internal, temporary, still listed in CLCC */ 40 | CALL_FLAG_CONFERENCE = 16, /*!< external, from dial() begin conference after activate this call */ 41 | CALL_FLAG_MASTER = 32, /*!< internal, channel fd[0] is pvt->audio_fd and fd[1] is timer fd */ 42 | CALL_FLAG_BRIDGE_LOOP = 64, /*!< internal, found channel bridged to channel on same device */ 43 | CALL_FLAG_BRIDGE_CHECK = 128, /*!< internal, we already do check for bridge loop */ 44 | CALL_FLAG_MULTIPARTY = 256, /*!< internal, CLCC mpty is 1 */ 45 | } call_flag_t; 46 | 47 | 48 | /* */ 49 | typedef struct cpvt { 50 | AST_LIST_ENTRY (cpvt) entry; /*!< linked list pointers */ 51 | 52 | struct ast_channel* channel; /*!< Channel pointer */ 53 | struct pvt *pvt; /*!< pointer to device structure */ 54 | 55 | short call_idx; /*!< device call ID */ 56 | #define MIN_CALL_IDX 0 57 | #define MAX_CALL_IDX 31 58 | 59 | call_state_t state; /*!< see also call_state_t */ 60 | int flags; /*!< see also call_flag_t */ 61 | 62 | /* TODO: join with flags */ 63 | unsigned int dir:1; /*!< call direction */ 64 | #define CALL_DIR_OUTGOING 0 65 | #define CALL_DIR_INCOMING 1 66 | 67 | int rd_pipe[2]; /*!< pipe for split readed from device */ 68 | #define PIPE_READ 0 69 | #define PIPE_WRITE 1 70 | 71 | struct mixstream mixstream; /*!< mix stream */ 72 | char a_read_buf[FRAME_SIZE + AST_FRIENDLY_OFFSET];/*!< audio read buffer */ 73 | struct ast_frame a_read_frame; /*!< readed frame buffer */ 74 | 75 | // size_t write; /*!< write position in pvt->a_write_buf */ 76 | // size_t used; /*!< bytes used in pvt->a_write_buf */ 77 | // char a_write_buf[FRAME_SIZE * 5]; /*!< audio write buffer */ 78 | // struct ringbuffer a_write_rb; /*!< audio ring buffer */ 79 | } cpvt_t; 80 | 81 | #define CPVT_SET_FLAGS(cpvt, flag) do { (cpvt)->flags |= (flag); } while(0) 82 | #define CPVT_RESET_FLAGS(cpvt, flag) do { (cpvt)->flags &= ~((int)flag); } while(0) 83 | #define CPVT_TEST_FLAG(cpvt, flag) ((cpvt)->flags & (flag)) 84 | #define CPVT_TEST_FLAGS(cpvt, flag) (((cpvt)->flags & (flag)) == (flag)) 85 | 86 | #define CPVT_IS_MASTER(cpvt) CPVT_TEST_FLAG(cpvt, CALL_FLAG_MASTER) 87 | #define CPVT_IS_ACTIVE(cpvt) ((cpvt)->state == CALL_STATE_ACTIVE) 88 | #define CPVT_IS_SOUND_SOURCE(cpvt) ((cpvt)->state == CALL_STATE_ACTIVE || (cpvt)->state == CALL_STATE_DIALING || (cpvt)->state == CALL_STATE_ALERTING) 89 | 90 | 91 | EXPORT_DECL struct cpvt * cpvt_alloc(struct pvt * pvt, int call_idx, unsigned dir, call_state_t statem); 92 | EXPORT_DECL void cpvt_free(struct cpvt* cpvt); 93 | 94 | EXPORT_DECL struct cpvt * pvt_find_cpvt(struct pvt * pvt, int call_idx); 95 | EXPORT_DECL const char * pvt_call_dir(const struct pvt * pvt); 96 | 97 | #/* */ 98 | INLINE_DECL const char * call_state2str(call_state_t state) 99 | { 100 | static const char * const states[] = { 101 | /* real device states */ 102 | "active", 103 | "held", 104 | "dialing", 105 | "alerting", 106 | "incoming", 107 | "waiting", 108 | 109 | /* pseudo states */ 110 | "released", 111 | "initialize" 112 | }; 113 | 114 | return enum2str(state, states, ITEMS_OF(states)); 115 | } 116 | 117 | #endif /* CHAN_DONGLE_CPVT_H_INCLUDED */ 118 | -------------------------------------------------------------------------------- /dc_config.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif /* HAVE_CONFIG_H */ 7 | 8 | #include "dc_config.h" 9 | #include /* ast_parse_caller_presentation() */ 10 | 11 | static struct ast_jb_conf jbconf_default = 12 | { 13 | .flags = 0, 14 | .max_size = -1, 15 | .resync_threshold = -1, 16 | .impl = "", 17 | .target_extra = -1, 18 | }; 19 | 20 | 21 | static const char * const dtmf_values[] = { "off", "inband", "relax" }; 22 | 23 | EXPORT_DEF int dc_dtmf_str2setting(const char * value) 24 | { 25 | return str2enum(value, dtmf_values, ITEMS_OF(dtmf_values)); 26 | } 27 | 28 | EXPORT_DEF const char * dc_dtmf_setting2str(dc_dtmf_setting_t dtmf) 29 | { 30 | return enum2str(dtmf, dtmf_values, ITEMS_OF(dtmf_values)); 31 | } 32 | 33 | #/* assume config is zerofill */ 34 | static int dc_uconfig_fill(struct ast_config * cfg, const char * cat, struct dc_uconfig * config) 35 | { 36 | const char * audio_tty; 37 | const char * data_tty; 38 | const char * imei; 39 | const char * imsi; 40 | 41 | audio_tty = ast_variable_retrieve (cfg, cat, "audio"); 42 | data_tty = ast_variable_retrieve (cfg, cat, "data"); 43 | imei = ast_variable_retrieve (cfg, cat, "imei"); 44 | imsi = ast_variable_retrieve (cfg, cat, "imsi"); 45 | 46 | if(imei && strlen(imei) != IMEI_SIZE) { 47 | ast_log (LOG_WARNING, "[%s] Ignore invalid IMEI value '%s'\n", cat, imei); 48 | imei = NULL; 49 | } 50 | if(imsi && strlen(imsi) != IMSI_SIZE) { 51 | ast_log (LOG_WARNING, "[%s] Ignore invalid IMSI value '%s'\n", cat, imsi); 52 | imsi = NULL; 53 | } 54 | 55 | if(!audio_tty && !imei && !imsi) 56 | { 57 | ast_log (LOG_ERROR, "Skipping device %s. Missing required audio_tty setting\n", cat); 58 | return 1; 59 | } 60 | 61 | if(!data_tty && !imei && !imsi) 62 | { 63 | ast_log (LOG_ERROR, "Skipping device %s. Missing required data_tty setting\n", cat); 64 | return 1; 65 | } 66 | 67 | if((!data_tty && audio_tty) || (data_tty && !audio_tty)) 68 | { 69 | ast_log (LOG_ERROR, "Skipping device %s. data_tty and audio_tty should use together\n", cat); 70 | return 1; 71 | } 72 | 73 | ast_copy_string (config->id, cat, sizeof (config->id)); 74 | ast_copy_string (config->data_tty, S_OR(data_tty, ""), sizeof (config->data_tty)); 75 | ast_copy_string (config->audio_tty, S_OR(audio_tty, ""), sizeof (config->audio_tty)); 76 | ast_copy_string (config->imei, S_OR(imei, ""), sizeof (config->imei)); 77 | ast_copy_string (config->imsi, S_OR(imsi, ""), sizeof (config->imsi)); 78 | 79 | return 0; 80 | } 81 | 82 | #/* */ 83 | EXPORT_DEF void dc_sconfig_fill_defaults(struct dc_sconfig * config) 84 | { 85 | /* first set default values */ 86 | memset(config, 0, sizeof(*config)); 87 | 88 | ast_copy_string (config->context, "default", sizeof (config->context)); 89 | ast_copy_string (config->exten, "", sizeof (config->exten)); 90 | ast_copy_string (config->language, DEFAULT_LANGUAGE, sizeof (config->language)); 91 | 92 | config->u2diag = -1; 93 | config->resetdongle = 1; 94 | config->callingpres = -1; 95 | config->initstate = DEV_STATE_STARTED; 96 | config->callwaiting = CALL_WAITING_AUTO; 97 | config->dtmf = DC_DTMF_SETTING_RELAX; 98 | 99 | config->mindtmfgap = DEFAULT_MINDTMFGAP; 100 | config->mindtmfduration = DEFAULT_MINDTMFDURATION; 101 | config->mindtmfinterval = DEFAULT_MINDTMFINTERVAL; 102 | } 103 | 104 | #/* */ 105 | EXPORT_DEF void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struct dc_sconfig * config) 106 | { 107 | struct ast_variable * v; 108 | 109 | /* read config and translate to values */ 110 | for (v = ast_variable_browse (cfg, cat); v; v = v->next) 111 | { 112 | if (!strcasecmp (v->name, "context")) 113 | { 114 | ast_copy_string (config->context, v->value, sizeof (config->context)); 115 | } 116 | else if (!strcasecmp (v->name, "exten")) 117 | { 118 | ast_copy_string (config->exten, v->value, sizeof (config->exten)); 119 | } 120 | else if (!strcasecmp (v->name, "language")) 121 | { 122 | ast_copy_string (config->language, v->value, sizeof (config->language));/* set channel language */ 123 | } 124 | else if (!strcasecmp (v->name, "group")) 125 | { 126 | config->group = (int) strtol (v->value, (char**) NULL, 10); /* group is set to 0 if invalid */ 127 | } 128 | else if (!strcasecmp (v->name, "rxgain")) 129 | { 130 | config->rxgain = (int) strtol (v->value, (char**) NULL, 10); /* rxgain is set to 0 if invalid */ 131 | } 132 | else if (!strcasecmp (v->name, "txgain")) 133 | { 134 | config->txgain = (int) strtol (v->value, (char**) NULL, 10); /* txgain is set to 0 if invalid */ 135 | } 136 | else if (!strcasecmp (v->name, "u2diag")) 137 | { 138 | errno = 0; 139 | config->u2diag = (int) strtol (v->value, (char**) NULL, 10); /* u2diag is set to -1 if invalid */ 140 | if (config->u2diag == 0 && errno == EINVAL) 141 | { 142 | config->u2diag = -1; 143 | } 144 | } 145 | else if (!strcasecmp (v->name, "callingpres")) 146 | { 147 | config->callingpres = ast_parse_caller_presentation (v->value); 148 | if (config->callingpres == -1) 149 | { 150 | errno = 0; 151 | config->callingpres = (int) strtol (v->value, (char**) NULL, 10);/* callingpres is set to -1 if invalid */ 152 | if (config->callingpres == 0 && errno == EINVAL) 153 | { 154 | config->callingpres = -1; 155 | } 156 | } 157 | } 158 | else if (!strcasecmp (v->name, "usecallingpres")) 159 | { 160 | config->usecallingpres = ast_true (v->value); /* usecallingpres is set to 0 if invalid */ 161 | } 162 | else if (!strcasecmp (v->name, "autodeletesms")) 163 | { 164 | config->autodeletesms = ast_true (v->value); /* autodeletesms is set to 0 if invalid */ 165 | } 166 | else if (!strcasecmp (v->name, "resetdongle")) 167 | { 168 | config->resetdongle = ast_true (v->value); /* resetdongle is set to 0 if invalid */ 169 | } 170 | else if (!strcasecmp (v->name, "disablesms")) 171 | { 172 | config->disablesms = ast_true (v->value); /* disablesms is set to 0 if invalid */ 173 | } 174 | else if (!strcasecmp (v->name, "smsaspdu")) 175 | { 176 | config->smsaspdu = ast_true (v->value); /* send_sms_as_pdu us set to 0 if invalid */ 177 | } 178 | else if (!strcasecmp (v->name, "disable")) 179 | { 180 | config->initstate = ast_true (v->value) ? DEV_STATE_REMOVED : DEV_STATE_STARTED; 181 | } 182 | else if (!strcasecmp (v->name, "initstate")) 183 | { 184 | int val = str2enum(v->value, dev_state_strs, ITEMS_OF(dev_state_strs)); 185 | if(val == DEV_STATE_STOPPED || val == DEV_STATE_STARTED || val == DEV_STATE_REMOVED) 186 | config->initstate = val; 187 | else 188 | ast_log(LOG_ERROR, "Invalid value for 'initstate': '%s', must be one of 'stop' 'start' 'remove' default is 'start'\n", v->value); 189 | } 190 | else if (!strcasecmp (v->name, "callwaiting")) 191 | { 192 | if(strcasecmp(v->value, "auto")) 193 | config->callwaiting = ast_true (v->value); 194 | } 195 | else if (!strcasecmp (v->name, "dtmf")) 196 | { 197 | int val = dc_dtmf_str2setting(v->value); 198 | if(val >= 0) 199 | config->dtmf = val; 200 | else 201 | ast_log(LOG_ERROR, "Invalid value for 'dtmf': '%s', setting default 'relax'\n", v->value); 202 | } 203 | else if (!strcasecmp (v->name, "mindtmfgap")) 204 | { 205 | errno = 0; 206 | config->mindtmfgap = (int) strtol (v->value, (char**) NULL, 10); 207 | if ((config->mindtmfgap == 0 && errno == EINVAL) || config->mindtmfgap < 0) 208 | { 209 | ast_log(LOG_ERROR, "Invalid value for 'mindtmfgap' '%s', setting default %d\n", v->value, DEFAULT_MINDTMFGAP); 210 | config->mindtmfgap = DEFAULT_MINDTMFGAP; 211 | } 212 | } 213 | else if (!strcasecmp (v->name, "mindtmfduration")) 214 | { 215 | errno = 0; 216 | config->mindtmfduration = (int) strtol (v->value, (char**) NULL, 10); 217 | if ((config->mindtmfduration == 0 && errno == EINVAL) || config->mindtmfduration < 0) 218 | { 219 | ast_log(LOG_ERROR, "Invalid value for 'mindtmfgap' '%s', setting default %d\n", v->value, DEFAULT_MINDTMFDURATION); 220 | config->mindtmfduration = DEFAULT_MINDTMFDURATION; 221 | } 222 | } 223 | else if (!strcasecmp (v->name, "mindtmfinterval")) 224 | { 225 | errno = 0; 226 | config->mindtmfinterval = (int) strtol (v->value, (char**) NULL, 10); 227 | if ((config->mindtmfinterval == 0 && errno == EINVAL) || config->mindtmfinterval < 0) 228 | { 229 | ast_log(LOG_ERROR, "Invalid value for 'mindtmfinterval' '%s', setting default %d\n", v->value, DEFAULT_MINDTMFINTERVAL); 230 | config->mindtmfduration = DEFAULT_MINDTMFINTERVAL; 231 | } 232 | } 233 | } 234 | } 235 | 236 | #/* */ 237 | EXPORT_DEF void dc_gconfig_fill(struct ast_config * cfg, const char * cat, struct dc_gconfig * config) 238 | { 239 | struct ast_variable * v; 240 | int tmp; 241 | const char * stmp; 242 | 243 | /* set default values */ 244 | memcpy(&config->jbconf, &jbconf_default, sizeof(config->jbconf)); 245 | config->discovery_interval = DEFAULT_DISCOVERY_INT; 246 | 247 | stmp = ast_variable_retrieve (cfg, cat, "interval"); 248 | if(stmp) 249 | { 250 | errno = 0; 251 | tmp = (int) strtol (stmp, (char**) NULL, 10); 252 | if (tmp == 0 && errno == EINVAL) 253 | ast_log (LOG_NOTICE, "Error parsing 'interval' in general section, using default value %d\n", config->discovery_interval); 254 | else 255 | config->discovery_interval = tmp; 256 | } 257 | 258 | 259 | for (v = ast_variable_browse (cfg, cat); v; v = v->next) 260 | /* handle jb conf */ 261 | ast_jb_read_conf (&config->jbconf, v->name, v->value); 262 | } 263 | 264 | #/* */ 265 | EXPORT_DEF int dc_config_fill(struct ast_config * cfg, const char * cat, const struct dc_sconfig * parent, struct pvt_config * config) 266 | { 267 | /* try set unique first */ 268 | int err = dc_uconfig_fill(cfg, cat, &config->unique); 269 | if(!err) 270 | { 271 | /* inherit from parent */ 272 | memcpy(&config->shared, parent, sizeof(config->shared)); 273 | 274 | /* overwrite local */ 275 | dc_sconfig_fill(cfg, cat, &config->shared); 276 | } 277 | 278 | return err; 279 | } 280 | -------------------------------------------------------------------------------- /dc_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_DC_CONFIG_H_INCLUDED 5 | #define CHAN_DONGLE_DC_CONFIG_H_INCLUDED 6 | 7 | #include 8 | #include /* AST_MAX_CONTEXT MAX_LANGUAGE */ 9 | 10 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 11 | #include "mutils.h" 12 | 13 | #define CONFIG_FILE "dongle.conf" 14 | #define DEVNAMELEN 31 15 | #define IMEI_SIZE 15 16 | #define IMSI_SIZE 15 17 | #define DEVPATHLEN 256 18 | 19 | typedef enum { 20 | DEV_STATE_STOPPED = 0, 21 | DEV_STATE_RESTARTED, 22 | DEV_STATE_REMOVED, 23 | DEV_STATE_STARTED, 24 | } dev_state_t; 25 | EXPORT_DECL const char * const dev_state_strs[4]; 26 | 27 | typedef enum { 28 | CALL_WAITING_DISALLOWED = 0, 29 | CALL_WAITING_ALLOWED, 30 | CALL_WAITING_AUTO 31 | } call_waiting_t; 32 | 33 | INLINE_DECL const char * dc_cw_setting2str(call_waiting_t cw) 34 | { 35 | static const char * const options[] = { "disabled", "allowed", "auto" }; 36 | return enum2str(cw, options, ITEMS_OF(options)); 37 | } 38 | 39 | typedef enum { 40 | DC_DTMF_SETTING_OFF = 0, 41 | DC_DTMF_SETTING_INBAND, 42 | DC_DTMF_SETTING_RELAX, 43 | } dc_dtmf_setting_t; 44 | 45 | /* 46 | Config API 47 | Operations 48 | convert from string to native 49 | convent from native to string 50 | get native value 51 | get alternative presentation 52 | 53 | set native value ? 54 | 55 | types: 56 | string of limited length 57 | integer with limits 58 | enum 59 | boolean 60 | */ 61 | 62 | /* Global inherited (shared) settings */ 63 | typedef struct dc_sconfig 64 | { 65 | char context[AST_MAX_CONTEXT]; /*!< the context for incoming calls; 'default '*/ 66 | char exten[AST_MAX_EXTENSION]; /*!< exten, not overwrite valid subscriber_number */ 67 | char language[MAX_LANGUAGE]; /*!< default language 'en' */ 68 | int group; /*!< group number for group dialling 0 */ 69 | int rxgain; /*!< increase the incoming volume 0 */ 70 | int txgain; /*!< increase the outgoint volume 0 */ 71 | int u2diag; /*!< -1 */ 72 | int callingpres; /*!< calling presentation */ 73 | 74 | unsigned int usecallingpres:1; /*! -1 */ 75 | unsigned int autodeletesms:1; /*! 0 */ 76 | unsigned int resetdongle:1; /*! 1 */ 77 | unsigned int disablesms:1; /*! 0 */ 78 | unsigned int smsaspdu:1; /*! 0 */ 79 | dev_state_t initstate; /*! DEV_STATE_STARTED */ 80 | // unsigned int disable:1; /*! 0 */ 81 | 82 | call_waiting_t callwaiting; /*!< enable/disable/auto call waiting CALL_WAITING_AUTO */ 83 | dc_dtmf_setting_t dtmf; /*!< off/inband/relax incoming DTMF detection, default DC_DTMF_SETTING_RELAX */ 84 | 85 | int mindtmfgap; /*!< minimal time in ms from end of previews DTMF and begining of next */ 86 | #define DEFAULT_MINDTMFGAP 45 87 | 88 | int mindtmfduration; /*!< minimal DTMF duration in ms */ 89 | #define DEFAULT_MINDTMFDURATION 80 90 | 91 | int mindtmfinterval; /*!< minimal DTMF interval beetween ends in ms, applied only on same digit */ 92 | #define DEFAULT_MINDTMFINTERVAL 200 93 | } dc_sconfig_t; 94 | 95 | /* Global settings */ 96 | typedef struct dc_gconfig 97 | { 98 | struct ast_jb_conf jbconf; /*!< jitter buffer settings, disabled by default */ 99 | int discovery_interval; /*!< The device discovery interval */ 100 | #define DEFAULT_DISCOVERY_INT 60 101 | } dc_gconfig_t; 102 | 103 | /* Local required (unique) settings */ 104 | typedef struct dc_uconfig 105 | { 106 | /* unique settings */ 107 | char id[DEVNAMELEN]; /*!< id from dongle.conf */ 108 | char audio_tty[DEVPATHLEN]; /*!< tty for audio connection */ 109 | char data_tty[DEVPATHLEN]; /*!< tty for AT commands */ 110 | char imei[IMEI_SIZE+1]; /*!< search device by imei */ 111 | char imsi[IMSI_SIZE+1]; /*!< search device by imsi */ 112 | } dc_uconfig_t; 113 | 114 | /* all Config settings join in one place */ 115 | typedef struct pvt_config 116 | { 117 | dc_uconfig_t unique; /*!< unique settings */ 118 | dc_sconfig_t shared; /*!< possible inherited settings */ 119 | } pvt_config_t; 120 | #define SCONFIG(cfg,name) ((cfg)->shared.name) 121 | #define UCONFIG(cfg,name) ((cfg)->unique.name) 122 | 123 | EXPORT_DECL int dc_dtmf_str2setting(const char * str); 124 | EXPORT_DECL const char * dc_dtmf_setting2str(dc_dtmf_setting_t dtmf); 125 | EXPORT_DECL void dc_sconfig_fill_defaults(struct dc_sconfig * config); 126 | EXPORT_DECL void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struct dc_sconfig * config); 127 | EXPORT_DECL void dc_gconfig_fill(struct ast_config * cfg, const char * cat, struct dc_gconfig * config); 128 | EXPORT_DECL int dc_config_fill(struct ast_config * cfg, const char * cat, const struct dc_sconfig * parent, struct pvt_config * config); 129 | 130 | 131 | #endif /* CHAN_DONGLE_DC_CONFIG_H_INCLUDED */ 132 | -------------------------------------------------------------------------------- /etc/dongle.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | interval=15 ; Number of seconds between trying to connect to devices 4 | 5 | ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- 6 | ;jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a 7 | ; Dongle channel. Defaults to "no". An enabled jitterbuffer will 8 | ; be used only if the sending side can create and the receiving 9 | ; side can not accept jitter. The Dongle channel can't accept jitter, 10 | ; thus an enabled jitterbuffer on the receive Dongle side will always 11 | ; be used if the sending side can create jitter. 12 | 13 | ;jbforce = no ; Forces the use of a jitterbuffer on the receive side of a Dongle 14 | ; channel. Defaults to "no". 15 | 16 | ;jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. 17 | 18 | ;jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is 19 | ; resynchronized. Useful to improve the quality of the voice, with 20 | ; big jumps in/broken timestamps, usually sent from exotic devices 21 | ; and programs. Defaults to 1000. 22 | 23 | ;jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a Dongle 24 | ; channel. Two implementations are currently available - "fixed" 25 | ; (with size always equals to jbmaxsize) and "adaptive" (with 26 | ; variable size, actually the new jb of IAX2). Defaults to fixed. 27 | 28 | ;jbtargetextra = 40 ; This option only affects the jb when 'jbimpl = adaptive' is set. 29 | ; The option represents the number of milliseconds by which the new jitter buffer 30 | ; will pad its size. the default is 40, so without modification, the new 31 | ; jitter buffer will set its size to the jitter value plus 40 milliseconds. 32 | ; increasing this value may help if your network normally has low jitter, 33 | ; but occasionally has spikes. 34 | 35 | ;jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". 36 | ;----------------------------------------------------------------------------------- 37 | 38 | [defaults] 39 | ; now you can set here any not required device settings as template 40 | ; sure you can overwrite in any [device] section this default values 41 | 42 | context=default ; context for incoming calls 43 | group=0 ; calling group 44 | rxgain=0 ; increase the incoming volume; may be negative 45 | txgain=0 ; increase the outgoint volume; may be negative 46 | autodeletesms=yes ; auto delete incoming sms 47 | resetdongle=yes ; reset dongle during initialization with ATZ command 48 | u2diag=-1 ; set ^U2DIAG parameter on device (0 = disable everything except modem function) ; -1 not use ^U2DIAG command 49 | usecallingpres=yes ; use the caller ID presentation or not 50 | callingpres=allowed_passed_screen ; set caller ID presentation by default use default network settings 51 | disablesms=no ; disable of SMS reading from device when received 52 | ; chan_dongle has currently a bug with SMS reception. When a SMS gets in during a 53 | ; call chan_dongle might crash. Enable this option to disable sms reception. 54 | ; default = no 55 | 56 | language=en ; set channel default language 57 | smsaspdu=yes ; if 'yes' send SMS in PDU mode, feature implementation incomplete and we strongly recommend say 'yes' 58 | mindtmfgap=45 ; minimal interval from end of previews DTMF from begining of next in ms 59 | mindtmfduration=80 ; minimal DTMF tone duration in ms 60 | mindtmfinterval=200 ; minimal interval between ends of DTMF of same digits in ms 61 | 62 | callwaiting=auto ; if 'yes' allow incoming calls waiting; by default use network settings 63 | ; if 'no' waiting calls just ignored 64 | disable=no ; OBSOLETED by initstate: if 'yes' no load this device and just ignore this section 65 | 66 | initstate=start ; specified initial state of device, must be one of 'stop' 'start' 'remote' 67 | ; 'remove' same as 'disable=yes' 68 | 69 | exten=+1234567890 ; exten for start incoming calls, only in case of Subscriber Number not available!, also set to CALLERID(ndid) 70 | 71 | dtmf=relax ; control of incoming DTMF detection, possible values: 72 | ; off - off DTMF tones detection, voice data passed to asterisk unaltered 73 | ; use this value for gateways or if not use DTMF for AVR or inside dialplan 74 | ; inband - do DTMF tones detection 75 | ; relax - like inband but with relaxdtmf option 76 | ; default is 'relax' by compatibility reason 77 | 78 | ; dongle required settings 79 | [dongle0] 80 | audio=/dev/ttyUSB1 ; tty port for audio connection; no default value 81 | data=/dev/ttyUSB2 ; tty port for AT commands; no default value 82 | 83 | ; or you can omit both audio and data together and use imei=123456789012345 and/or imsi=123456789012345 84 | ; imei and imsi must contain exactly 15 digits ! 85 | ; imei/imsi discovery is available on Linux only 86 | imei=123456789012345 87 | imsi=123456789012345 88 | 89 | ; if audio and data set together with imei and/or imsi audio and data has precedence 90 | ; you can use both imei and imsi together in this case exact match by imei and imsi required 91 | 92 | -------------------------------------------------------------------------------- /etc/extensions.conf: -------------------------------------------------------------------------------- 1 | ; this is chunks of Asterisk extensions.conf file for show some chan_dongle features 2 | [general] 3 | 4 | [dongle-incoming] 5 | ; example of ussd receive 6 | exten => ussd,1,Set(type=${USSD_TYPE}) 7 | ; values from 0 till 5 8 | ; 0 - 'USSD Notify' 9 | ; 1 - 'USSD Request' 10 | ; 2 - 'USSD Terminated by network' 11 | ; 3 - 'Other local client has responded' 12 | ; 4 - 'Operation not supported' 13 | ; 5 - 'Network time out' 14 | 15 | exten => ussd,n,Set(typestr=${USSD_TYPE_STR}) 16 | ; type in string, see above 17 | 18 | exten => ussd,n,Set(ussd=${USSD}) 19 | ; USSD text, but may be truncated by first \n 20 | 21 | exten => ussd,n,Set(ussd_multiline=${BASE64_DECODE(${USSD_BASE64})}) 22 | ; USSD text, may be multiline 23 | ; Note: this exten run in Local channel not attached to anything, also all CALLERID() is empty 24 | 25 | exten => ussd,n,Hangup 26 | 27 | 28 | ; example of sms receive 29 | exten => sms,1,Set(sms=${SMS}) 30 | ; SMS text, but may be truncated by first \n 31 | 32 | exten => sms,n,Set(sms_multiline=${BASE64_DECODE(${SMS_BASE64})}) 33 | ; SMS text, may be multiline 34 | 35 | exten => sms,n,Set(raw_cmgr_message=${CMGR}) 36 | ; raw CMGR message from dongle 37 | 38 | ; Note: this exten run in Local channel not attached to anything, also CALLERID(num) is address of SMS originator 39 | 40 | exten => sms,n,Hangup 41 | 42 | 43 | ; example of begining context execution from not default exten 44 | exten => +12345678901,1,Verbose(This exten executed if Subscriber Number is available and equal +12345678901 or exten setting value is +12345678901) 45 | exten => +12345678901,n,Hangup 46 | 47 | 48 | ; example of channel variables setting by chan_dongle 49 | exten => s,1,Set(NAME_OF_DEVICE=${DONGLE0_STATUS}) 50 | ; for example 'dongle0' or 'dongle1' see dongle.conf 51 | 52 | exten => s,n,Set(NAME_OF_PROVIDE=${DONGLEPROVIDER}) 53 | ; for example see output of cli 'dongle show devices' column "Provider Name" 54 | 55 | exten => s,n,Set(IMEI_OF_DEVICE=${DONGLEIMEI}) 56 | ; for example see output of cli 'dongle show devices' column "IMEI" 57 | 58 | exten => s,n,Set(IMSI_OF_SIMCARD=${DONGLEIMSI}) 59 | ; for example see output of cli 'dongle show devices' column "IMSI" 60 | 61 | exten => s,n,Set(SUBSCRIBER_NUMBER=${DONGLENUMBER}) 62 | ; Subscriber Number example see output of cli 'dongle show devices' column "Number" 63 | ; may be empty, use for save in SIM commands AT+CPBS="ON" and AT+CPBW=1,"+123456789",145 64 | 65 | exten => s,n,Set(CNUM_NUMBER=${CALLERID(dnid)}) 66 | ; Set to Subscriber Number if available 67 | 68 | ; applications of chan_dongle 69 | 70 | exten => s,n,DongleStatus(dongle0,DONGLE0_STATUS) 71 | exten => s,n,DongleStatus(g1,DONGLE1_STATUS) 72 | exten => s,n,DongleStatus(r1,DONGLE2_STATUS) 73 | exten => s,n,DongleStatus(p:PROVIDER NAME,DONGLE3_STATUS) 74 | exten => s,n,DongleStatus(i:123456789012345,DONGLE4_STATUS) 75 | exten => s,n,DongleStatus(s:25099,DONGLE5_STATUS) 76 | ; for first argument see Dial() Resource part of dial string 77 | ; get status of device and store result in variable 78 | ; possible values of ${DONGLE0_STATUS} 79 | ; -1 invalid argument 80 | ; 1 device not found 81 | ; 2 device connected and free 82 | ; 3 device connected and in use 83 | 84 | exten => s,n,DongleSendSMS(dongle0,+18004005422,"Hello how are you, Danila?",1440,yes) 85 | ; send SMS on selected device and to specified number 86 | ; device name of Dongle device 87 | ; destination number in International format with leading '+' or w/o leading '+' 88 | ; message maximum 70 UCS-2 symbols 89 | ; validity period in minutes, will be round up to nearest possible value, optional default is 3 days 90 | ; report request if true report for this SMS is required, optional default is not 91 | 92 | 93 | ; functions of chan_dongle 94 | 95 | exten => s,n,GotoIf($["${CHANNEL(callstate)}" = "waiting"]?waiting-call) 96 | ; now we provide channel function argument callstate 97 | ; possible values 98 | ; active ; enjoy and speek 99 | ; held ; this call is held 100 | ; dialing ; for outgoing calls 101 | ; alerting ; for outgoing calls, number dialed and called party ringing 102 | ; incoming ; for incoming calls 103 | ; waiting ; for incoming waiting calls; 104 | ; if callwaiting=no channels for waiting calls never created 105 | 106 | ; initialize ; never appear 107 | ; released ; never appear 108 | 109 | ; Answer on waiting call activate this call and place any other active calls 110 | ; on hold, but execution of dialplan for these calls not break stopped or frozen 111 | ; When active call terminated one of held becomes active. 112 | 113 | exten => s,n,Set(CHANNEL(callstate)=active) 114 | ; if callstate is 'held' you can assign new value 'active' 115 | ; its mean activate this call and place on hold all other active calls but 116 | ; execution of dialplan for these calls not break stopped or frozen 117 | 118 | 119 | exten => s,n,GotoIf($["${CHANNEL(dtmf)}" != "off"]?turn_off_dtmf) 120 | ; you can read actual value of DTMF detection settings 121 | ; possible values 122 | ; off ; DTMF detection is off 123 | ; inband ; detect DTMF inband 124 | ; relax ; detect DTMF inband with relax option as defined in main asterisk docs 125 | 126 | 127 | exten => s,n,Set(CHANNEL(dtmf)=off) 128 | ; example usage of assign to channel local DTMF settings 129 | ; this not overwrite global config file settings and apply only for this channel 130 | 131 | exten => s,n,Dial(Dongle/dongle0/+79139131234) 132 | exten => s,n,Dial(Dongle/g1/+79139131234) 133 | exten => s,n,Dial(Dongle/r1/879139131234) 134 | exten => s,n,Dial(Dongle/p:PROVIDER NAME/+79139131234) 135 | exten => s,n,Dial(Dongle/i:123456789012345/+79139131234) 136 | exten => s,n,Dial(Dongle/s:25099/+79139131234) 137 | ; make outgoing call 138 | ; name on device with this name 139 | ; g1 on first free device in group 1 140 | ; r1 round robin devices in group 1 141 | ; p: with first free device with Operator name beggining with name 142 | ; i: with device exactly matched IMEI 143 | ; s: with first free device with IMSI prefix 144 | 145 | exten => s,n,Dial(Dongle/g1/holdother:+79139131234) 146 | exten => s,n,Dial(Dongle/r1/holdother:+79139131234) 147 | exten => s,n,Dial(Dongle/p:PROVIDER NAME/holdother:+79139131234) 148 | exten => s,n,Dial(Dongle/i:123456789012345/holdother:+79139131234) 149 | exten => s,n,Dial(Dongle/s:25099/holdother:+79139131234) 150 | ; now we add option 'holdother' for dialing 151 | ; this option do 152 | ; 1) When looking for available devices by group, provider IMEI, 153 | ; IMSI prefix not ignore devices with whose state does not 154 | ; prohibit a new outgoing call when other call place on hold 155 | ; 156 | ; 2) Before actual dialing place active calls on hold 157 | ; but execution of dialplan for these calls not break stopped or frozen 158 | ; 3) This call will be active if succesfully answered 159 | ; BUG !!! 160 | ; tested for call waiting and hold features Huawei E1550 has a next bug: 161 | ; When local side hangup any call including held call ALL other calls LOST sound 162 | ; When remove side hangup active call ALL other calls LOST sound 163 | ; Please double check true this for you or not 164 | ; If true reduce usage of this useful features 165 | 166 | exten => s,n,Dial(Dongle/g1/conference:+79139131234) 167 | exten => s,n,Dial(Dongle/r1/conference:+79139131234) 168 | exten => s,n,Dial(Dongle/p:PROVIDER NAME/conference:+79139131234) 169 | exten => s,n,Dial(Dongle/i:123456789012345/conference:+79139131234) 170 | exten => s,n,Dial(Dongle/s:25099/conference:+79139131234) 171 | ; and also option 'conference' added 172 | ; this option do 173 | ; 1) When looking for available devices by group, provider IMEI, 174 | ; IMSI prefix not ignore devices with whose state does not 175 | ; prohibit a new outgoing call when other call place on hold 176 | ; 177 | ; 2) Before actual dialing place active calls on hold 178 | ; but execution of dialplan for these calls not break stopped or frozen 179 | ; 3) If answered attach hold calls to conrefence (in term of GSM) 180 | ; Also if created outgoing channel place call on same device that incoming channel 181 | ; both incoming and outgoing channels become readonly to avoid the voice loop. 182 | ; 183 | ; see also BUG !!! note above 184 | 185 | exten => s,n,Hangup 186 | 187 | -------------------------------------------------------------------------------- /export.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_EXPORT_H_INCLUDED 5 | #define CHAN_DONGLE_EXPORT_H_INCLUDED 6 | 7 | #ifdef BUILD_SINGLE 8 | 9 | #define EXPORT_DEF static 10 | #define EXPORT_DECL static 11 | #define INLINE_DECL static inline 12 | #else /* BUILD_SINGLE */ 13 | 14 | #define EXPORT_DEF 15 | #define EXPORT_DECL extern 16 | #define INLINE_DECL static inline 17 | 18 | #endif /* BUILD_SINGLE */ 19 | #endif /* CHAN_DONGLE_EXPORT_H_INCLUDED */ 20 | -------------------------------------------------------------------------------- /helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | 9 | bg 10 | */ 11 | #ifdef HAVE_CONFIG_H 12 | #include 13 | #endif /* HAVE_CONFIG_H */ 14 | 15 | #include /* SIGURG */ 16 | 17 | #include 18 | #include /* AST_PRES_* */ 19 | 20 | #include "helpers.h" 21 | #include "chan_dongle.h" /* devices */ 22 | #include "at_command.h" 23 | #include "pdu.h" /* pdu_digit2code() */ 24 | 25 | static int is_valid_ussd_string(const char* number) 26 | { 27 | for(; *number; number++) 28 | if(pdu_digit2code(*number) == 0) 29 | return 0; 30 | 31 | return 1; 32 | } 33 | 34 | #/* */ 35 | EXPORT_DEF int is_valid_phone_number(const char* number) 36 | { 37 | if(number[0] == '+') 38 | number++; 39 | return is_valid_ussd_string(number); 40 | } 41 | 42 | 43 | #/* */ 44 | EXPORT_DEF int get_at_clir_value (struct pvt* pvt, int clir) 45 | { 46 | int res = 0; 47 | 48 | switch (clir) 49 | { 50 | case AST_PRES_ALLOWED_NETWORK_NUMBER: 51 | case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: 52 | case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: 53 | case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: 54 | case AST_PRES_NUMBER_NOT_AVAILABLE: 55 | ast_debug (2, "[%s] callingpres: %s\n", PVT_ID(pvt), ast_describe_caller_presentation (clir)); 56 | res = 2; 57 | break; 58 | 59 | case AST_PRES_PROHIB_NETWORK_NUMBER: 60 | case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: 61 | case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: 62 | case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: 63 | ast_debug (2, "[%s] callingpres: %s\n", PVT_ID(pvt), ast_describe_caller_presentation (clir)); 64 | res = 1; 65 | break; 66 | 67 | default: 68 | ast_log (LOG_WARNING, "[%s] Unsupported callingpres: %d\n", PVT_ID(pvt), clir); 69 | if ((clir & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) 70 | { 71 | res = 0; 72 | } 73 | else 74 | { 75 | res = 2; 76 | } 77 | break; 78 | } 79 | 80 | return res; 81 | } 82 | 83 | typedef int (*at_cmd_f)(struct cpvt*, const char*, const char*, unsigned, int, void **); 84 | 85 | #/* */ 86 | static const char* send2(const char* dev_name, int * status, int online, const char* emsg, const char* okmsg, at_cmd_f func, const char* arg1, const char * arg2, unsigned arg3, int arg4, void ** arg5) 87 | { 88 | struct pvt* pvt; 89 | const char* msg; 90 | 91 | if(status) 92 | *status = 0; 93 | pvt = find_device_ext(dev_name, &msg); 94 | if(pvt) 95 | { 96 | if(pvt->connected && (!online || (pvt->initialized && pvt->gsm_registered))) 97 | { 98 | if((*func) (&pvt->sys_chan, arg1, arg2, arg3, arg4, arg5)) 99 | { 100 | msg = emsg; 101 | ast_log (LOG_ERROR, "[%s] %s\n", PVT_ID(pvt), emsg); 102 | } 103 | else 104 | { 105 | msg = okmsg; 106 | if(status) 107 | *status = 1; 108 | } 109 | } 110 | else 111 | msg = "Device not connected / initialized / registered"; 112 | ast_mutex_unlock (&pvt->lock); 113 | } 114 | return msg; 115 | } 116 | 117 | #/* */ 118 | EXPORT_DEF const char* send_ussd(const char* dev_name, const char* ussd, int * status, void ** id) 119 | { 120 | if(is_valid_ussd_string(ussd)) 121 | return send2(dev_name, status, 1, "Error adding USSD command to queue", "USSD queued for send", (at_cmd_f)at_enque_ussd, ussd, 0, 0, 0, id); 122 | if(status) 123 | *status = 0; 124 | return "Invalid USSD"; 125 | } 126 | 127 | #/* */ 128 | EXPORT_DEF const char * send_sms(const char * dev_name, const char * number, const char * message, const char * validity, const char * report, int * status, void ** id) 129 | { 130 | if(is_valid_phone_number(number)) 131 | { 132 | int val = 0; 133 | int srr = 0; 134 | 135 | if(validity) 136 | { 137 | val = strtol (validity, NULL, 10); 138 | if(val <= 0) 139 | val = 0; 140 | } 141 | 142 | if(report) 143 | srr = ast_true (report); 144 | 145 | return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enque_sms, number, message, val, srr, id); 146 | } 147 | if(status) 148 | *status = 0; 149 | return "Invalid destination number"; 150 | } 151 | 152 | #/* */ 153 | EXPORT_DEF const char * send_pdu(const char * dev_name, const char * pdu, int * status, void ** id) 154 | { 155 | return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enque_pdu, pdu, NULL, 0, 0, id); 156 | } 157 | 158 | #/* */ 159 | EXPORT_DEF const char* send_reset(const char* dev_name, int * status) 160 | { 161 | return send2(dev_name, status, 0, "Error adding reset command to queue", "Reset command queued for execute", (at_cmd_f)at_enque_reset, 0, 0, 0, 0, NULL); 162 | } 163 | 164 | #/* */ 165 | EXPORT_DEF const char* send_ccwa_set(const char* dev_name, call_waiting_t enable, int * status) 166 | { 167 | return send2(dev_name, status, 1, "Error adding CCWA commands to queue", "Call-Waiting commands queued for execute", (at_cmd_f)at_enque_set_ccwa, 0, 0, enable, 0, NULL); 168 | } 169 | 170 | #/* */ 171 | EXPORT_DEF const char* send_at_command(const char* dev_name, const char* command) 172 | { 173 | return send2(dev_name, NULL, 0, "Error adding command", "Command queued for execute", (at_cmd_f)at_enque_user_cmd, command, NULL, 0, 0, NULL); 174 | } 175 | 176 | EXPORT_DEF const char* schedule_restart_event(dev_state_t event, restate_time_t when, const char* dev_name, int * status) 177 | { 178 | const char * msg; 179 | struct pvt * pvt = find_device(dev_name); 180 | 181 | if (pvt) 182 | { 183 | pvt->desired_state = event; 184 | pvt->restart_time = when; 185 | 186 | pvt_try_restate(pvt); 187 | ast_mutex_unlock (&pvt->lock); 188 | 189 | msg = dev_state2str_msg(event); 190 | 191 | if(status) 192 | *status = 1; 193 | } 194 | else 195 | { 196 | msg = "Device not found"; 197 | if(status) 198 | *status = 0; 199 | } 200 | 201 | return msg; 202 | } 203 | -------------------------------------------------------------------------------- /helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010,2011 bg 3 | */ 4 | #ifndef CHAN_DONGLE_HELPERS_H_INCLUDED 5 | #define CHAN_DONGLE_HELPERS_H_INCLUDED 6 | 7 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 8 | #include "dc_config.h" /* call_waiting_t */ 9 | #include "chan_dongle.h" /* restate_time_t */ 10 | 11 | EXPORT_DECL int get_at_clir_value (struct pvt* pvt, int clir); 12 | 13 | /* return status string of sending, status arg is optional */ 14 | EXPORT_DECL const char * send_ussd(const char * dev_name, const char* ussd, int * status, void ** id); 15 | EXPORT_DECL const char * send_sms(const char * dev_name, const char* number, const char* message, const char * validity, const char * report, int * status, void ** id); 16 | EXPORT_DECL const char * send_pdu(const char * dev_name, const char * pdu, int * status, void ** id); 17 | EXPORT_DECL const char * send_reset(const char * dev_name, int * status); 18 | EXPORT_DECL const char * send_ccwa_set(const char * dev_name, call_waiting_t enable, int * status); 19 | EXPORT_DECL const char * send_at_command(const char * dev_name, const char* command); 20 | EXPORT_DECL const char * schedule_restart_event(dev_state_t event, restate_time_t when, const char * dev_name, int * status); 21 | EXPORT_DECL int is_valid_phone_number(const char * number); 22 | 23 | #endif /* CHAN_DONGLE_HELPERS_H_INCLUDED */ 24 | -------------------------------------------------------------------------------- /manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_MANAGER_H_INCLUDED 5 | #define CHAN_DONGLE_MANAGER_H_INCLUDED 6 | 7 | #ifdef BUILD_MANAGER 8 | 9 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 10 | 11 | EXPORT_DECL void manager_register(); 12 | EXPORT_DECL void manager_unregister(); 13 | 14 | EXPORT_DECL void manager_event_message(const char * event, const char * devname, const char * message); 15 | EXPORT_DECL void manager_event_message_raw(const char * event, const char * devname, const char * message); 16 | 17 | EXPORT_DECL void manager_event_new_ussd(const char * devname, char * message); 18 | EXPORT_DECL void manager_event_new_sms(const char * devname, char * number, char * message); 19 | EXPORT_DECL void manager_event_new_sms_base64 (const char * devname, char * number, char * message_base64); 20 | EXPORT_DECL void manager_event_cend(const char * devname, int call_index, int duration, int end_status, int cc_cause); 21 | EXPORT_DECL void manager_event_call_state_change(const char * devname, int call_index, const char * newstate); 22 | EXPORT_DECL void manager_event_device_status(const char * devname, const char * newstatus); 23 | EXPORT_DECL void manager_event_sent_notify(const char * devname, const char * type, const void * id, const char * result); 24 | 25 | #else /* BUILD_MANAGER */ 26 | 27 | #define manager_register() 28 | #define manager_unregister() 29 | 30 | #define manager_event_message(event, devname, message) 31 | #define manager_event_message_raw(event, devname, message) 32 | #define manager_event_new_ussd(devname, message) 33 | #define manager_event_new_sms(devname, number, message) 34 | #define manager_event_new_sms_base64(devname, number, message_base64) 35 | #define manager_event_cend(devname, call_index, duration, end_status, cc_cause) 36 | #define manager_event_call_state_change(devname, call_index, newstate) 37 | #define manager_event_device_status(devname, newstatus) 38 | #define manager_event_sent_notify(devname, type, id, result) 39 | 40 | #endif /* BUILD_MANAGER */ 41 | 42 | #endif /* CHAN_DONGLE_MANAGER_H_INCLUDED */ 43 | -------------------------------------------------------------------------------- /memmem.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include 3 | #endif /* HAVE_CONFIG_H */ 4 | 5 | #ifndef HAVE_MEMMEM 6 | /*- 7 | * Copyright (c) 2005 Pascal Gloor 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 3. The name of the author may not be used to endorse or promote 18 | * products derived from this software without specific prior written 19 | * permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | #include "memmem.h" 35 | #include /* memchr() memcmp() NULL */ 36 | 37 | /* 38 | * Find the first occurrence of the byte string s in byte string l. 39 | */ 40 | 41 | EXPORT_DEF void * 42 | memmem(const void *l, size_t l_len, const void *s, size_t s_len) 43 | { 44 | register char *cur, *last; 45 | const char *cl = (const char *)l; 46 | const char *cs = (const char *)s; 47 | 48 | /* we need something to compare */ 49 | if (l_len == 0 || s_len == 0) 50 | return NULL; 51 | 52 | /* "s" must be smaller or equal to "l" */ 53 | if (l_len < s_len) 54 | return NULL; 55 | 56 | /* special case where s_len == 1 */ 57 | if (s_len == 1) 58 | return memchr(l, (int)*cs, l_len); 59 | 60 | /* the last position where its possible to find "s" in "l" */ 61 | last = (char *)cl + l_len - s_len; 62 | 63 | for (cur = (char *)cl; cur <= last; cur++) 64 | if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) 65 | return cur; 66 | 67 | return NULL; 68 | } 69 | 70 | #endif /* HAVE_MEMMEM */ 71 | -------------------------------------------------------------------------------- /memmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_MEMMEM_H_INCLUDED 5 | #define CHAN_DONGLE_MEMMEM_H_INCLUDED 6 | 7 | #ifdef HAVE_CONFIG_H 8 | #include 9 | #endif /* HAVE_CONFIG_H */ 10 | 11 | #ifdef HAVE_MEMMEM 12 | 13 | #ifndef _GNU_SOURCE 14 | #define _GNU_SOURCE 15 | #endif /* _GNU_SOURCE */ 16 | #include 17 | 18 | #else /* HAVE_MEMMEM */ 19 | 20 | #include /* size_t */ 21 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 22 | 23 | EXPORT_DECL void * memmem(const void *l, size_t l_len, const void *s, size_t s_len); 24 | 25 | #endif /* HAVE_MEMMEM */ 26 | #endif /* CHAN_DONGLE_MANAGER_H_INCLUDED */ 27 | -------------------------------------------------------------------------------- /mixbuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif /* HAVE_CONFIG_H */ 7 | 8 | #include 9 | #include /* ast_slinear_saturated_add() */ 10 | 11 | #include "mixbuffer.h" 12 | 13 | #/* */ 14 | EXPORT_DEF void mixb_attach(struct mixbuffer * mb, struct mixstream * stream) 15 | { 16 | stream->entry.next = NULL; 17 | stream->used = 0; 18 | stream->write = mb->rb.read; 19 | AST_LIST_INSERT_TAIL(&mb->streams, stream, entry); 20 | mb->attached++; 21 | } 22 | 23 | #/* */ 24 | EXPORT_DEF void mixb_detach(struct mixbuffer * mb, struct mixstream * stream) 25 | { 26 | mb->attached--; 27 | AST_LIST_REMOVE(&mb->streams, stream, entry); 28 | } 29 | 30 | #/* TODO: move up */ 31 | static void * saturated_sum(void* s1, const void *s2, size_t n) 32 | { 33 | short* s11 = s1; 34 | short* s22 = (short*)s2; 35 | 36 | /* FIXME: odd bytes */ 37 | for(n /= 2; n; n--, s11++, s22++) 38 | ast_slinear_saturated_add(s11, s22); 39 | 40 | return s1; 41 | } 42 | 43 | #/* function not update rb */ 44 | static inline size_t mixb_mix_write(struct mixbuffer * mb, struct mixstream * stream, const char * data, size_t len) 45 | { 46 | size_t rv; 47 | /* save global state */ 48 | size_t save_write = mb->rb.write; 49 | size_t save_used = mb->rb.used; 50 | 51 | /* load local state */ 52 | mb->rb.write = stream->write; 53 | mb->rb.used = stream->used; 54 | 55 | rv = rb_write_core(&mb->rb, data, len, saturated_sum); 56 | 57 | /* update local state */ 58 | stream->write = mb->rb.write; 59 | stream->used = mb->rb.used; 60 | 61 | /* restore global state */ 62 | mb->rb.used = save_used; 63 | mb->rb.write = save_write; 64 | 65 | return rv; 66 | } 67 | 68 | #/* */ 69 | EXPORT_DEF size_t mixb_write(struct mixbuffer * mb, struct mixstream * stream, const char * data, size_t len) 70 | { 71 | /* local state: how many data you fit? */ 72 | size_t max_mix = mixb_free(mb, stream); 73 | 74 | if(max_mix < len) 75 | len = max_mix; 76 | 77 | if(len > 0) 78 | { 79 | max_mix = mb->rb.used - stream->used; 80 | if(len > max_mix) 81 | { 82 | /* optitional Mix followed by copy */ 83 | if(max_mix) 84 | mixb_mix_write(mb, stream, data, max_mix); 85 | rb_write(&mb->rb, data + max_mix, len - max_mix); 86 | 87 | /* save local state */ 88 | stream->write = mb->rb.write; 89 | stream->used = mb->rb.used; 90 | } 91 | else 92 | { 93 | /* Mix only */ 94 | mixb_mix_write(mb, stream, data, len); 95 | } 96 | } 97 | 98 | return len; 99 | } 100 | 101 | #/* */ 102 | EXPORT_DEF size_t mixb_read_upd(struct mixbuffer * mb, size_t len) 103 | { 104 | struct mixstream * stream; 105 | 106 | // NOTE: change used, read and also can change write 107 | size_t rv = rb_read_upd(&mb->rb, len); 108 | 109 | // all streams has one read but differ used 110 | AST_LIST_TRAVERSE(&mb->streams, stream, entry) { 111 | if(stream->used > len) 112 | stream->used -= len; 113 | else 114 | stream->used = 0; 115 | stream->write = mb->rb.read + stream->used; 116 | if(stream->write >= mb->rb.size) 117 | stream->write -= mb->rb.size; 118 | } 119 | 120 | return rv; 121 | } 122 | -------------------------------------------------------------------------------- /mixbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | 5 | #ifndef CHAN_DONGLE_MIXBUFFER_H_INCLUDED 6 | #define CHAN_DONGLE_MIXBUFFER_H_INCLUDED 7 | 8 | #include 9 | #include /* AST_LIST_ENTRY() AST_LIST_HEAD_NOLOCK() */ 10 | 11 | #include "ringbuffer.h" 12 | 13 | struct mixstream { 14 | AST_LIST_ENTRY(mixstream) entry; 15 | size_t used; /*!< number of bytes used */ 16 | size_t write; /*!< write position */ 17 | }; 18 | 19 | struct mixbuffer { 20 | AST_LIST_HEAD_NOLOCK(,mixstream) streams; /*!< list of stream descriptions */ 21 | struct ringbuffer rb; /*!< base */ 22 | unsigned attached; /*!< number of attached streams */ 23 | }; 24 | 25 | /* initialize mixbuffer */ 26 | INLINE_DECL void mixb_init(struct mixbuffer * mb, void * buf, size_t len) 27 | { 28 | AST_LIST_HEAD_INIT_NOLOCK(&mb->streams); 29 | rb_init(&mb->rb, buf, len); 30 | mb->attached = 0; 31 | } 32 | 33 | /* attach stream to mix buffer */ 34 | EXPORT_DECL void mixb_attach(struct mixbuffer * mb, struct mixstream * stream); 35 | 36 | /* detach stream from mix buffer */ 37 | EXPORT_DECL void mixb_detach(struct mixbuffer * mb, struct mixstream * stream); 38 | 39 | /* get amount of free bytes in buffer for specified stream */ 40 | INLINE_DECL size_t mixb_free (const struct mixbuffer * mb, const struct mixstream * stream) 41 | { 42 | return mb->rb.size - stream->used; 43 | } 44 | 45 | /* get bytes used i.e. now may bytes can read */ 46 | INLINE_DECL size_t mixb_used(const struct mixbuffer * mb) 47 | { 48 | return rb_used(&mb->rb); 49 | } 50 | 51 | /* advice read position */ 52 | EXPORT_DECL size_t mixb_read_upd(struct mixbuffer * mb, size_t len); 53 | 54 | /* add data to mix buffer for specified stream */ 55 | EXPORT_DECL size_t mixb_write(struct mixbuffer * mb, struct mixstream * stream, const char * data, size_t len); 56 | 57 | /* get data pointer and sizes in iov for all available for reading data in buffer */ 58 | INLINE_DECL int mixb_read_all_iov (const struct mixbuffer * mb, struct iovec iov[2]) 59 | { 60 | return rb_read_all_iov(&mb->rb, iov); 61 | } 62 | 63 | /* get data pointer and sizes in iov only for first len bytes */ 64 | INLINE_DECL int mixb_read_n_iov (const struct mixbuffer * mb, struct iovec iov[2], size_t len) 65 | { 66 | return rb_read_n_iov(&mb->rb, iov, len); 67 | } 68 | 69 | /* get number of attached streams */ 70 | INLINE_DECL int mixb_streams (const struct mixbuffer * mb) 71 | { 72 | return mb->attached; 73 | } 74 | 75 | #endif /* CHAN_DONGLE_MIXBUFFER_H_INCLUDED */ 76 | -------------------------------------------------------------------------------- /mutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010,2011 bg 3 | */ 4 | #ifndef CHAN_DONGLE_MUTILS_H_INCLUDED 5 | #define CHAN_DONGLE_MUTILS_H_INCLUDED 6 | 7 | #include "export.h" 8 | 9 | #define ITEMS_OF(x) (sizeof(x)/sizeof((x)[0])) 10 | #define STRLEN(string) (sizeof(string)-1) 11 | 12 | #ifndef MIN 13 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 14 | #endif 15 | 16 | INLINE_DECL const char * enum2str_def(unsigned value, const char * const names[], unsigned items, const char * def) 17 | { 18 | const char * name; 19 | if(value < items) 20 | name = names[value]; 21 | else 22 | name = def; 23 | return name; 24 | } 25 | 26 | INLINE_DECL const char * enum2str(unsigned value, const char * const names[], unsigned items) 27 | { 28 | return enum2str_def(value, names, items, "unknown"); 29 | } 30 | 31 | INLINE_DECL int str2enum(const char * value, const char * const options[], unsigned items) 32 | { 33 | unsigned index; 34 | for(index = 0; index < items; index++) 35 | { 36 | if(strcasecmp(value, options[index]) == 0) 37 | return index; 38 | } 39 | 40 | return -1; 41 | } 42 | 43 | #endif /* CHAN_DONGLE_MUTILS_H_INCLUDED */ 44 | -------------------------------------------------------------------------------- /pdiscovery.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 bg 3 | */ 4 | #ifndef CHAN_DONGLE_PDISCOVERY_H_INCLUDED 5 | #define CHAN_DONGLE_PDISCOVERY_H_INCLUDED 6 | 7 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 8 | 9 | enum INTERFACE_TYPE { 10 | INTERFACE_TYPE_DATA = 0, 11 | INTERFACE_TYPE_VOICE, 12 | // INTERFACE_TYPE_COM, 13 | INTERFACE_TYPE_NUMBERS, 14 | }; 15 | 16 | struct pdiscovery_ports { 17 | char * ports[INTERFACE_TYPE_NUMBERS]; 18 | }; 19 | 20 | struct pdiscovery_result { 21 | char * imei; 22 | char * imsi; 23 | struct pdiscovery_ports ports; 24 | }; 25 | 26 | struct pdiscovery_cache_item; 27 | 28 | EXPORT_DECL void pdiscovery_init(); 29 | EXPORT_DECL void pdiscovery_fini(); 30 | /* return non-zero if found */ 31 | EXPORT_DECL int pdiscovery_lookup(const char * device, const char * imei, const char * imsi, char ** dport, char ** aport); 32 | EXPORT_DECL const struct pdiscovery_result * pdiscovery_list_begin(const struct pdiscovery_cache_item ** opaque); 33 | EXPORT_DECL const struct pdiscovery_result * pdiscovery_list_next(const struct pdiscovery_cache_item ** opaque); 34 | EXPORT_DECL void pdiscovery_list_end(); 35 | 36 | #endif /* CHAN_DONGLE_PDISCOVERY_H_INCLUDED */ 37 | -------------------------------------------------------------------------------- /pdu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #ifndef CHAN_DONGLE_PDU_H_INCLUDED 5 | #define CHAN_DONGLE_PDU_H_INCLUDED 6 | 7 | #include /* size_t */ 8 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 9 | #include "char_conv.h" /* str_encoding_t */ 10 | 11 | EXPORT_DECL char pdu_digit2code(char digit); 12 | EXPORT_DECL int pdu_build(char * buffer, size_t length, const char * csca, const char * dst, const char * msg, unsigned valid_minutes, int srr); 13 | EXPORT_DECL const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc); 14 | EXPORT_DECL int pdu_parse_sca(char ** pdu, size_t * length); 15 | 16 | #endif /* CHAN_DONGLE_PDU_H_INCLUDED */ 17 | -------------------------------------------------------------------------------- /ringbuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | */ 9 | #ifdef HAVE_CONFIG_H 10 | #include 11 | #endif /* HAVE_CONFIG_H */ 12 | 13 | #include "memmem.h" 14 | #include /* memchr() */ 15 | 16 | #include "ringbuffer.h" 17 | 18 | EXPORT_DEF int rb_memcmp (const struct ringbuffer* rb, const char* mem, size_t len) 19 | { 20 | size_t tmp; 21 | 22 | if (rb->used > 0 && len > 0 && rb->used >= len) 23 | { 24 | if ((rb->read + len) > rb->size) 25 | { 26 | tmp = rb->size - rb->read; 27 | if (memcmp (rb->buffer + rb->read, mem, tmp) == 0) 28 | { 29 | len -= tmp; 30 | mem += tmp; 31 | 32 | if (memcmp (rb->buffer, mem, len) == 0) 33 | { 34 | return 0; 35 | } 36 | } 37 | } 38 | else 39 | { 40 | if (memcmp (rb->buffer + rb->read, mem, len) == 0) 41 | { 42 | return 0; 43 | } 44 | } 45 | 46 | return 1; 47 | } 48 | 49 | return -1; 50 | } 51 | 52 | /* ============================ READ ============================= */ 53 | EXPORT_DEF int rb_read_all_iov (const struct ringbuffer* rb, struct iovec iov[2]) 54 | { 55 | if (rb->used > 0) 56 | { 57 | if ((rb->read + rb->used) > rb->size) 58 | { 59 | iov[0].iov_base = rb->buffer + rb->read; 60 | iov[0].iov_len = rb->size - rb->read; 61 | iov[1].iov_base = rb->buffer; 62 | iov[1].iov_len = rb->used - iov[0].iov_len; 63 | return 2; 64 | } 65 | else 66 | { 67 | iov[0].iov_base = rb->buffer + rb->read; 68 | iov[0].iov_len = rb->used; 69 | iov[1].iov_len = 0; 70 | return 1; 71 | } 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | EXPORT_DEF int rb_read_n_iov (const struct ringbuffer* rb, struct iovec iov[2], size_t len) 78 | { 79 | if (rb->used < len) 80 | { 81 | return 0; 82 | } 83 | 84 | if (len > 0) 85 | { 86 | if ((rb->read + len) > rb->size) 87 | { 88 | iov[0].iov_base = rb->buffer + rb->read; 89 | iov[0].iov_len = rb->size - rb->read; 90 | iov[1].iov_base = rb->buffer; 91 | iov[1].iov_len = len - iov[0].iov_len; 92 | return 2; 93 | } 94 | else 95 | { 96 | iov[0].iov_base = rb->buffer + rb->read; 97 | iov[0].iov_len = len; 98 | iov[1].iov_len = 0; 99 | return 1; 100 | } 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | EXPORT_DEF int rb_read_until_char_iov (const struct ringbuffer* rb, struct iovec iov[2], char c) 107 | { 108 | void* p; 109 | 110 | if (rb->used > 0) 111 | { 112 | if ((rb->read + rb->used) > rb->size) 113 | { 114 | iov[0].iov_base = rb->buffer + rb->read; 115 | iov[0].iov_len = rb->size - rb->read; 116 | if ((p = memchr (iov[0].iov_base, c, iov[0].iov_len)) != NULL) 117 | { 118 | iov[0].iov_len = p - iov[0].iov_base; 119 | iov[1].iov_len = 0; 120 | return 1; 121 | } 122 | 123 | if ((p = memchr (rb->buffer, c, rb->used - iov[0].iov_len)) != NULL) 124 | { 125 | iov[1].iov_base = rb->buffer; 126 | iov[1].iov_len = p - rb->buffer; 127 | return 2; 128 | } 129 | } 130 | else 131 | { 132 | iov[0].iov_base = rb->buffer + rb->read; 133 | iov[0].iov_len = rb->used; 134 | if ((p = memchr (iov[0].iov_base, c, iov[0].iov_len)) != NULL) 135 | { 136 | iov[0].iov_len = p - iov[0].iov_base; 137 | iov[1].iov_len = 0; 138 | return 1; 139 | } 140 | } 141 | } 142 | 143 | return 0; 144 | } 145 | 146 | EXPORT_DEF int rb_read_until_mem_iov (const struct ringbuffer* rb, struct iovec iov[2], const void* mem, size_t len) 147 | { 148 | size_t i; 149 | void* p; 150 | 151 | if (len == 1) 152 | { 153 | return rb_read_until_char_iov (rb, iov, *((char*) mem)); 154 | } 155 | 156 | if (rb->used > 0 && len > 0 && rb->used >= len) 157 | { 158 | if ((rb->read + rb->used) > rb->size) 159 | { 160 | iov[0].iov_base = rb->buffer + rb->read; 161 | iov[0].iov_len = rb->size - rb->read; 162 | if (iov[0].iov_len >= len) 163 | { 164 | if ((p = memmem (iov[0].iov_base, iov[0].iov_len, mem, len)) != NULL) 165 | { 166 | iov[0].iov_len = p - iov[0].iov_base; 167 | iov[1].iov_len = 0; 168 | return 1; 169 | } 170 | 171 | i = 1; 172 | iov[1].iov_base = iov[0].iov_base + iov[0].iov_len - len + 1; 173 | } 174 | else 175 | { 176 | i = len - iov[0].iov_len; 177 | iov[1].iov_base = iov[0].iov_base; 178 | } 179 | 180 | 181 | while (i < len) 182 | { 183 | if (memcmp (iov[1].iov_base, mem, len - i) == 0 && memcmp (rb->buffer, mem + i, i) == 0) 184 | { 185 | iov[0].iov_len = iov[1].iov_base - iov[0].iov_base; 186 | iov[1].iov_len = 0; 187 | return 1; 188 | } 189 | 190 | if (rb->used == iov[0].iov_len + i) 191 | { 192 | return 0; 193 | } 194 | 195 | iov[1].iov_base++; 196 | i++; 197 | } 198 | 199 | if (rb->used >= iov[0].iov_len + len) 200 | { 201 | if ((p = memmem (rb->buffer, rb->used - iov[0].iov_len, mem, len)) != NULL) 202 | { 203 | if (p == rb->buffer) 204 | { 205 | iov[1].iov_len = 0; 206 | return 1; 207 | } 208 | 209 | iov[1].iov_base = rb->buffer; 210 | iov[1].iov_len = p - rb->buffer; 211 | return 2; 212 | } 213 | } 214 | } 215 | else 216 | { 217 | iov[0].iov_base = rb->buffer + rb->read; 218 | iov[0].iov_len = rb->used; 219 | if ((p = memmem (iov[0].iov_base, iov[0].iov_len, mem, len)) != NULL) 220 | { 221 | iov[0].iov_len = p - iov[0].iov_base; 222 | iov[1].iov_len = 0; 223 | 224 | return 1; 225 | } 226 | } 227 | } 228 | 229 | return 0; 230 | } 231 | 232 | EXPORT_DEF size_t rb_read_upd (struct ringbuffer* rb, size_t len) 233 | { 234 | size_t s; 235 | 236 | if (rb->used < len) 237 | { 238 | len = rb->used; 239 | } 240 | 241 | if (len > 0) 242 | { 243 | rb->used -= len; 244 | 245 | if (rb->used == 0) 246 | { 247 | rb->read = 0; 248 | rb->write = 0; 249 | } 250 | else 251 | { 252 | s = rb->read + len; 253 | 254 | if (s >= rb->size) 255 | { 256 | rb->read = s - rb->size; 257 | } 258 | else 259 | { 260 | rb->read = s; 261 | } 262 | } 263 | } 264 | 265 | return len; 266 | } 267 | 268 | /* unused 269 | static size_t rb_read (struct ringbuffer* rb, char* buf, size_t len) 270 | { 271 | size_t s; 272 | 273 | if (rb->used < len) 274 | { 275 | len = rb->used; 276 | } 277 | 278 | if (len > 0) 279 | { 280 | s = rb->read + len; 281 | if (s > rb->size) 282 | { 283 | memmove (buf, rb->buffer + rb->read, rb->size - rb->read); 284 | memmove (buf + rb->size - rb->read, rb->buffer, s - rb->size); 285 | rb->read = s - rb->size; 286 | } 287 | else 288 | { 289 | memmove (buf, rb->buffer + rb->read, len); 290 | if (s == rb->size) 291 | { 292 | rb->read = 0; 293 | } 294 | else 295 | { 296 | rb->read = s; 297 | } 298 | } 299 | 300 | rb->used -= len; 301 | 302 | if (rb->used == 0) 303 | { 304 | rb->read = 0; 305 | rb->write = 0; 306 | } 307 | } 308 | 309 | return len; 310 | } 311 | */ 312 | 313 | /* ============================ WRITE ============================ */ 314 | 315 | EXPORT_DEF int rb_write_iov (const struct ringbuffer* rb, struct iovec iov[2]) 316 | { 317 | size_t free; 318 | 319 | free = rb_free (rb); 320 | if (free > 0) 321 | { 322 | if ((rb->write + free) > rb->size) 323 | { 324 | iov[0].iov_base = rb->buffer + rb->write; 325 | iov[0].iov_len = rb->size - rb->write; 326 | iov[1].iov_base = rb->buffer; 327 | iov[1].iov_len = free - iov[0].iov_len; 328 | 329 | return 2; 330 | } 331 | else 332 | { 333 | iov[0].iov_base = rb->buffer + rb->write; 334 | iov[0].iov_len = free; 335 | 336 | return 1; 337 | } 338 | } 339 | 340 | return 0; 341 | } 342 | 343 | EXPORT_DEF size_t rb_write_upd (struct ringbuffer* rb, size_t len) 344 | { 345 | size_t free; 346 | size_t s; 347 | 348 | free = rb_free (rb); 349 | if (free < len) 350 | { 351 | len = free; 352 | } 353 | 354 | if (len > 0) 355 | { 356 | s = rb->write + len; 357 | 358 | if (s > rb->size) 359 | { 360 | rb->write = s - rb->size; 361 | } 362 | else 363 | { 364 | rb->write = s; 365 | } 366 | 367 | rb->used += len; 368 | } 369 | 370 | return len; 371 | } 372 | 373 | EXPORT_DEF size_t rb_write_core (struct ringbuffer* rb, const char* buf, size_t len, rb_write_f method) 374 | { 375 | size_t free; 376 | size_t s; 377 | 378 | free = rb_free (rb); 379 | if (free < len) 380 | { 381 | len = free; 382 | } 383 | 384 | if (len > 0) 385 | { 386 | s = rb->write + len; 387 | 388 | if (s > rb->size) 389 | { 390 | (*method) (rb->buffer + rb->write, buf, rb->size - rb->write); 391 | (*method) (rb->buffer, buf + rb->size - rb->write, s - rb->size); 392 | rb->write = s - rb->size; 393 | } 394 | else 395 | { 396 | (*method) (rb->buffer + rb->write, buf, len); 397 | if (s == rb->size) 398 | { 399 | rb->write = 0; 400 | } 401 | else 402 | { 403 | rb->write = s; 404 | } 405 | } 406 | 407 | rb->used += len; 408 | } 409 | 410 | return len; 411 | } 412 | -------------------------------------------------------------------------------- /ringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 - 2010 3 | 4 | Artem Makhutov 5 | http://www.makhutov.org 6 | 7 | Dmitry Vagin 8 | */ 9 | 10 | #ifndef ____RINGBUFFER_H__ 11 | #define ____RINGBUFFER_H__ 12 | 13 | #include /* struct iovec */ 14 | #include "export.h" /* EXPORT_DECL EXPORT_DEF */ 15 | 16 | typedef void * (*rb_write_f)(void* s1, const void* s2, size_t n); 17 | 18 | struct ringbuffer 19 | { 20 | void* buffer; /*!< pointer to data buffer */ 21 | size_t size; /*!< size of buffer */ 22 | size_t used; /*!< number of bytes used */ 23 | size_t read; /*!< read position */ 24 | size_t write; /*!< write position */ 25 | }; 26 | 27 | 28 | INLINE_DECL void rb_init (struct ringbuffer* rb, void* buf, size_t size) 29 | { 30 | rb->buffer = buf; 31 | rb->size = size; 32 | rb->used = 0; 33 | rb->read = 0; 34 | rb->write = 0; 35 | } 36 | 37 | INLINE_DECL size_t rb_used (const struct ringbuffer* rb) 38 | { 39 | return rb->used; 40 | } 41 | 42 | INLINE_DECL size_t rb_free (const struct ringbuffer* rb) 43 | { 44 | return rb->size - rb->used; 45 | } 46 | 47 | EXPORT_DECL int rb_memcmp (const struct ringbuffer*, const char*, size_t); 48 | 49 | /*!< fill io vectors array with readed data (situable for writev()) and return number of io vectors updated */ 50 | EXPORT_DECL int rb_read_all_iov (const struct ringbuffer* rb, struct iovec iov[2]); 51 | 52 | /*!< fill io vectors array and return number of io vectors updated for reading len bytes */ 53 | EXPORT_DECL int rb_read_n_iov (const struct ringbuffer* rb, struct iovec* iov, size_t len); 54 | 55 | EXPORT_DECL int rb_read_until_char_iov (const struct ringbuffer*, struct iovec iov[2], char); 56 | EXPORT_DECL int rb_read_until_mem_iov (const struct ringbuffer*, struct iovec iov[2], const void*, size_t); 57 | 58 | /*!< advice read position to len bytes */ 59 | EXPORT_DECL size_t rb_read_upd (struct ringbuffer* rb, size_t len); 60 | 61 | /*!< fill io vectors array with free data (situable for readv()) and return number of io vectors updated */ 62 | EXPORT_DECL int rb_write_iov (const struct ringbuffer*, struct iovec iov[2]); 63 | 64 | /*!< advice write position to len bytes */ 65 | EXPORT_DECL size_t rb_write_upd (struct ringbuffer*, size_t); 66 | 67 | EXPORT_DEF size_t rb_write_core (struct ringbuffer* rb, const char* buf, size_t len, rb_write_f method); 68 | 69 | INLINE_DECL size_t rb_write (struct ringbuffer* rb, const char* buf, size_t len) 70 | { 71 | return rb_write_core(rb, buf, len, memmove); 72 | } 73 | 74 | #endif /* ____RINGBUFFER_H__ */ 75 | -------------------------------------------------------------------------------- /single.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 bg 3 | */ 4 | #define BUILD_SINGLE 5 | 6 | #include "app.c" 7 | #include "at_command.c" 8 | #include "at_parse.c" 9 | #include "at_queue.c" 10 | #include "at_read.c" 11 | #include "at_response.c" 12 | #include "chan_dongle.c" 13 | #include "channel.c" 14 | #include "char_conv.c" 15 | #include "cli.c" 16 | #include "cpvt.c" 17 | #include "helpers.c" 18 | #include "manager.c" 19 | #include "memmem.c" 20 | #include "ringbuffer.c" 21 | #include "dc_config.c" 22 | #include "pdu.c" 23 | #include "mixbuffer.c" 24 | #include "pdiscovery.c" 25 | -------------------------------------------------------------------------------- /stamp-h.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krip-tip/asterisk-chan-dongle/e3bce3c1d43cec1e4f8e548b4d3880e289747b24/stamp-h.in -------------------------------------------------------------------------------- /test/parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "at_parse.h" /* at_parse_*() */ 6 | #include "mutils.h" /* ITEMS_OF() */ 7 | 8 | 9 | int ok = 0; 10 | int faults = 0; 11 | 12 | #/* */ 13 | void test_parse_cnum() 14 | { 15 | static const struct test_case { 16 | const char * input; 17 | const char * result; 18 | } cases[] = { 19 | { "+CNUM: \"*Subscriber Number\",\"+79139131234\",145", "+79139131234" }, 20 | { "+CNUM: \"Subscriber Number\",\"\",145", "" }, 21 | { "+CNUM: \"Subscriber Number\",,145", "" }, 22 | { "+CNUM: \"\",\"+79139131234\",145", "+79139131234" }, 23 | { "+CNUM: ,\"\",145", "" }, 24 | { "+CNUM: ,,145", "" }, 25 | { "+CNUM: \"\",+79139131234\",145", "+79139131234" }, 26 | { "+CNUM: \"\",+79139131234,145", "+79139131234" }, 27 | }; 28 | unsigned idx = 0; 29 | char * input; 30 | const char * res; 31 | const char * msg; 32 | 33 | for(; idx < ITEMS_OF(cases); ++idx) { 34 | input = strdup(cases[idx].input); 35 | fprintf(stderr, "%s(\"%s\")...", "at_parse_cnum", input); 36 | res = at_parse_cnum(input); 37 | if(strcmp(res, cases[idx].result) == 0) { 38 | msg = "OK"; 39 | ok++; 40 | } else { 41 | msg = "FAIL"; 42 | faults++; 43 | } 44 | fprintf(stderr, " = \"%s\"\t%s\n", res, msg); 45 | free(input); 46 | } 47 | fprintf(stderr, "\n"); 48 | } 49 | 50 | #/* */ 51 | void test_parse_cops() 52 | { 53 | static const struct test_case { 54 | const char * input; 55 | const char * result; 56 | } cases[] = { 57 | { "+COPS: 0,0,\"TELE2\",0", "TELE2" }, 58 | { "+COPS: 0,0,\"TELE2,0", "TELE2" }, 59 | { "+COPS: 0,0,TELE2,0", "TELE2" }, 60 | }; 61 | unsigned idx = 0; 62 | char * input; 63 | const char * res; 64 | const char * msg; 65 | 66 | for(; idx < ITEMS_OF(cases); ++idx) { 67 | input = strdup(cases[idx].input); 68 | fprintf(stderr, "%s(\"%s\")...", "at_parse_cops", input); 69 | res = at_parse_cops(input); 70 | if(strcmp(res, cases[idx].result) == 0) { 71 | msg = "OK"; 72 | ok++; 73 | } else { 74 | msg = "FAIL"; 75 | faults++; 76 | } 77 | fprintf(stderr, " = \"%s\"\t%s\n", res, msg); 78 | free(input); 79 | } 80 | fprintf(stderr, "\n"); 81 | } 82 | 83 | #/* */ 84 | void test_parse_creg() 85 | { 86 | struct result { 87 | int res; 88 | int gsm_reg; 89 | int gsm_reg_status; 90 | char * lac; 91 | char * ci; 92 | }; 93 | static const struct test_case { 94 | const char * input; 95 | struct result result; 96 | } cases[] = { 97 | { "+CREG: 2,1,9110,7E6", { 0, 1, 1, "9110", "7E6"} }, 98 | { "+CREG: 2,1,XXXX,AAAA", { 0, 1, 1, "XXXX", "AAAA"} }, 99 | }; 100 | unsigned idx = 0; 101 | char * input; 102 | struct result result; 103 | const char * msg; 104 | 105 | for(; idx < ITEMS_OF(cases); ++idx) { 106 | input = strdup(cases[idx].input); 107 | fprintf(stderr, "%s(\"%s\")...", "at_parse_creg", input); 108 | result.res = at_parse_creg(input, strlen(input), &result.gsm_reg, &result.gsm_reg_status, &result.lac, &result.ci); 109 | if(result.res == cases[idx].result.res 110 | && 111 | result.gsm_reg == cases[idx].result.gsm_reg 112 | && 113 | result.gsm_reg_status == cases[idx].result.gsm_reg_status 114 | && 115 | strcmp(result.lac, cases[idx].result.lac) == 0 116 | && 117 | strcmp(result.ci, cases[idx].result.ci) == 0 118 | ) { 119 | msg = "OK"; 120 | ok++; 121 | } else { 122 | msg = "FAIL"; 123 | faults++; 124 | } 125 | fprintf(stderr, " = %d (%d,%d,\"%s\",\"%s\")\t%s\n", result.res, result.gsm_reg, result.gsm_reg_status, result.lac, result.ci, msg); 126 | free(input); 127 | } 128 | fprintf(stderr, "\n"); 129 | } 130 | 131 | #/* */ 132 | void test_parse_cmti() 133 | { 134 | static const struct test_case { 135 | const char * input; 136 | int result; 137 | } cases[] = { 138 | { "+CMTI: \"ME\",41", 41 }, 139 | { "+CMTI: 0,111", 111 }, 140 | { "+CMTI: ", -1 }, 141 | }; 142 | unsigned idx = 0; 143 | char * input; 144 | int result; 145 | const char * msg; 146 | 147 | for(; idx < ITEMS_OF(cases); ++idx) { 148 | input = strdup(cases[idx].input); 149 | fprintf(stderr, "%s(\"%s\")...", "at_parse_cmti", input); 150 | result = at_parse_cmti(input); 151 | if(result == cases[idx].result) { 152 | msg = "OK"; 153 | ok++; 154 | } else { 155 | msg = "FAIL"; 156 | faults++; 157 | } 158 | fprintf(stderr, " = %d\t%s\n", result, msg); 159 | free(input); 160 | } 161 | fprintf(stderr, "\n"); 162 | } 163 | 164 | #/* */ 165 | void test_parse_cmgr() 166 | { 167 | struct result { 168 | const char * res; 169 | char * str; 170 | char * oa; 171 | str_encoding_t oa_enc; 172 | char * msg; 173 | str_encoding_t msg_enc; 174 | }; 175 | static const struct test_case { 176 | const char * input; 177 | struct result result; 178 | } cases[] = { 179 | { "+CMGR: \"REC READ\",\"+79139131234\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", 180 | { 181 | NULL, 182 | "\"REC READ\",\"+79139131234", 183 | "+79139131234", 184 | STR_ENCODING_7BIT, 185 | "041F04400438043204350442", 186 | STR_ENCODING_UNKNOWN 187 | } 188 | }, 189 | { "+CMGR: \"REC READ\",\"002B00370039003500330037003600310032003000350032\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", 190 | { 191 | NULL, 192 | "\"REC READ\",\"002B00370039003500330037003600310032003000350032", 193 | "002B00370039003500330037003600310032003000350032", 194 | STR_ENCODING_UNKNOWN, 195 | "041F04400438043204350442", 196 | STR_ENCODING_UNKNOWN 197 | } 198 | }, 199 | { "+CMGR: 0,,106\r\n07911111111100F3040B911111111111F200000121702214952163B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", 200 | { 201 | NULL, 202 | "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", 203 | "+11111111112", 204 | STR_ENCODING_7BIT, 205 | "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", 206 | STR_ENCODING_7BIT_HEX 207 | } 208 | }, 209 | { "+CMGR: 0,,159\r\n07919740430900F3440B912222222220F20008012180004390218C0500030003010031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", 210 | { 211 | NULL, 212 | "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", 213 | "+22222222022", 214 | STR_ENCODING_7BIT, 215 | "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", 216 | STR_ENCODING_UCS2_HEX 217 | } 218 | }, 219 | 220 | }; 221 | 222 | unsigned idx = 0; 223 | char * input; 224 | struct result result; 225 | char oa[200]; 226 | const char * msg; 227 | 228 | result.oa = oa; 229 | for(; idx < ITEMS_OF(cases); ++idx) { 230 | result.str = input = strdup(cases[idx].input); 231 | fprintf(stderr, "%s(\"%s\")...", "at_parse_cmgr", input); 232 | result.res = at_parse_cmgr(&result.str, strlen(result.str), result.oa, sizeof(oa), &result.oa_enc, &result.msg, &result.msg_enc); 233 | if( ((result.res == NULL && result.res == cases[idx].result.res) || strcmp(result.res, cases[idx].result.res) == 0) 234 | && 235 | strcmp(result.str, cases[idx].result.str) == 0 236 | && 237 | strcmp(result.oa, cases[idx].result.oa) == 0 238 | && 239 | result.oa_enc == cases[idx].result.oa_enc 240 | && 241 | strcmp(result.msg, cases[idx].result.msg) == 0 242 | && 243 | result.msg_enc == cases[idx].result.msg_enc 244 | ) { 245 | msg = "OK"; 246 | ok++; 247 | } else { 248 | msg = "FAIL"; 249 | faults++; 250 | } 251 | fprintf(stderr, " = '%s' ('%s','%s',%d,'%s',%d)\t%s\n", result.res, result.str, result.oa, result.oa_enc, result.msg, result.msg_enc, msg); 252 | free(input); 253 | } 254 | fprintf(stderr, "\n"); 255 | } 256 | 257 | #/* */ 258 | void test_parse_cusd() 259 | { 260 | struct result { 261 | int res; 262 | int type; 263 | char * cusd; 264 | int dcs; 265 | }; 266 | static const struct test_case { 267 | const char * input; 268 | struct result result; 269 | } cases[] = { 270 | { "+CUSD: 0,\"CF2135487D2E4130572D0682BB1A\",0", { 0, 0, "CF2135487D2E4130572D0682BB1A", 0} }, 271 | { "+CUSD: 1,\"CF2135487D2E4130572D0682BB1A\",1", { 0, 1, "CF2135487D2E4130572D0682BB1A", 1} }, 272 | { "+CUSD: 5", { 0, 5, "", -1} }, 273 | }; 274 | unsigned idx = 0; 275 | char * input; 276 | struct result result; 277 | const char * msg; 278 | 279 | for(; idx < ITEMS_OF(cases); ++idx) { 280 | input = strdup(cases[idx].input); 281 | fprintf(stderr, "%s(\"%s\")...", "at_parse_cusd", input); 282 | result.res = at_parse_cusd(input, &result.type, &result.cusd, &result.dcs); 283 | if(result.res == cases[idx].result.res 284 | && 285 | result.type == cases[idx].result.type 286 | && 287 | result.dcs == cases[idx].result.dcs 288 | && 289 | strcmp(result.cusd, cases[idx].result.cusd) == 0 290 | ) { 291 | msg = "OK"; 292 | ok++; 293 | } else { 294 | msg = "FAIL"; 295 | faults++; 296 | } 297 | fprintf(stderr, " = %d (%d,\"%s\",%d)\t%s\n", result.res, result.type, result.cusd, result.dcs, msg); 298 | free(input); 299 | } 300 | fprintf(stderr, "\n"); 301 | } 302 | 303 | #/* */ 304 | void test_parse_cpin() 305 | { 306 | } 307 | 308 | #/* */ 309 | void test_parse_csq() 310 | { 311 | } 312 | 313 | #/* */ 314 | void test_parse_rssi() 315 | { 316 | } 317 | 318 | #/* */ 319 | void test_parse_mode() 320 | { 321 | } 322 | 323 | #/* */ 324 | void test_parse_csca() 325 | { 326 | } 327 | 328 | #/* */ 329 | void test_parse_clcc() 330 | { 331 | struct result { 332 | int res; 333 | 334 | unsigned index; 335 | unsigned dir; 336 | unsigned stat; 337 | unsigned mode; 338 | unsigned mpty; 339 | char * number; 340 | unsigned toa; 341 | }; 342 | static const struct test_case { 343 | const char * input; 344 | struct result result; 345 | } cases[] = { 346 | { "+CLCC: 1,1,4,0,0,\"\",145", { 0, 1, 1, 4, 0, 0, "", 145} }, 347 | { "+CLCC: 1,1,4,0,0,\"+79139131234\",145", { 0, 1, 1, 4, 0, 0, "+79139131234", 145} }, 348 | { "+CLCC: 1,1,4,0,0,\"+7913913ABCA\",145", { 0, 1, 1, 4, 0, 0, "+7913913ABCA", 145} }, 349 | { "+CLCC: 1,1,4,0,0,\"+7913913ABCA\"", { -1, 0, 0, 0, 0, 0, "", 0} }, 350 | }; 351 | unsigned idx = 0; 352 | char * input; 353 | struct result result; 354 | const char * msg; 355 | 356 | for(; idx < ITEMS_OF(cases); ++idx) { 357 | input = strdup(cases[idx].input); 358 | fprintf(stderr, "%s(\"%s\")...", "at_parse_clcc", input); 359 | result.res = at_parse_clcc(input, &result.index, &result.dir, &result.stat, &result.mode, &result.mpty, &result.number, &result.toa); 360 | if(result.res == cases[idx].result.res 361 | && 362 | result.index == cases[idx].result.index 363 | && 364 | result.dir == cases[idx].result.dir 365 | && 366 | result.stat == cases[idx].result.stat 367 | && 368 | result.mode == cases[idx].result.mode 369 | && 370 | result.mpty == cases[idx].result.mpty 371 | && 372 | strcmp(result.number, cases[idx].result.number) == 0 373 | && 374 | result.toa == cases[idx].result.toa 375 | ) { 376 | msg = "OK"; 377 | ok++; 378 | } else { 379 | msg = "FAIL"; 380 | faults++; 381 | } 382 | fprintf(stderr, " = %d (%d,%d,%d,%d,%d,\"%s\",%d)\t%s\n", result.res, result.index, result.dir, result.stat, result.mode, result.mpty, result.number, result.toa, msg); 383 | free(input); 384 | } 385 | fprintf(stderr, "\n"); 386 | } 387 | 388 | #/* */ 389 | void test_parse_ccwa() 390 | { 391 | } 392 | 393 | #/* */ 394 | int main() 395 | { 396 | test_parse_cnum(); 397 | test_parse_cops(); 398 | test_parse_creg(); 399 | test_parse_cmti(); 400 | test_parse_cmgr(); 401 | test_parse_cusd(); 402 | test_parse_cpin(); 403 | test_parse_csq(); 404 | test_parse_rssi(); 405 | test_parse_mode(); 406 | test_parse_csca(); 407 | test_parse_clcc(); 408 | test_parse_ccwa(); 409 | 410 | fprintf(stderr, "done %d tests: %d OK %d FAILS\n", ok + faults, ok, faults); 411 | return 0; 412 | } -------------------------------------------------------------------------------- /test/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mixbuffer.h" 3 | #include "helpers.h" 4 | 5 | void hex_encode(unsigned char * bytes, unsigned length) 6 | { 7 | for(; length; --length) 8 | { 9 | fprintf(stderr, "%02X", *bytes++); 10 | } 11 | } 12 | 13 | void check_result1(unsigned st, int lbuf, const struct mixbuffer * mixb, struct mixstream * lb) 14 | { 15 | 16 | struct state { 17 | char buffer[40]; 18 | struct mixbuffer mb; 19 | struct mixstream st; 20 | }; 21 | struct state states[50]; 22 | 23 | if(st < ITEMS_OF(states)) 24 | { 25 | // struct state * state = states + st; 26 | /* 27 | state->rb.buffer = rb->buffer; 28 | int rv = memcmp(rb->buffer, state->buffer, sizeof(state->buffer)) == 0 29 | && 30 | memcmp(rb, &state->rb, sizeof(state->rb)) == 0 31 | && 32 | memcmp(lb, &state->lb, sizeof(state->lb)) == 0; 33 | */ 34 | fprintf(stderr, "'"); 35 | hex_encode(mixb->rb.buffer, mixb->rb.size); 36 | fprintf(stderr, "', %2u, %2u, %2u, %2u, %2u, %2u, %2d\n", 37 | (unsigned)mixb->rb.size, 38 | (unsigned)mixb->rb.used, 39 | (unsigned)mixb->rb.read, 40 | (unsigned)mixb->rb.write, 41 | (unsigned)lb->write, 42 | (unsigned)lb->used, 43 | lbuf 44 | ); 45 | } 46 | } 47 | 48 | #/* */ 49 | void test_suite1() 50 | { 51 | unsigned i; 52 | char buffer[40]; 53 | 54 | static const char x1[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00 }; 55 | static const char x2[] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00 }; 56 | static const char x3[] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }; 57 | static const char x4[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00 }; 58 | // static const char x5[] = { 0x05, 0x05, 0x05, 0x05, 0x05, 0x00 }; 59 | // static const char x6[] = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00 }; 60 | // static const char x7[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00 }; 61 | // static const char x8[] = { 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00 }; 62 | // static const char x9[] = { 0x10, 0x10, 0x10, 0x10, 0x00 }; 63 | static const char * strings[] = { 64 | x1, x2, x3, x4, 65 | // x5, x6, x7, x8, x9 66 | }; 67 | 68 | struct mixbuffer mb; 69 | struct mixstream locals[5]; 70 | 71 | memset(buffer, 0, sizeof(buffer)); 72 | 73 | mixb_init(&mb, buffer, sizeof(buffer)); 74 | for(i = 0; i < ITEMS_OF(locals); i++) 75 | mixb_attach(&mb, &locals[i]); 76 | 77 | fprintf(stderr, "Testing rb_overwrite()"); 78 | 79 | 80 | fprintf(stderr, "Data size used read write write1 used1 idx\n"); 81 | for(i = 0; i < 50; i++) { 82 | int idx = i % ITEMS_OF(strings); 83 | unsigned length = strlen(strings[idx]); 84 | int lbuf = i % ITEMS_OF(locals); 85 | 86 | if(mixb_free(&mb, &locals[lbuf]) < length) 87 | mixb_read_upd(&mb, length - mixb_free(&mb, &locals[lbuf])); 88 | 89 | mixb_write(&mb, &locals[lbuf], strings[idx], length); 90 | check_result1(i, lbuf, &mb, &locals[lbuf]); 91 | } 92 | 93 | for(i = 0; i < ITEMS_OF(locals); i++) 94 | mixb_detach(&mb, &locals[i]); 95 | 96 | } 97 | 98 | #/* */ 99 | int main(int argc, char * argv[]) 100 | { 101 | test_suite1(); 102 | 103 | return 0; 104 | } -------------------------------------------------------------------------------- /tools/discovery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include /* AST_LIST_ENTRY() */ 10 | 11 | #include "mutils.h" 12 | #include "tty.h" 13 | 14 | struct dev_descr { 15 | AST_LIST_ENTRY (dev_descr) entry; 16 | 17 | int busnum; 18 | char devpath[40]; 19 | int configuration; 20 | int interfaceno; 21 | char port[80]; 22 | }; 23 | 24 | typedef AST_LIST_HEAD_NOLOCK(, dev_descr) dev_list_t; 25 | 26 | static const char sys_driver[] = "/sys/bus/usb/drivers"; 27 | static const char port_number[] = "port_number"; 28 | 29 | #/* */ 30 | char * read_result(int fd) 31 | { 32 | unsigned total = 0; 33 | char buf[4096]; 34 | char * found; 35 | int readed; 36 | 37 | while(1) { 38 | readed = read(fd, buf + total, sizeof(buf) - total); 39 | if(readed <= 0) 40 | return NULL; 41 | total += readed; 42 | // fprintf(stdout, "%*s", readed, buf); 43 | if((found = memmem(buf, total, "\r\nOK\r\n", 6)) != NULL) { 44 | found[0] = 0; 45 | return strdup(buf); 46 | } else if((found = memmem(buf, total, "\r\nERROR\r\n", 9)) != NULL) { 47 | return NULL; 48 | } 49 | } 50 | return NULL; 51 | } 52 | 53 | #/* */ 54 | unsigned count_lines(const char * lines) 55 | { 56 | unsigned lno = 1; 57 | const char * str = lines; 58 | 59 | while(1) { 60 | const char * x = strstr(str, "\r\n"); 61 | if(x == NULL) 62 | break; 63 | str = x + 2; 64 | lno++; 65 | } 66 | 67 | return lno; 68 | } 69 | 70 | #/* */ 71 | char ** read_results(int fd, int * argc) 72 | { 73 | int arg; 74 | char * str; 75 | char * x; 76 | char ** argv; 77 | int lno; 78 | 79 | char * lines = read_result(fd); 80 | 81 | if(!lines) { 82 | argc = 0; 83 | return NULL; 84 | } 85 | 86 | lno = count_lines(lines); 87 | argv = malloc((lno + 1) * sizeof(lines)); 88 | 89 | for(arg = 0, str = lines; arg < lno; ) { 90 | x = strstr(str, "\r\n"); 91 | if(x == NULL) 92 | break; 93 | x[0] = 0; 94 | if(str[0] != 0) 95 | argv[arg++] = strdup(str); 96 | str = x + 2; 97 | } 98 | free(lines); 99 | argv[arg] = NULL; 100 | 101 | *argc = arg; 102 | return argv; 103 | } 104 | 105 | #/* */ 106 | void free_results(char ** argv) 107 | { 108 | if(argv) { 109 | unsigned idx; 110 | for(idx = 0; argv[idx]; idx++) { 111 | free(argv[idx]); 112 | } 113 | free(argv); 114 | } 115 | } 116 | 117 | #/* */ 118 | int discovery_port(struct dev_descr * descr, const char * name) 119 | { 120 | char pname[PATH_MAX]; 121 | struct dirent * entry; 122 | struct stat statb; 123 | 124 | DIR * dir = opendir(name); 125 | if(dir) { 126 | while((entry = readdir(dir)) != NULL) { 127 | snprintf(pname, sizeof(pname), "%s/%s/%s", name, entry->d_name, port_number); 128 | if(stat(pname, &statb) == 0) { 129 | snprintf(descr->port, sizeof(descr->port), "/dev/%s", entry->d_name); 130 | return 1; 131 | } 132 | } 133 | closedir(dir); 134 | } 135 | return 0; 136 | } 137 | 138 | #/* */ 139 | char * get_info_item(char ** argv, const char * name, unsigned len) 140 | { 141 | for(; argv[0]; argv++) { 142 | if(strncmp(argv[0], name, len) == 0) { 143 | char * found = argv[0] + len; 144 | while(found[0] == ' ') 145 | found++; 146 | return strdup(found); 147 | } 148 | } 149 | return NULL; 150 | } 151 | 152 | #/* */ 153 | int get_info(const char * port, char ** manu, char ** model, char ** imei, char ** imsi) 154 | { 155 | int fd = opentty(port); 156 | *manu = *model = *imei = *imsi = 0; 157 | if(fd >= 0) { 158 | static const char ati[] = "ATI\r"; 159 | static const char cimi[] = "AT+CIMI\r"; 160 | char **argv; 161 | int argc; 162 | 163 | write_all(fd, ati, STRLEN(ati)); 164 | argv = read_results(fd, &argc); 165 | if(argv) { 166 | static const char s_manufactorer[] = "Manufacturer:"; 167 | static const char s_model[] = "Model:"; 168 | static const char s_imei[] = "IMEI:"; 169 | 170 | 171 | *manu = get_info_item(argv, s_manufactorer, STRLEN(s_manufactorer)); 172 | *model = get_info_item(argv, s_model, STRLEN(s_model)); 173 | *imei = get_info_item(argv, s_imei, STRLEN(s_imei)); 174 | 175 | free_results(argv); 176 | } 177 | 178 | write_all(fd, cimi, STRLEN(cimi)); 179 | argv = read_results(fd, &argc); 180 | if(argv) { 181 | int x = 0; 182 | if(strncmp(argv[x], cimi, STRLEN(cimi)) == 0) 183 | x++; 184 | *imsi = strdup(argv[x]); 185 | free_results(argv); 186 | } 187 | closetty(port, fd); 188 | } 189 | 190 | return *manu || *model || *imei || *imsi; 191 | } 192 | 193 | #/* */ 194 | int discovery_all(dev_list_t * devs) 195 | { 196 | struct dev_descr * dev; 197 | 198 | AST_LIST_TRAVERSE(devs, dev, entry) { 199 | if(dev->interfaceno == 0) { 200 | char * manu, * model, * imei, * imsi; 201 | fprintf(stdout, "Bus: %d Dev: %s Conf: %d\n", dev->busnum, dev->devpath, dev->configuration); 202 | if(get_info(dev->port, &manu, &model, &imei, &imsi)) { 203 | fprintf(stdout, "Manufacturer: %s Model: %s IMEI: %s IMSI: %s\n", manu, model, imei, imsi); 204 | free(manu); 205 | free(model); 206 | free(imei); 207 | free(imsi); 208 | } 209 | } 210 | fprintf(stdout, "\tInterface: %d Port: %s\n", dev->interfaceno, dev->port); 211 | } 212 | return 0; 213 | } 214 | 215 | /* 216 | * /sys/bus/usb/drivers/option 217 | * 1-1:1.0 218 | * 1-1:1.1 219 | * 1-1:1.2 220 | * 221 | */ 222 | 223 | #/* */ 224 | void discovery_driver(const char * driver) 225 | { 226 | char realname[PATH_MAX]; 227 | char name[PATH_MAX]; 228 | char * name2; 229 | DIR * dir; 230 | struct dirent * entry; 231 | int len; 232 | dev_list_t devs; 233 | 234 | len = snprintf(name, sizeof(name), "%s/%s", sys_driver, driver); 235 | dir = opendir(name); 236 | if(dir) { 237 | while((entry = readdir(dir)) != NULL) { 238 | struct dev_descr * descr = malloc(sizeof(*descr)); 239 | /* check numbers */ 240 | // if(entry->d_type == && sscanf(entry->d_name, "%u:%u-%u:%u", &id1, &id2, &id3, &id4) == 4) 241 | if(sscanf(entry->d_name, "%d-%39[^:]:%d.%d", &descr->busnum, descr->devpath, &descr->configuration, &descr->interfaceno) == 4) 242 | { 243 | // fprintf(stdout, "device %s\n", entry->d_name); 244 | snprintf(name + len, sizeof(name) - len, "/%s", entry->d_name); 245 | 246 | if(realpath(name, realname) != NULL) 247 | name2 = realname; 248 | else { 249 | name2 = name; 250 | } 251 | 252 | if(discovery_port(descr, name2)) { 253 | AST_LIST_INSERT_TAIL(&devs, descr, entry); 254 | continue; 255 | } 256 | } 257 | free(descr); 258 | } 259 | closedir(dir); 260 | } 261 | 262 | discovery_all(&devs); 263 | } 264 | 265 | #/* */ 266 | int main(int argc, char * argv[]) 267 | { 268 | int arg; 269 | 270 | discovery_driver("option"); 271 | for(arg = 1; arg < argc; ++arg) 272 | discovery_driver(argv[arg]); 273 | return 0; 274 | } 275 | -------------------------------------------------------------------------------- /tools/tty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include /* struct termios tcgetattr() tcsetattr() */ 3 | #include /* O_RDWR O_NOCTTY */ 4 | #include /* S_IRUSR | S_IRGRP | S_IROTH */ 5 | #include /* strrchr() */ 6 | #include /* PATH_MAX */ 7 | #include /* realpath() */ 8 | #include /* getpid() */ 9 | #include /* errno */ 10 | #include /* kill() */ 11 | 12 | #/* return length of lockname */ 13 | static int lock_build(const char * devname, char * buf, unsigned length) 14 | { 15 | const char * basename; 16 | char resolved_path[PATH_MAX]; 17 | 18 | /* follow symlinks */ 19 | if(realpath(devname, resolved_path) != NULL) 20 | devname = resolved_path; 21 | 22 | /* 23 | while(1) 24 | { 25 | len = readlink(devname, symlink, sizeof(symlink) - 1); 26 | if(len <= 0) 27 | break; 28 | symlink[len] = 0; 29 | if(symlink[0] == '/') 30 | devname = symlink; 31 | else 32 | { 33 | // TODO 34 | memmove() 35 | memcpy(symlink, devname); 36 | } 37 | } 38 | */ 39 | 40 | basename = strrchr(devname, '/'); 41 | if(basename) 42 | basename++; 43 | else 44 | basename = devname; 45 | 46 | /* TODO: use asterisk build settings for /var/lock */ 47 | return snprintf(buf, length, "/var/lock/LOCK..%s", basename); 48 | } 49 | 50 | #/* return 0 on error */ 51 | static int lock_create(const char * lockfile) 52 | { 53 | int fd; 54 | int len = 0; 55 | char pidb[21]; 56 | 57 | fd = open(lockfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IRGRP | S_IROTH); 58 | if(fd >= 0) 59 | { 60 | len = snprintf(pidb, sizeof(pidb), "%d", getpid()); 61 | len = write(fd, pidb, len); 62 | close(fd); 63 | } 64 | return len; 65 | } 66 | 67 | #/* return pid of owner, 0 if free */ 68 | static int lock_try(const char * devname) 69 | { 70 | int fd; 71 | int len; 72 | int pid = 0; 73 | char name[256]; 74 | char pidb[21]; 75 | 76 | lock_build(devname, name, sizeof(name)); 77 | 78 | /* FIXME: rise condition: some time between lock check and got lock */ 79 | fd = open(name, O_RDONLY); 80 | if(fd >= 0) 81 | { 82 | len = read(fd, pidb, sizeof(pidb) - 1); 83 | if(len > 0) 84 | { 85 | pidb[len] = 0; 86 | len = strtol(pidb, NULL, 10); 87 | if(kill(len, 0) == 0) 88 | pid = len; 89 | } 90 | close(fd); 91 | } 92 | if(pid == 0) 93 | { 94 | unlink(name); 95 | lock_create(name); 96 | } 97 | return pid; 98 | } 99 | 100 | int opentty (const char* dev) 101 | { 102 | int pid; 103 | int fd; 104 | struct termios term_attr; 105 | 106 | fd = open (dev, O_RDWR | O_NOCTTY); 107 | 108 | if (fd < 0) 109 | { 110 | return -1; 111 | } 112 | 113 | if (tcgetattr (fd, &term_attr) != 0) 114 | { 115 | close(fd); 116 | return -1; 117 | } 118 | 119 | term_attr.c_cflag = B115200 | CS8 | CREAD | CRTSCTS; 120 | term_attr.c_iflag = 0; 121 | term_attr.c_oflag = 0; 122 | term_attr.c_lflag = 0; 123 | term_attr.c_cc[VMIN] = 1; 124 | term_attr.c_cc[VTIME] = 0; 125 | 126 | if (tcsetattr (fd, TCSAFLUSH, &term_attr) != 0) 127 | { 128 | } 129 | 130 | pid = lock_try(dev); 131 | if(pid != 0) 132 | { 133 | close(fd); 134 | return -1; 135 | } 136 | 137 | return fd; 138 | } 139 | 140 | #/* */ 141 | void closetty(const char * dev, int fd) 142 | { 143 | char name[256]; 144 | 145 | close(fd); 146 | 147 | /* remove lock */ 148 | lock_build(dev, name, sizeof(name)); 149 | unlink(name); 150 | } 151 | 152 | #/* */ 153 | size_t write_all (int fd, const char* buf, size_t count) 154 | { 155 | ssize_t out_count; 156 | size_t total = 0; 157 | unsigned errs = 10; 158 | 159 | while (count > 0) 160 | { 161 | out_count = write (fd, buf, count); 162 | if (out_count <= 0) 163 | { 164 | if(errno == EINTR || errno == EAGAIN) 165 | { 166 | errs--; 167 | if(errs != 0) 168 | continue; 169 | } 170 | break; 171 | } 172 | // fprintf(stdout, "%*s", out_count, buf); 173 | errs = 10; 174 | count -= out_count; 175 | buf += out_count; 176 | total += out_count; 177 | } 178 | return total; 179 | } 180 | -------------------------------------------------------------------------------- /tools/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef TTY_H_INCLUDED 2 | #define TTY_H_INCLUDED 3 | 4 | int opentty (const char * dev); 5 | void closetty(const char * dev, int fd); 6 | size_t write_all (int fd, const char* buf, size_t count); 7 | 8 | #endif /* TTY_H_INCLUDED */ 9 | --------------------------------------------------------------------------------