From 3848c3232efdfaa6500631e41ea086f8de874dd5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 4 Jun 2009 12:55:26 -0700 Subject: Make Android specific changes for hcidump. Add Android.mk, stub out IPV6 features. --- Android.mk | 39 +++++++++++++++++++++++++++++++++++++++ parser/bnep.c | 2 +- parser/tcpip.c | 12 ++++++++++-- src/hcidump.c | 3 ++- 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100755 Android.mk diff --git a/Android.mk b/Android.mk new file mode 100755 index 0000000..faf8993 --- /dev/null +++ b/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES:= \ + $(call include-path-for,bluez)/include/ \ + +LOCAL_CFLAGS:= \ + -DVERSION=\"1.42\" + +LOCAL_SRC_FILES:= \ + parser/avctp.c \ + parser/avdtp.c \ + parser/bnep.c \ + parser/bpa.c \ + parser/capi.c \ + parser/cmtp.c \ + parser/csr.c \ + parser/ericsson.c \ + parser/hci.c \ + parser/hcrp.c \ + parser/hidp.c \ + parser/l2cap.c \ + parser/lmp.c \ + parser/obex.c \ + parser/parser.c \ + parser/ppp.c \ + parser/rfcomm.c \ + parser/sdp.c \ + parser/tcpip.c \ + src/hcidump.c + +LOCAL_SHARED_LIBRARIES := \ + libbluetooth \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE:=hcidump + +include $(BUILD_EXECUTABLE) diff --git a/parser/bnep.c b/parser/bnep.c index 8ecb591..4ab5aa4 100644 --- a/parser/bnep.c +++ b/parser/bnep.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include "parser.h" diff --git a/parser/tcpip.c b/parser/tcpip.c index 7c2d2a0..7e29b3f 100644 --- a/parser/tcpip.c +++ b/parser/tcpip.c @@ -32,10 +32,14 @@ #include #include -#include +#include #include #include + +#ifdef HAS_INET6 #include +#endif + #include #include #include @@ -44,6 +48,7 @@ void arp_dump(int level, struct frame *frm) { +#if 0 int i; char buf[20]; struct sockaddr_in sai; @@ -68,11 +73,14 @@ void arp_dump(int level, struct frame *frm) printf("(%s)\n", buf); frm->ptr += sizeof(struct ether_arp); frm->len -= sizeof(struct ether_arp); +#endif raw_dump(level, frm); // not needed. + } void ip_dump(int level, struct frame *frm) { +#if 0 char src[50], dst[50]; struct ip *ip = (struct ip *) (frm->ptr); uint8_t proto; @@ -136,6 +144,6 @@ void ip_dump(int level, struct frame *frm) printf("Unknown Protocol: 0x%02x\n", ip->ip_p); break; } - +#endif raw_dump(level, frm); } diff --git a/src/hcidump.c b/src/hcidump.c index 5c5ae38..9a2429d 100644 --- a/src/hcidump.c +++ b/src/hcidump.c @@ -660,13 +660,14 @@ static int open_connection(char *addr, char *port) ((struct sockaddr_in *) &ss)->sin_addr.s_addr = htonl(INADDR_ANY); ((struct sockaddr_in *) &ss)->sin_port = 0; break; +#ifdef HAS_INET6 case AF_INET6: memcpy(&((struct sockaddr_in6 *) &ss)->sin6_addr, &in6addr_any, sizeof(in6addr_any)); ((struct sockaddr_in6 *) &ss)->sin6_port = 0; break; +#endif } - if (bind(sk, (struct sockaddr *) &ss, sizeof(ss)) < 0) { perror("Can't bind socket"); close(sk); -- cgit v1.2.3 From 827ee4b3a3926200c6f70e6be418656a6619aa2d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 9 Jun 2009 22:55:31 -0700 Subject: Don't build hcidump for SDK and simulator. --- Android.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Android.mk b/Android.mk index faf8993..47bb8fa 100755 --- a/Android.mk +++ b/Android.mk @@ -1,3 +1,6 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -37,3 +40,5 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE:=hcidump include $(BUILD_EXECUTABLE) +endif +endif -- cgit v1.2.3 From 7bd8a6e7d99f8e000432401851a38362fd2ce989 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Thu, 12 Nov 2009 18:45:23 -0800 Subject: eclair snapshot --- AUTHORS | 5 + Android.mk | 44 + COPYING | 340 ++++++ ChangeLog | 282 +++++ INSTALL | 236 ++++ Makefile.am | 6 + NEWS | 0 README | 34 + acinclude.m4 | 82 ++ bootstrap | 6 + bootstrap-configure | 11 + configure.in | 22 + parser/Makefile.am | 12 + parser/avctp.c | 45 + parser/avdtp.c | 493 ++++++++ parser/bnep.c | 318 +++++ parser/bpa.c | 65 + parser/capi.c | 849 +++++++++++++ parser/cmtp.c | 211 ++++ parser/csr.c | 628 ++++++++++ parser/ericsson.c | 53 + parser/hci.c | 3322 +++++++++++++++++++++++++++++++++++++++++++++++++++ parser/hcrp.c | 116 ++ parser/hidp.c | 171 +++ parser/l2cap.c | 971 +++++++++++++++ parser/lmp.c | 1349 +++++++++++++++++++++ parser/obex.c | 309 +++++ parser/parser.c | 346 ++++++ parser/parser.h | 252 ++++ parser/ppp.c | 229 ++++ parser/rfcomm.c | 350 ++++++ parser/rfcomm.h | 494 ++++++++ parser/sdp.c | 808 +++++++++++++ parser/sdp.h | 162 +++ parser/tcpip.c | 149 +++ src/Makefile.am | 16 + src/bpasniff.c | 476 ++++++++ src/csrsniff.c | 282 +++++ src/hcidump.8 | 148 +++ src/hcidump.c | 1178 ++++++++++++++++++ src/magic.btsnoop | 12 + 41 files changed, 14882 insertions(+) create mode 100644 AUTHORS create mode 100755 Android.mk create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acinclude.m4 create mode 100755 bootstrap create mode 100755 bootstrap-configure create mode 100644 configure.in create mode 100644 parser/Makefile.am create mode 100644 parser/avctp.c create mode 100644 parser/avdtp.c create mode 100644 parser/bnep.c create mode 100644 parser/bpa.c create mode 100644 parser/capi.c create mode 100644 parser/cmtp.c create mode 100644 parser/csr.c create mode 100644 parser/ericsson.c create mode 100644 parser/hci.c create mode 100644 parser/hcrp.c create mode 100644 parser/hidp.c create mode 100644 parser/l2cap.c create mode 100644 parser/lmp.c create mode 100644 parser/obex.c create mode 100644 parser/parser.c create mode 100644 parser/parser.h create mode 100644 parser/ppp.c create mode 100644 parser/rfcomm.c create mode 100644 parser/rfcomm.h create mode 100644 parser/sdp.c create mode 100644 parser/sdp.h create mode 100644 parser/tcpip.c create mode 100644 src/Makefile.am create mode 100644 src/bpasniff.c create mode 100644 src/csrsniff.c create mode 100644 src/hcidump.8 create mode 100644 src/hcidump.c create mode 100644 src/magic.btsnoop diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..804e2f6 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +Maxim Krasnyansky +Marcel Holtmann +Wayne Lee +Ricky Yuen +Takashi Sasai diff --git a/Android.mk b/Android.mk new file mode 100755 index 0000000..47bb8fa --- /dev/null +++ b/Android.mk @@ -0,0 +1,44 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES:= \ + $(call include-path-for,bluez)/include/ \ + +LOCAL_CFLAGS:= \ + -DVERSION=\"1.42\" + +LOCAL_SRC_FILES:= \ + parser/avctp.c \ + parser/avdtp.c \ + parser/bnep.c \ + parser/bpa.c \ + parser/capi.c \ + parser/cmtp.c \ + parser/csr.c \ + parser/ericsson.c \ + parser/hci.c \ + parser/hcrp.c \ + parser/hidp.c \ + parser/l2cap.c \ + parser/lmp.c \ + parser/obex.c \ + parser/parser.c \ + parser/ppp.c \ + parser/rfcomm.c \ + parser/sdp.c \ + parser/tcpip.c \ + src/hcidump.c + +LOCAL_SHARED_LIBRARIES := \ + libbluetooth \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE:=hcidump + +include $(BUILD_EXECUTABLE) +endif +endif diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..198c46b --- /dev/null +++ b/ChangeLog @@ -0,0 +1,282 @@ +ver 1.42: + Decode the Read Link Policy Settings command. + Decode Default Link Policy Settings commands + Enable PIE by default if supported. + Don't optimize when debug is enabled. + +ver 1.41: + Improve decoding of HCI connection link type. + Fix handling of unsupported L2CAP config options. + Add full decoding of L2CAP RFC config option. + Reset to L2CAP basic mode when MTU config request is sent. + +ver 1.40: + Add HCI and LMP decoding support for Simple Pairing. + + Note: + This version needs at least bluez-libs-3.14 + +ver 1.39: + Add support for daemon discover protocol. + Fix device disconnect handling. + +ver 1.38: + Add support for daemon mode. + +ver 1.37: + Add decoding for SCO packet flags. + Add decoding for more extended inquiry features. + Add missing HCI command and event decodings. + + Note: + This version needs at least bluez-libs-3.12 + +ver 1.36: + Add missing HCI command definitions. + Add missing HCI event definitions. + Add missing HCI error definitions. + +ver 1.35: + Add partial decoding for eSCO setup. + Fix HCI command array size. + +ver 1.34: + Add reading support for MacOS X packet logger format. + Add decoding for AFH host channel classification command. + Add decoding for QoS setup command. + +ver 1.33: + Add support for IPv6 decoding. + Add IPv6 connection support. + +ver 1.32: + Add decoding for link supervision timeout change event. + Add decoding for host controller to host flow control setting. + Add decoding for host number of completed packets. + Add decoding for host buffer size command. + Fix OBEX header parsing. + + Note: + This version needs at least bluez-libs-3.3 + +ver 1.31: + Add Ericsson LMP decoding support. + Update sniff subrating decoding. + Update UUID and attribute definitions. + + Note: + This version needs at least bluez-libs-3.0 + +ver 1.30: + Add decoding for pause encryption feature. + Add generic RFCOMM streaming helpers. + Add HDLC decoding support to the PPP parser. + Add unslip support and basic protocol decoding. + Add PPP extraction support. + Move TCP/IP decoders into a separate file. + Fix DoS problem of the L2CAP parser. + +ver 1.29: + Obfuscate the BD_ADDR if no vendor events are requested. + Decode and display binary strings correctly. + Allow null-terminated text strings. + Add definitions for Apple Agent. + Add skeleton for AVCTP parser. + Add skeleton for PPP parser. + +ver 1.28: + Add missing UUID definitions and translations. + Add option to disable vendor commands and events. + Add decoding for hold mode, sniff mode and park state. + Add decoding for sniff subrate command complete event. + Decode class of device and BD_ADDR for inquiry filter. + Make it possible to obfuscate pin codes and link keys. + + Note: + This version needs at least bluez-libs-2.23 + +ver 1.27: + Decode the extended inquiry response payload. + Update HCI_DEV_NONE frame format. + Update the CSR PS key handling. + Add another bunch LMP message decodings. + Add compile time buffer checks (FORTIFY SOURCE). + +ver 1.26: + Fix memory leak with lmp_vertostr() function. + Fix FHS packet decoding for slave connections. + Add support for decoding more LMP messages. + Display the data of extended inquiry results. + Correct HCI command and event string arrays. + Add SCO audio extraction support. + +ver 1.25: + Add new attribute identifiers and UUID definitions. + Add decoding support for extended inquiry response. + Add more detailed BCCMD decoding. + + Note: + This version needs at least bluez-libs-2.20 + +ver 1.24: + Allow selection of system device. + Handle system events and commands. + +ver 1.23: + Add decoding support for inquiry scan type. + Fix connection accept timeout and scan enable decoding. + Show human readable timestamps only in verbose decoding mode. + Dump raw BNEP payload only in non-verbose decoding mode. + Update CSR BCCMD support and correct uint32 decoding. + + Note: + This version needs at least bluez-libs-2.18 + +ver 1.22: + Only do verbose decoding when requested. + Fix number of completed packets decoding. + Improve the decoding of CSR vendor commands and events. + Use human readable timestamps. + Add support for the BTSnoop file format. + +ver 1.21: + Clear L2CAP states when receiving the HCI disconnect. + Make the OBEX parser aware of that RFCOMM is a stream. + Update HCI verbose decoding routines. + Add CSR verbose decoding support. + + Note: + This version needs at least bluez-libs-2.17 + +ver 1.20: + Workaround for inquiry results with RSSI and page scan mode. + Decode almost every used HCI commands and events. + + Note: + This version needs at least bluez-libs-2.16 + +ver 1.19: + Fix error message decoding. + Add IAC decoding support. + Add LMP parser support. + Add BCCMD decoding support. + Add L2CAP retransmission and flow control decoding. + +ver 1.18: + Fix declaration of the SDP data structures. + Extend the HCI verbose decoding support. + +ver 1.17: + Add support for HCI verbose decoding. + Add support for L2CAP verbose decoding. + Add first version of the BPA sniffer utility. + + Note: + This version needs at least bluez-libs-2.15 + +ver 1.16: + Add UUID translation for WAP. + Fix display of UUID 128. + Fix display of L2CAP config hint bit and QoS option. + Fix parsing of SDP continuation state. + +ver 1.15: + Update the L2CAP channel counting routine. + Fix SBC codec specific decoding. + Fix AVDTP signal channel header decoding. + Add decoding for the AVDTP media channel header. + +ver 1.14: + Count the number of L2CAP channels per PSM. + Differ between the AVDTP signal and media channel. + Add full decoding of the AVDTP signal channel information. + +ver 1.13: + Update many UUID to text translations. + Add support for sending and receiving dumps over TCP. + Add support for the vendor packets of the Digianswer BPA. + + Note: + This version needs at least bluez-libs-2.11 + +ver 1.12: + Fix whitespace stuff for extended dump. + Add support for dynamic RFCOMM channels. + Add initial OBEX parser support. + +ver 1.11: + Trace company id from the chip manufacturer. + Support extended dump functionality. + Use the L2CAP_CONF_RFC_MODE value. + Use bt_get_unaligned() for unaligned access. + Make compiling with debug information optional. + Don't override CFLAGS from configure. + + Note: + This version needs at least bluez-libs-2.10 + +ver 1.10: + Fix display of L2CAP CID. + Show L2CAP config values only when they are present. + Decode L2CAP information request and response. + Update autoconf and automake routines. + +ver 1.9: + Initial AVDTP parser support. + Various minor fixes and cleanups. + +ver 1.8: + Support Bluetooth 1.2 HCI commands and events. + Decode RFCOMM credit based flow control. + +ver 1.7: + HCRP parser support. + CAPI parser support. + CMTP reassembly support. + Support for dynamic PSM. + +ver 1.6: + HIDP parser support. + Various fixes for the SDP parser. + +ver 1.5: + Included man page for hcidump. + Minor fix for HCI grabber. + +ver 1.4: + CMTP parser support. + Various fixes for SDP, RFCOMM and BNEP parsers. + +ver 1.3: + RFCOMM and BNEP parser fixes. + Use getopt_long instead of argp. + Added --psm option. + Automake build environment. + Other minor fixes. + +ver 1.2: + BNEP support. + Correct filter initialization. + Minor fixes. + +ver 1.1: + SCO support. + Support for frame time stamps. + RAW mode and other minor fixes. + +ver 1.0: + L2CAP fragment reassembly. + RFCOMM parser (Wayne Lee). + SDP parser (Ricky Yuen). + Simple filtering. + +ver 0.2: + L2CAP parsing. PSM tracking. + HCI events and commands parsing. + Packet parser moved to unified library (libparser). + HCI socket filter support. + Improved command line option parsing. + Improved output format. + +ver 0.1: + Initial implementation. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..56b077d --- /dev/null +++ b/INSTALL @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PREFIX', the package will +use PREFIX as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..ee3bba3 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = parser src + +MAINTAINERCLEANFILES = Makefile.in \ + aclocal.m4 configure config.h.in \ + depcomp missing install-sh mkinstalldirs diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..46b411e --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +BlueZ - Bluetooth protocol stack for Linux +****************************************** + +Copyright (C) 2000-2002 Maxim Krasnyansky +Copyright (C) 2003-2007 Marcel Holtmann + +Bluetooth packet analyzer + + +Compilation and installation +============================ + +In order to compile Bluetooth analyzer you need following software packages: + - Linux Bluetooth protocol stack (BlueZ) + - GCC compiler + +To configure run: + ./configure --prefix=/usr --mandir=/usr/share/man + +Configure automatically searches for all required components and packages. + +To compile and install run: + make && make install + + +Information +=========== + +Mailing lists: + bluez-users@lists.sf.net - BlueZ general questions and discussions + bluez-devel@lists.sf.net - BlueZ development + +For additional information about the project visit BlueZ web site: + http://www.bluez.org diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..fa328e4 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,82 @@ +AC_DEFUN([AC_PROG_CC_PIE], [ + AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ + echo 'void f(){}' > conftest.c + if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then + ac_cv_prog_cc_pie=yes + else + ac_cv_prog_cc_pie=no + fi + rm -rf conftest* + ]) +]) + +AC_DEFUN([AC_INIT_BLUEZ], [ + AC_PREFIX_DEFAULT(/usr/local) + + if (test "${CFLAGS}" = ""); then + CFLAGS="-Wall -O2" + fi + + if (test "${prefix}" = "NONE"); then + dnl no prefix and no sysconfdir, so default to /etc + if (test "$sysconfdir" = '${prefix}/etc'); then + AC_SUBST([sysconfdir], ['/etc']) + fi + + dnl no prefix and no mandir, so use ${prefix}/share/man as default + if (test "$mandir" = '${prefix}/man'); then + AC_SUBST([mandir], ['${prefix}/share/man']) + fi + + prefix="${ac_default_prefix}" + fi + + if (test "${libdir}" = '${exec_prefix}/lib'); then + libdir="${prefix}/lib" + fi + + if (test "$sysconfdir" = '${prefix}/etc'); then + configdir="${prefix}/etc/bluetooth" + else + configdir="${sysconfdir}/bluetooth" + fi + + AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}", [Directory for the configuration files]) +]) + +AC_DEFUN([AC_PATH_BLUEZ], [ + PKG_CHECK_MODULES(BLUEZ, bluez, dummy=yes, AC_MSG_ERROR(Bluetooth library is required)) + AC_SUBST(BLUEZ_CFLAGS) + AC_SUBST(BLUEZ_LIBS) +]) + +AC_DEFUN([AC_ARG_BLUEZ], [ + debug_enable=no + fortify_enable=yes + pie_enable=yes + + AC_ARG_ENABLE(fortify, AC_HELP_STRING([--disable-fortify], [disable compile time buffer checks]), [ + fortify_enable=${enableval} + ]) + + AC_ARG_ENABLE(pie, AC_HELP_STRING([--disable-pie], [enable position independent executables flag]), [ + pie_enable=${enableval} + ]) + + AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [ + debug_enable=${enableval} + ]) + + if (test "${fortify_enable}" = "yes"); then + CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" + fi + + if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then + CFLAGS="$CFLAGS -fPIE" + LDFLAGS="$LDFLAGS -pie" + fi + + if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then + CFLAGS="$CFLAGS -g -O0" + fi +]) diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..d25a117 --- /dev/null +++ b/bootstrap @@ -0,0 +1,6 @@ +#!/bin/sh + +aclocal && \ + autoheader && \ + automake --add-missing --copy && \ + autoconf diff --git a/bootstrap-configure b/bootstrap-configure new file mode 100755 index 0000000..44e7ee8 --- /dev/null +++ b/bootstrap-configure @@ -0,0 +1,11 @@ +#!/bin/sh + +if [ -f config.status ]; then + make maintainer-clean +fi + +./bootstrap && \ + ./configure --enable-maintainer-mode \ + --enable-debug \ + --prefix=/usr \ + --mandir=/usr/share/man diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..963e201 --- /dev/null +++ b/configure.in @@ -0,0 +1,22 @@ +AC_PREREQ(2.50) +AC_INIT() + +AM_INIT_AUTOMAKE(bluez-hcidump, 1.42) +AM_CONFIG_HEADER(config.h) + +AM_MAINTAINER_MODE + +AC_INIT_BLUEZ + +AC_LANG_C + +AC_PROG_CC +AC_PROG_CC_PIE +AC_PROG_INSTALL +AC_PROG_RANLIB + +AC_PATH_BLUEZ + +AC_ARG_BLUEZ + +AC_OUTPUT(Makefile src/Makefile parser/Makefile) diff --git a/parser/Makefile.am b/parser/Makefile.am new file mode 100644 index 0000000..cfaa048 --- /dev/null +++ b/parser/Makefile.am @@ -0,0 +1,12 @@ + +noinst_LIBRARIES = libparser.a + +libparser_a_SOURCES = \ + parser.c parser.h lmp.c hci.c \ + l2cap.c sdp.c sdp.h rfcomm.c rfcomm.h \ + bnep.c cmtp.c hidp.c hcrp.c avdtp.c avctp.c \ + obex.c capi.c ppp.c tcpip.c ericsson.c csr.c bpa.c + +AM_CFLAGS = @BLUEZ_CFLAGS@ + +MAINTAINERCLEANFILES = Makefile.in diff --git a/parser/avctp.c b/parser/avctp.c new file mode 100644 index 0000000..d9eb4f1 --- /dev/null +++ b/parser/avctp.c @@ -0,0 +1,45 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +void avctp_dump(int level, struct frame *frm) +{ + p_indent(level, frm); + printf("AVCTP:\n"); + + raw_dump(level, frm); +} diff --git a/parser/avdtp.c b/parser/avdtp.c new file mode 100644 index 0000000..22c128a --- /dev/null +++ b/parser/avdtp.c @@ -0,0 +1,493 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +static char *si2str(uint8_t si) +{ + switch (si & 0x7f) { + case 0x01: + return "Discover"; + case 0x02: + return "Capabilities"; + case 0x03: + return "Set config"; + case 0x04: + return "Get config"; + case 0x05: + return "Reconfigure"; + case 0x06: + return "Open"; + case 0x07: + return "Start"; + case 0x08: + return "Close"; + case 0x09: + return "Suspend"; + case 0x0a: + return "Abort"; + case 0x0b: + return "Security"; + default: + return "Unknown"; + } +} + +static char *pt2str(uint8_t hdr) +{ + switch (hdr & 0x0c) { + case 0x00: + return "Single"; + case 0x04: + return "Start"; + case 0x08: + return "Cont"; + case 0x0c: + return "End"; + default: + return "Unk"; + } +} + +static char *mt2str(uint8_t hdr) +{ + switch (hdr & 0x03) { + case 0x00: + return "cmd"; + case 0x02: + return "rsp"; + case 0x03: + return "rej"; + default: + return "rfd"; + } +} + +static char *media2str(uint8_t type) +{ + switch (type) { + case 0: + return "Audio"; + case 1: + return "Video"; + case 2: + return "Multimedia"; + default: + return "Reserved"; + } +} + +static char *codec2str(uint8_t type, uint8_t codec) +{ + switch (type) { + case 0: + switch (codec) { + case 0: + return "SBC"; + case 1: + return "MPEG-1,2 Audio"; + case 2: + return "MPEG-2,4 AAC"; + case 4: + return "ATRAC family"; + case 255: + return "non-A2DP"; + default: + return "Reserved"; + } + break; + case 1: + switch (codec) { + case 1: + return "H.263 baseline"; + case 2: + return "MPEG-4 Visual Simple Profile"; + case 3: + return "H.263 profile 3"; + case 4: + return "H.263 profile 8"; + case 255: + return "Non-VDP"; + default: + return "Reserved"; + } + break; + } + return "Unknown"; +} + +static char *cat2str(uint8_t cat) +{ + switch (cat) { + case 1: + return "Media Transport"; + case 2: + return "Reporting"; + case 3: + return "Recovery"; + case 4: + return "Content Protection"; + case 5: + return "Header Compression"; + case 6: + return "Multiplexing"; + case 7: + return "Media Codec"; + default: + return "Reserved"; + } +} + +static void errorcode(int level, struct frame *frm) +{ + uint8_t code; + + p_indent(level, frm); + code = get_u8(frm); + printf("Error code %d\n", code); +} + +static void acp_seid(int level, struct frame *frm) +{ + uint8_t seid; + + p_indent(level, frm); + seid = get_u8(frm); + printf("ACP SEID %d\n", seid >> 2); +} + +static void acp_int_seid(int level, struct frame *frm) +{ + uint8_t acp_seid, int_seid; + + p_indent(level, frm); + acp_seid = get_u8(frm); + int_seid = get_u8(frm); + printf("ACP SEID %d - INT SEID %d\n", acp_seid >> 2, int_seid >> 2); +} + +static void capabilities(int level, struct frame *frm) +{ + uint8_t cat, len; + + while (frm->len > 1) { + p_indent(level, frm); + cat = get_u8(frm); + len = get_u8(frm); + + if (cat == 7) { + uint8_t type, codec, tmp; + + type = get_u8(frm); + codec = get_u8(frm); + + printf("%s - %s\n", cat2str(cat), codec2str(type, codec)); + + switch (codec) { + case 0: + tmp = get_u8(frm); + p_indent(level + 1, frm); + if (tmp & 0x80) + printf("16kHz "); + if (tmp & 0x40) + printf("32kHz "); + if (tmp & 0x20) + printf("44.1kHz "); + if (tmp & 0x10) + printf("48kHz "); + printf("\n"); + p_indent(level + 1, frm); + if (tmp & 0x08) + printf("Mono "); + if (tmp & 0x04) + printf("DualChannel "); + if (tmp & 0x02) + printf("Stereo "); + if (tmp & 0x01) + printf("JointStereo "); + printf("\n"); + tmp = get_u8(frm); + p_indent(level + 1, frm); + if (tmp & 0x80) + printf("4 "); + if (tmp & 0x40) + printf("8 "); + if (tmp & 0x20) + printf("12 "); + if (tmp & 0x10) + printf("16 "); + printf("Blocks\n"); + p_indent(level + 1, frm); + if (tmp & 0x08) + printf("4 "); + if (tmp & 0x04) + printf("8 "); + printf("Subbands\n"); + p_indent(level + 1, frm); + if (tmp & 0x02) + printf("SNR "); + if (tmp & 0x01) + printf("Loudness "); + printf("\n"); + tmp = get_u8(frm); + p_indent(level + 1, frm); + printf("Bitpool Range %d-%d\n", tmp, get_u8(frm)); + break; + default: + hex_dump(level + 1, frm, len - 2); + frm->ptr += (len - 2); + frm->len -= (len - 2); + break; + } + } else { + printf("%s\n", cat2str(cat)); + hex_dump(level + 1, frm, len); + + frm->ptr += len; + frm->len -= len; + } + } +} + +static inline void discover(int level, uint8_t hdr, struct frame *frm) +{ + uint8_t seid, type; + + switch (hdr & 0x03) { + case 0x02: + while (frm->len > 1) { + p_indent(level, frm); + seid = get_u8(frm); + type = get_u8(frm); + printf("ACP SEID %d - %s %s%s\n", + seid >> 2, media2str(type >> 4), + type & 0x08 ? "Sink" : "Source", + seid & 0x02 ? " (InUse)" : ""); + } + break; + case 0x03: + errorcode(level, frm); + break; + } +} + +static inline void get_capabilities(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + break; + case 0x02: + capabilities(level, frm); + break; + case 0x03: + errorcode(level, frm); + break; + } +} + +static inline void set_configuration(int level, uint8_t hdr, struct frame *frm) +{ + uint8_t cat; + + switch (hdr & 0x03) { + case 0x00: + acp_int_seid(level, frm); + capabilities(level, frm); + break; + case 0x03: + p_indent(level, frm); + cat = get_u8(frm); + printf("%s\n", cat2str(cat)); + errorcode(level, frm); + break; + } +} + +static inline void get_configuration(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + case 0x02: + capabilities(level, frm); + break; + case 0x03: + errorcode(level, frm); + break; + } +} + +static inline void reconfigure(int level, uint8_t hdr, struct frame *frm) +{ + uint8_t cat; + + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + capabilities(level, frm); + break; + case 0x03: + p_indent(level, frm); + cat = get_u8(frm); + printf("%s\n", cat2str(cat)); + errorcode(level, frm); + break; + } +} + +static inline void open_close_stream(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + break; + case 0x03: + errorcode(level, frm); + break; + } +} + +static inline void start_suspend_stream(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + while (frm->len > 0) + acp_seid(level, frm); + break; + case 0x03: + acp_seid(level, frm); + errorcode(level, frm); + break; + } +} + +static inline void abort_streaming(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + break; + } +} + +static inline void security(int level, uint8_t hdr, struct frame *frm) +{ + switch (hdr & 0x03) { + case 0x00: + acp_seid(level, frm); + case 0x02: + hex_dump(level + 1, frm, frm->len); + frm->ptr += frm->len; + frm->len = 0; + break; + case 0x03: + errorcode(level, frm); + break; + } +} + +void avdtp_dump(int level, struct frame *frm) +{ + uint8_t hdr, sid, nsp, type; + uint16_t seqn; + uint32_t time, ssrc; + + switch (frm->num) { + case 1: + p_indent(level, frm); + hdr = get_u8(frm); + + nsp = (hdr & 0x0c) == 0x04 ? get_u8(frm) : 0; + sid = hdr & 0x08 ? 0x00 : get_u8(frm); + + printf("AVDTP(s): %s %s: transaction %d\n", + hdr & 0x08 ? pt2str(hdr) : si2str(sid), mt2str(hdr), hdr >> 4); + + switch (sid & 0x7f) { + case 0x01: + discover(level + 1, hdr, frm); + break; + case 0x02: + get_capabilities(level + 1, hdr, frm); + break; + case 0x03: + set_configuration(level + 1, hdr, frm); + break; + case 0x04: + get_configuration(level + 1, hdr, frm); + break; + case 0x05: + reconfigure(level + 1, hdr, frm); + break; + case 0x06: + open_close_stream(level + 1, hdr, frm); + break; + case 0x07: + start_suspend_stream(level + 1, hdr, frm); + break; + case 0x08: + open_close_stream(level + 1, hdr, frm); + break; + case 0x09: + start_suspend_stream(level + 1, hdr, frm); + break; + case 0x0a: + abort_streaming(level + 1, hdr, frm); + break; + case 0x0b: + security(level + 1, hdr, frm); + break; + } + + break; + + case 2: + p_indent(level, frm); + hdr = get_u8(frm); + type = get_u8(frm); + seqn = get_u16(frm); + time = get_u32(frm); + ssrc = get_u32(frm); + + printf("AVDTP(m): ver %d %s%scc %d %spt %d seqn %d time %d ssrc %d\n", + hdr >> 6, hdr & 0x20 ? "pad " : "", hdr & 0x10 ? "ext " : "", + hdr & 0xf, type & 0x80 ? "mark " : "", type & 0x7f, seqn, time, ssrc); + break; + } + + raw_dump(level, frm); +} diff --git a/parser/bnep.c b/parser/bnep.c new file mode 100644 index 0000000..4ab5aa4 --- /dev/null +++ b/parser/bnep.c @@ -0,0 +1,318 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2003 Takashi Sasai + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "parser.h" + +/* BNEP Type */ +#define BNEP_GENERAL_ETHERNET 0x00 +#define BNEP_CONTROL 0x01 +#define BNEP_COMPRESSED_ETHERNET 0x02 +#define BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY 0x03 +#define BNEP_COMPRESSED_ETHERNET_DEST_ONLY 0x04 + +/* BNEP Control Packet Type */ +#define BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD 0x00 +#define BNEP_SETUP_CONNECTION_REQUEST_MSG 0x01 +#define BNEP_SETUP_CONNECTION_RESPONSE_MSG 0x02 +#define BNEP_FILTER_NET_TYPE_SET_MSG 0x03 +#define BNEP_FILTER_NET_TYPE_RESPONSE_MSG 0x04 +#define BNEP_FILTER_MULT_ADDR_SET_MSG 0x05 +#define BNEP_FILTER_MULT_ADDR_RESPONSE_MSG 0x06 + +/* BNEP Extension Type */ +#define BNEP_EXTENSION_CONTROL 0x00 + +#ifndef ETHERTYPE_IPV6 +#define ETHERTYPE_IPV6 ETH_P_IPV6 +#endif + +static char *get_macaddr(struct frame *frm) +{ + static char str[20]; + unsigned char *buf = frm->ptr; + + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + + frm->ptr += 6; + frm->len -= 6; + + return str; +} + +static void bnep_control(int level, struct frame *frm, int header_length) +{ + uint8_t uuid_size; + int i, length; + char *s; + uint32_t uuid = 0; + uint8_t type = get_u8(frm); + + p_indent(++level, frm); + switch (type) { + case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: + printf("Not Understood(0x%02x) type 0x%02x\n", type, get_u8(frm)); + break; + + case BNEP_SETUP_CONNECTION_REQUEST_MSG: + uuid_size = get_u8(frm); + printf("Setup Req(0x%02x) size 0x%02x ", type, uuid_size); + switch (uuid_size) { + case 2: + uuid = get_u16(frm); + printf("dst 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + uuid = get_u16(frm); + printf(" src 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + printf("\n"); + break; + case 4: + uuid = get_u32(frm); + printf("dst 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + uuid = get_u32(frm); + printf(" src 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + printf("\n"); + break; + case 16: + uuid = get_u32(frm); + printf("dst 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + frm->ptr += 12; + frm->len -= 12; + uuid = get_u32(frm); + printf(" src 0x%x", uuid); + if ((s = get_uuid_name(uuid)) != 0) + printf("(%s)", s); + printf("\n"); + frm->ptr += 12; + frm->len -= 12; + break; + default: + frm->ptr += (uuid_size * 2); + frm->len -= (uuid_size * 2); + break; + } + break; + + case BNEP_SETUP_CONNECTION_RESPONSE_MSG: + printf("Setup Rsp(0x%02x) res 0x%04x\n", + type, get_u16(frm)); + break; + + case BNEP_FILTER_NET_TYPE_SET_MSG: + length = get_u16(frm); + printf("Filter NetType Set(0x%02x) len 0x%04x\n", + type, length); + for (i = 0; i < length / 4; i++) { + p_indent(level + 1, frm); + printf("0x%04x - ", get_u16(frm)); + printf("0x%04x\n", get_u16(frm)); + } + break; + + case BNEP_FILTER_NET_TYPE_RESPONSE_MSG: + printf("Filter NetType Rsp(0x%02x) res 0x%04x\n", + type, get_u16(frm)); + break; + + case BNEP_FILTER_MULT_ADDR_SET_MSG: + length = get_u16(frm); + printf("Filter MultAddr Set(0x%02x) len 0x%04x\n", + type, length); + for (i = 0; i < length / 12; i++) { + p_indent(level + 1, frm); + printf("%s - ", get_macaddr(frm)); + printf("%s\n", get_macaddr(frm)); + } + break; + + case BNEP_FILTER_MULT_ADDR_RESPONSE_MSG: + printf("Filter MultAddr Rsp(0x%02x) res 0x%04x\n", + type, get_u16(frm)); + break; + + default: + printf("Unknown control type(0x%02x)\n", type); + raw_ndump(level + 1, frm, header_length - 1); + frm->ptr += header_length - 1; + frm->len -= header_length - 1; + return; + } +} + +static void bnep_eval_extension(int level, struct frame *frm) +{ + uint8_t type = get_u8(frm); + uint8_t length = get_u8(frm); + int extension = type & 0x80; + + p_indent(level, frm); + + switch (type & 0x7f) { + case BNEP_EXTENSION_CONTROL: + printf("Ext Control(0x%02x|%s) len 0x%02x\n", + type & 0x7f, extension ? "1" : "0", length); + bnep_control(level, frm, length); + break; + + default: + printf("Ext Unknown(0x%02x|%s) len 0x%02x\n", + type & 0x7f, extension ? "1" : "0", length); + raw_ndump(level + 1, frm, length); + frm->ptr += length; + frm->len -= length; + } + + if (extension) + bnep_eval_extension(level, frm); +} + +void bnep_dump(int level, struct frame *frm) +{ + uint8_t type = get_u8(frm); + uint16_t proto = 0x0000; + int extension = type & 0x80; + + p_indent(level, frm); + + switch (type & 0x7f) { + case BNEP_CONTROL: + printf("BNEP: Control(0x%02x|%s)\n", + type & 0x7f, extension ? "1" : "0"); + bnep_control(level, frm, -1); + break; + + case BNEP_COMPRESSED_ETHERNET: + printf("BNEP: Compressed(0x%02x|%s)\n", + type & 0x7f, extension ? "1" : "0"); + p_indent(++level, frm); + proto = get_u16(frm); + printf("[proto 0x%04x]\n", proto); + break; + + case BNEP_GENERAL_ETHERNET: + printf("BNEP: General ethernet(0x%02x|%s)\n", + type & 0x7f, extension ? "1" : "0"); + p_indent(++level, frm); + printf("dst %s ", get_macaddr(frm)); + printf("src %s ", get_macaddr(frm)); + proto = get_u16(frm); + printf("[proto 0x%04x]\n", proto); + break; + + case BNEP_COMPRESSED_ETHERNET_DEST_ONLY: + printf("BNEP: Compressed DestOnly(0x%02x|%s)\n", + type & 0x7f, extension ? "1" : "0"); + p_indent(++level, frm); + printf("dst %s ", get_macaddr(frm)); + proto = get_u16(frm); + printf("[proto 0x%04x]\n", proto); + break; + + case BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY: + printf("BNEP: Compressed SrcOnly(0x%02x|%s)\n", + type & 0x7f, extension ? "1" : "0"); + p_indent(++level, frm); + printf("src %s ", get_macaddr(frm)); + proto = get_u16(frm); + printf("[proto 0x%04x]\n", proto); + break; + + default: + printf("(Unknown packet type)\n"); + return; + } + + /* Extension info */ + if (extension) + bnep_eval_extension(++level, frm); + + /* Control packet => No payload info */ + if ((type & 0x7f) == BNEP_CONTROL) + return; + + /* 802.1p header */ + if (proto == 0x8100) { + p_indent(level, frm); + printf("802.1p Header: 0x%04x ", get_u16(frm)); + proto = get_u16(frm); + printf("[proto 0x%04x]\n", proto); + } + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level, frm); + return; + } + + switch (proto) { + case ETHERTYPE_ARP: + p_indent(++level, frm); + printf("ARP: "); + arp_dump(level, frm); + break; + + case ETHERTYPE_REVARP: + p_indent(++level, frm); + printf("RARP: "); + arp_dump(level, frm); + break; + + case ETHERTYPE_IP: + p_indent(++level, frm); + printf("IP: "); + ip_dump(level, frm); + break; + + case ETHERTYPE_IPV6: + p_indent(++level, frm); + printf("IPV6: "); + ip_dump(level, frm); + break; + + default: + raw_dump(level, frm); + break; + } +} diff --git a/parser/bpa.c b/parser/bpa.c new file mode 100644 index 0000000..fa7974c --- /dev/null +++ b/parser/bpa.c @@ -0,0 +1,65 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "parser.h" + +#define BPA_U8(frm) (get_u8(frm)) +#define BPA_U16(frm) (btohs(htons(get_u16(frm)))) +#define BPA_U32(frm) (btohl(htonl(get_u32(frm)))) + +void bpa_dump(int level, struct frame *frm) +{ + uint8_t id, status, channel; + uint16_t num, len; + uint32_t time; + + id = get_u8(frm); + num = get_u16(frm); + len = BPA_U16(frm); + + status = get_u8(frm); + time = get_u32(frm); + channel = get_u8(frm); + + p_indent(level, frm); + printf("BPA: id %d num %d status 0x%02x time %d channel %d\n", + id, num, status, time, channel); + + raw_dump(level, frm); +} diff --git a/parser/capi.c b/parser/capi.c new file mode 100644 index 0000000..36d57aa --- /dev/null +++ b/parser/capi.c @@ -0,0 +1,849 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "parser.h" + +#define CAPI_U8(frm) (get_u8(frm)) +#define CAPI_U16(frm) (btohs(htons(get_u16(frm)))) +#define CAPI_U32(frm) (btohl(htonl(get_u32(frm)))) + +static char *cmd2str(uint8_t cmd) +{ + switch (cmd) { + case 0x01: + return "ALERT"; + case 0x02: + return "CONNECT"; + case 0x03: + return "CONNECT_ACTIVE"; + case 0x04: + return "DISCONNECT"; + case 0x05: + return "LISTEN"; + case 0x08: + return "INFO"; + case 0x20: + return "INTEROPERABILITY"; + case 0x41: + return "SELECT_B_PROTOCOL"; + case 0x80: + return "FACILITY"; + case 0x82: + return "CONNECT_B3"; + case 0x83: + return "CONNECT_B3_ACTIVE"; + case 0x84: + return "DISCONNECT_B3"; + case 0x86: + return "DATA_B3"; + case 0x87: + return "RESET_B3"; + case 0x88: + return "CONNECT_B3_T90_ACTIVE"; + case 0xff: + return "MANUFACTURER"; + default: + return "UNKNOWN"; + } +} + +static char *subcmd2str(uint8_t subcmd) +{ + switch (subcmd) { + case 0x80: + return "REQ"; + case 0x81: + return "CONF"; + case 0x82: + return "IND"; + case 0x83: + return "RESP"; + default: + return "UNKN"; + } +} + +static char *interopsel2str(uint16_t sel) +{ + switch (sel) { + case 0x0000: + return "USB Device Management"; + case 0x0001: + return "Bluetooth Device Management"; + default: + return "Unknown"; + } +} + +static char *func2str(uint16_t func) +{ + switch (func) { + case 0: + return "Register"; + case 1: + return "Release"; + case 2: + return "Get_Profile"; + case 3: + return "Get_Manufacturer"; + case 4: + return "Get_Version"; + case 5: + return "Get_Serial_Number"; + case 6: + return "Manufacturer"; + case 7: + return "Echo_Loopback"; + default: + return "Unknown"; + } +} + +static char *facilitysel2str(uint16_t sel) +{ + switch (sel) { + case 0x0000: + return "Handset"; + case 0x0001: + return "DTMF"; + case 0x0002: + return "V.42 bis"; + case 0x0003: + return "Supplementary Services"; + case 0x0004: + return "Power management wakeup"; + case 0x0005: + return "Line Interconnect"; + case 0x0006: + return "DTMF"; + default: + return "Unknown"; + } +} + +static char *info2str(uint16_t info) +{ + switch (info) { + case 0x0000: + return "No error"; + case 0x0001: + return "NCPI not supported by current protocol, NCPI ignored"; + case 0x0002: + return "Flags not supported by current protocol, flags ignored"; + case 0x2001: + return "Message not supported in current state"; + case 0x2002: + return "Incorrect Controller/PLCI/NCCI"; + case 0x2003: + return "No PLCI available"; + case 0x2004: + return "No NCCI available"; + case 0x2005: + return "No Listen resources available"; + case 0x2007: + return "Illegal message parameter coding"; + case 0x2008: + return "No interconnection resources available"; + case 0x3001: + return "B1 protocol not supported"; + case 0x3002: + return "B2 protocol not supported"; + case 0x3003: + return "B3 protocol not supported"; + case 0x3004: + return "B1 protocol parameter not supported"; + case 0x3005: + return "B2 protocol parameter not supported"; + case 0x3006: + return "B3 protocol parameter not supported"; + case 0x3007: + return "B protocol combination not supported"; + case 0x3008: + return "NCPI not supported"; + case 0x3009: + return "CIP Value unknown"; + case 0x300A: + return "Flags not supported (reserved bits)"; + case 0x300B: + return "Facility not supported"; + case 0x300C: + return "Data length not supported by current protocol"; + case 0x300D: + return "Reset procedure not supported by current protocol"; + case 0x300F: + return "Unsupported interoperability"; + case 0x3011: + return "Facility specific function not supported"; + case 0x3301: + return "Protocol error, Layer 1"; + case 0x3302: + return "Protocol error, Layer 2"; + case 0x3303: + return "Protocol error, Layer 3"; + case 0x3304: + return "Another application got that call"; + case 0x3305: + return "Cleared by Call Control Supervision"; + case 0x3400: + /* The cause value received from the network in a cause + * information element (Octet 4) is indicated in the field 00 */ + return "Disconnect cause from the network in accordance with Q.850/ETS 300 102-1"; + default: + return "Unknown"; + } +} + +static void profile(int level, struct frame *frm) +{ + uint16_t nctr, nchn; + uint32_t value; + + nctr = CAPI_U16(frm); + nchn = CAPI_U16(frm); + + if (nchn > 0) { + p_indent(level, frm); + printf("Controller: %d\n", nctr); + p_indent(level, frm); + printf("Number of B-channels: %d\n", nchn); + + value = CAPI_U32(frm); + p_indent(level, frm); + printf("Global options: 0x%04x\n", value); + value = CAPI_U32(frm); + p_indent(level, frm); + printf("B1 protocol support: 0x%08x\n", value); + value = CAPI_U32(frm); + p_indent(level, frm); + printf("B2 protocol support: 0x%08x\n", value); + value = CAPI_U32(frm); + p_indent(level, frm); + printf("B3 protocol support: 0x%08x\n", value); + + frm->ptr += 24; + frm->len -= 24; + + p_indent(level, frm); + printf("Manufacturer-specific information:\n"); + hex_dump(level, frm, 20); + } else { + p_indent(level, frm); + printf("Number of controllers: %d\n", nctr); + } +} + +static void cmd_common(int level, uint8_t subcmd, struct frame *frm) +{ + uint32_t val; + uint16_t info, ncci; + uint8_t ctr, plci; + + val = CAPI_U32(frm); + ctr = val & 0xff; + plci = (val & 0xff00) >> 8; + ncci = (val & 0xffff0000) >> 16; + + p_indent(level, frm); + printf("Controller: %d %s\n", ctr & 0x7f, ctr & 0x80 ? "Ext." : "Int."); + + if (plci > 0) { + p_indent(level, frm); + printf("PLCI: 0x%02x\n", plci); + } + + if (ncci > 0) { + p_indent(level, frm); + printf("NCCI: 0x%04x\n", ncci); + } + + if (subcmd == 0x81) { + info = CAPI_U16(frm); + p_indent(level, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + } +} + +static void cmd_alert(int level, uint8_t subcmd, struct frame *frm) +{ + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x80) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Additional info:\n"); + hex_dump(level, frm, len); + } + } +} + +static void cmd_connect(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t cip; + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x81) + return; + + cip = CAPI_U16(frm); + p_indent(level, frm); + printf("CIP value: 0x%04x\n", cip); + + len = CAPI_U8(frm); + frm->ptr += len; + frm->len -= len; + len = CAPI_U8(frm); + frm->ptr += len; + frm->len -= len; + len = CAPI_U8(frm); + frm->ptr += len; + frm->len -= len; + len = CAPI_U8(frm); + frm->ptr += len; + frm->len -= len; + + raw_dump(level, frm); +} + +static void cmd_disconnect(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t reason; + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x80) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Additional info:\n"); + hex_dump(level, frm, len); + } + } + + if (subcmd == 0x82) { + reason = CAPI_U16(frm); + p_indent(level, frm); + printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); + } +} + +static void cmd_connect_active(int level, uint8_t subcmd, struct frame *frm) +{ + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x82) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Connected number:\n"); + hex_dump(level, frm, len); + } + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Connected subaddress:\n"); + hex_dump(level, frm, len); + } + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("LLC:\n"); + hex_dump(level, frm, len); + } + } +} + +static void cmd_listen(int level, uint8_t subcmd, struct frame *frm) +{ + uint32_t mask; + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x80) { + mask = CAPI_U32(frm); + p_indent(level, frm); + printf("Info mask: 0x%08x\n", mask); + + mask = CAPI_U32(frm); + p_indent(level, frm); + printf("CIP mask: 0x%08x", mask); + + mask = CAPI_U32(frm); + if (mask > 0) + printf(" 0x%08x\n", mask); + else + printf("\n"); + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Calling party number:\n"); + hex_dump(level, frm, len); + } + frm->ptr += len; + frm->len -= len; + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Calling party subaddress:\n"); + hex_dump(level, frm, len); + } + frm->ptr += len; + frm->len -= len; + } +} + +static void cmd_info(int level, uint8_t subcmd, struct frame *frm) +{ + uint8_t len; + uint16_t info; + + cmd_common(level, subcmd, frm); + + switch (subcmd) { + case 0x80: + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Called party number:\n"); + hex_dump(level, frm, len); + } + frm->ptr += len; + frm->len -= len; + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Additional info:\n"); + hex_dump(level, frm, len); + } + break; + + case 0x82: + info = CAPI_U16(frm); + p_indent(level, frm); + printf("Info number: %d\n", info); + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("Info element:\n"); + hex_dump(level, frm, len); + } + break; + } +} + +static void cmd_interoperability(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t sel, func, info; + uint16_t nconn, datablkcnt, datablklen; + uint32_t ctr, value, major, minor; + + info = (subcmd == 0x81) ? CAPI_U16(frm) : 0; + sel = CAPI_U16(frm); + CAPI_U8(frm); + if (subcmd != 0x83) { + func = CAPI_U16(frm); + CAPI_U8(frm); + } else + func = 0; + + p_indent(level, frm); + printf("Selector: 0x%04x (%s)\n", sel, interopsel2str(sel)); + + switch (sel) { + case 0x0001: + p_indent(level, frm); + printf("Function: %d (%s)\n", func, func2str(func)); + + switch (subcmd) { + case 0x80: + switch (func) { + case 0: + nconn = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("maxLogicalConnections: %d\n", nconn); + datablkcnt = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("maxBDataBlocks: %d\n", datablkcnt); + datablklen = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("maxBDataLen: %d\n", datablklen); + break; + case 2: + case 3: + case 4: + case 5: + ctr = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Controller: %d\n", ctr); + break; + default: + raw_dump(level + 1, frm); + break; + } + break; + + case 0x81: + switch (func) { + case 0: + case 1: + info = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + break; + case 2: + info = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + CAPI_U8(frm); + profile(level + 1, frm); + break; + case 3: + info = CAPI_U16(frm); + p_indent(level + 1, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + ctr = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Controller: %d\n", ctr); + CAPI_U8(frm); + p_indent(level + 1, frm); + printf("Identification: \"%s\"\n", (char *) frm->ptr); + break; + case 4: + value = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Return value: 0x%04x\n", value); + ctr = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Controller: %d\n", ctr); + p_indent(level + 1, frm); + major = CAPI_U32(frm); + minor = CAPI_U32(frm); + printf("CAPI: %d.%d\n", major, minor); + major = CAPI_U32(frm); + minor = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Manufacture: %u.%01x%01x-%02u (%d.%d)\n", + (major & 0xf0) >> 4, (major & 0x0f) << 4, + (minor & 0xf0) >> 4, minor & 0x0f, + major, minor); + break; + case 5: + value = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Return value: 0x%04x\n", value); + ctr = CAPI_U32(frm); + p_indent(level + 1, frm); + printf("Controller: %d\n", ctr); + CAPI_U8(frm); + p_indent(level + 1, frm); + printf("Serial number: %.7s\n", (char *) frm->ptr); + break; + default: + raw_dump(level + 1, frm); + break; + } + break; + + default: + raw_dump(level, frm); + break; + } + break; + + default: + p_indent(level, frm); + printf("Function: %d\n", func); + if (subcmd == 0x81) { + p_indent(level, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + } + raw_dump(level + 1, frm); + break; + } +} + +static void cmd_facility(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t sel; + + cmd_common(level, subcmd, frm); + + sel = CAPI_U16(frm); + CAPI_U8(frm); + + p_indent(level, frm); + printf("Selector: 0x%04x (%s)\n", sel, facilitysel2str(sel)); + + raw_dump(level, frm); +} + +static void cmd_connect_b3(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t reject; + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x81) + return; + + if (subcmd == 0x83) { + reject = CAPI_U16(frm); + p_indent(level, frm); + printf("Reject: 0x%04x (%s)\n", reject, info2str(reject)); + } + + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("NCPI:\n"); + hex_dump(level, frm, len); + } +} + +static void cmd_connect_b3_active(int level, uint8_t subcmd, struct frame *frm) +{ + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x82) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("NCPI:\n"); + hex_dump(level, frm, len); + } + } +} + +static void cmd_disconnect_b3(int level, uint8_t subcmd, struct frame *frm) +{ + uint16_t reason; + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x82) { + reason = CAPI_U16(frm); + p_indent(level, frm); + printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); + } + + if (subcmd == 0x80 || subcmd == 0x82) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("NCPI:\n"); + hex_dump(level, frm, len); + } + } +} + +static void cmd_data_b3(int level, uint8_t subcmd, struct frame *frm) +{ + uint32_t data; + uint64_t data64; + uint16_t length, handle, flags, info; + + cmd_common(level, 0x00, frm); + + if (subcmd == 0x81 || subcmd == 0x83) { + handle = CAPI_U16(frm); + p_indent(level, frm); + printf("Data handle: 0x%04x\n", handle); + + if (subcmd == 0x81) { + info = CAPI_U16(frm); + p_indent(level, frm); + printf("Info: 0x%04x (%s)\n", info, info2str(info)); + } + } else { + data = CAPI_U32(frm); + + length = CAPI_U16(frm); + p_indent(level, frm); + printf("Data length: 0x%04x (%d bytes)\n", length, length); + + handle = CAPI_U16(frm); + p_indent(level, frm); + printf("Data handle: 0x%04x\n", handle); + + flags = CAPI_U16(frm); + p_indent(level, frm); + printf("Flags: 0x%04x\n", flags); + + if (data == 0) + data64 = get_u64(frm); + + raw_dump(level, frm); + } +} + +static void cmd_reset_b3(int level, uint8_t subcmd, struct frame *frm) +{ + uint8_t len; + + cmd_common(level, subcmd, frm); + + if (subcmd == 0x80 || subcmd == 0x82) { + len = CAPI_U8(frm); + if (len > 0) { + p_indent(level, frm); + printf("NCPI:\n"); + hex_dump(level, frm, len); + } + } +} + +static void cmd_manufacturer(int level, uint8_t subcmd, struct frame *frm) +{ + uint32_t ctr, class, func; + uint16_t len; + unsigned char *id; + + ctr = CAPI_U32(frm); + p_indent(level, frm); + printf("Controller: %d\n", ctr); + + id = (unsigned char *) frm->ptr; + p_indent(level, frm); + if (isprint(id[0]) && isprint(id[1]) && isprint(id[2]) && isprint(id[3])) + printf("Manufacturer: %.4s", id); + else + printf("Manufacturer: 0x%02x 0x%02x 0x%02x 0x%02x", + id[0], id[1], id[2], id[3]); + frm->ptr += 4; + frm->len -= 4; + + if (!strncmp((char *) id, "AVM!", 4)) { + class = CAPI_U32(frm); + func = CAPI_U32(frm); + len = CAPI_U8(frm); + if (len == 0xff) + len = CAPI_U16(frm); + + printf(" [class %d func %d len %d]\n", class, func, len); + } else + printf("\n"); + + raw_dump(level, frm); +} + +void capi_dump(int level, struct frame *frm) +{ + uint16_t len, appl, msgnum; + uint8_t cmd, subcmd; + + len = CAPI_U16(frm) - 8; + appl = CAPI_U16(frm); + cmd = CAPI_U8(frm); + subcmd = CAPI_U8(frm); + msgnum = CAPI_U16(frm); + + p_indent(level, frm); + + printf("CAPI_%s_%s: appl %d msgnum %d len %d\n", + cmd2str(cmd), subcmd2str(subcmd), appl, msgnum, len); + + switch (cmd) { + case 0x01: + cmd_alert(level + 1, subcmd, frm); + break; + case 0x02: + cmd_connect(level + 1, subcmd, frm); + break; + case 0x03: + cmd_connect_active(level + 1, subcmd, frm); + break; + case 0x04: + cmd_disconnect(level + 1, subcmd, frm); + break; + case 0x05: + cmd_listen(level + 1, subcmd, frm); + break; + case 0x08: + cmd_info(level + 1, subcmd, frm); + break; + case 0x20: + cmd_interoperability(level + 1, subcmd, frm); + break; + case 0x80: + cmd_facility(level + 1, subcmd, frm); + break; + case 0x82: + cmd_connect_b3(level + 1, subcmd, frm); + break; + case 0x83: + case 0x88: + cmd_connect_b3_active(level + 1, subcmd, frm); + break; + case 0x84: + cmd_disconnect_b3(level + 1, subcmd, frm); + break; + case 0x86: + cmd_data_b3(level + 1, subcmd, frm); + break; + case 0x87: + cmd_reset_b3(level + 1, subcmd, frm); + break; + case 0xff: + cmd_manufacturer(level + 1, subcmd, frm); + break; + default: + raw_dump(level, frm); + frm->ptr += len; + frm->len -= len; + break; + } +} diff --git a/parser/cmtp.c b/parser/cmtp.c new file mode 100644 index 0000000..bdc2024 --- /dev/null +++ b/parser/cmtp.c @@ -0,0 +1,211 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +#define TABLE_SIZE 10 + +static struct { + uint16_t handle; + uint16_t cid; + struct frame msg[16]; +} table[TABLE_SIZE]; + +static void add_segment(uint8_t bid, struct frame *frm, int len) +{ + uint16_t handle = frm->handle, cid = frm->cid; + struct frame *msg; + void *data; + int i, pos = -1; + + if (bid > 15) + return; + + for (i = 0; i < TABLE_SIZE; i++) { + if (table[i].handle == handle && table[i].cid == cid) { + pos = i; + break; + } + + if (pos < 0 && !table[i].handle && !table[i].cid) + pos = i; + } + + if (pos < 0) + return; + + table[pos].handle = handle; + table[pos].cid = cid; + msg = &table[pos].msg[bid]; + + data = malloc(msg->data_len + len); + if (!data) + return; + + if (msg->data_len > 0) + memcpy(data, msg->data, msg->data_len); + + memcpy(data + msg->data_len, frm->ptr, len); + free(msg->data); + msg->data = data; + msg->data_len += len; + msg->ptr = msg->data; + msg->len = msg->data_len; + msg->in = frm->in; + msg->ts = frm->ts; + msg->handle = handle; + msg->cid = cid; +} + +static void free_segment(uint8_t bid, struct frame *frm) +{ + uint16_t handle = frm->handle, cid = frm->cid; + struct frame *msg; + int i, len = 0, pos = -1; + + if (bid > 15) + return; + + for (i = 0; i < TABLE_SIZE; i++) + if (table[i].handle == handle && table[i].cid == cid) { + pos = i; + break; + } + + if (pos < 0) + return; + + msg = &table[pos].msg[bid]; + + if (msg->data) + free(msg->data); + + msg->data = NULL; + msg->data_len = 0; + + for (i = 0; i < 16; i++) + len += table[pos].msg[i].data_len; + + if (!len) { + table[pos].handle = 0; + table[pos].cid = 0; + } +} + +static struct frame *get_segment(uint8_t bid, struct frame *frm) +{ + uint16_t handle = frm->handle, cid = frm->cid; + int i; + + if (bid > 15) + return NULL; + + for (i = 0; i < TABLE_SIZE; i++) + if (table[i].handle == handle && table[i].cid == cid) + return &table[i].msg[bid]; + + return NULL; +} + +static char *bst2str(uint8_t bst) +{ + switch (bst) { + case 0x00: + return "complete CAPI Message"; + case 0x01: + return "segmented CAPI Message"; + case 0x02: + return "error"; + case 0x03: + return "reserved"; + default: + return "unknown"; + } +} + +void cmtp_dump(int level, struct frame *frm) +{ + struct frame *msg; + uint8_t hdr, bid; + uint16_t len; + + while (frm->len > 0) { + + hdr = get_u8(frm); + bid = (hdr & 0x3c) >> 2; + + switch ((hdr & 0xc0) >> 6) { + case 0x01: + len = get_u8(frm); + break; + case 0x02: + len = htons(get_u16(frm)); + break; + default: + len = 0; + break; + } + + p_indent(level, frm); + + printf("CMTP: %s: id %d len %d\n", bst2str(hdr & 0x03), bid, len); + + switch (hdr & 0x03) { + case 0x00: + add_segment(bid, frm, len); + msg = get_segment(bid, frm); + if (!msg) + break; + + if (!p_filter(FILT_CAPI)) + capi_dump(level + 1, msg); + else + raw_dump(level, msg); + + free_segment(bid, frm); + break; + case 0x01: + add_segment(bid, frm, len); + break; + default: + free_segment(bid, frm); + break; + } + + frm->ptr += len; + frm->len -= len; + } +} diff --git a/parser/csr.c b/parser/csr.c new file mode 100644 index 0000000..6cce85b --- /dev/null +++ b/parser/csr.c @@ -0,0 +1,628 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +#define CSR_U8(frm) (get_u8(frm)) +#define CSR_U16(frm) (btohs(htons(get_u16(frm)))) +#define CSR_U32(frm) ((CSR_U16(frm) << 16) + CSR_U16(frm)) +#define CSR_S16(frm) (btohs(htons(get_u16(frm)))) + +static char *type2str(uint16_t type) +{ + switch (type) { + case 0x0000: + return "Get req"; + case 0x0001: + return "Get rsp"; + case 0x0002: + return "Set req"; + default: + return "Reserved"; + } +} + +static inline void valueless_dump(int level, char *str, struct frame *frm) +{ + p_indent(level, frm); + printf("%s\n", str); +} + +static inline void complex_dump(int level, char *str, struct frame *frm) +{ + p_indent(level, frm); + printf("%s\n", str); + + raw_dump(level, frm); +} + +static inline void bool_dump(int level, char *str, struct frame *frm) +{ + uint16_t value; + + value = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: value %s (%d)\n", str, value ? "TRUE" : "FALSE", value); +} + +static inline void int8_dump(int level, char *str, struct frame *frm) +{ + int16_t value; + + value = CSR_S16(frm); + + p_indent(level, frm); + printf("%s: value %d (0x%2.2x)\n", str, value, value); +} + +static inline void int16_dump(int level, char *str, struct frame *frm) +{ + int16_t value; + + value = CSR_S16(frm); + + p_indent(level, frm); + printf("%s: value %d (0x%2.2x)\n", str, value, value); +} + +static inline void uint16_dump(int level, char *str, struct frame *frm) +{ + uint16_t value; + + value = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: value %d (0x%4.4x)\n", str, value, value); +} + +static inline void uint32_dump(int level, char *str, struct frame *frm) +{ + uint32_t value; + + value = CSR_U32(frm); + + p_indent(level, frm); + printf("%s: value %d (0x%4.4x)\n", str, value, value); +} + +static inline void bdaddr_dump(int level, char *str, struct frame *frm) +{ + char addr[18]; + + p_ba2str(frm->ptr, addr); + + p_indent(level, frm); + printf("%s: bdaddr %s\n", str, addr); +} + +static inline void features_dump(int level, char *str, struct frame *frm) +{ + unsigned char features[8]; + int i; + + memcpy(features, frm->ptr, 8); + + p_indent(level, frm); + printf("%s: features", str); + for (i = 0; i < 8; i++) + printf(" 0x%02x", features[i]); + printf("\n"); +} + +static inline void commands_dump(int level, char *str, struct frame *frm) +{ + unsigned char commands[64]; + int i; + + memcpy(commands, frm->ptr, frm->len); + + p_indent(level, frm); + printf("%s: commands", str); + for (i = 0; i < frm->len; i++) + printf(" 0x%02x", commands[i]); + printf("\n"); +} + +static inline void handle_length_dump(int level, char *str, struct frame *frm) +{ + uint16_t handle, length; + + handle = CSR_U16(frm); + length = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: handle %d length %d\n", str, handle, length); +} + +static inline void handle_clock_dump(int level, char *str, struct frame *frm) +{ + uint16_t handle; + uint32_t clock; + + handle = CSR_U16(frm); + clock = CSR_U32(frm); + + p_indent(level, frm); + printf("%s: handle %d clock 0x%4.4x\n", str, handle, clock); +} + +static inline void radiotest_dump(int level, char *str, struct frame *frm) +{ + uint16_t testid; + + testid = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: test id %d\n", str, testid); + + raw_dump(level, frm); +} + +static inline void psmemtype_dump(int level, char *str, struct frame *frm) +{ + uint16_t store, type; + + store = CSR_U16(frm); + type = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: store 0x%4.4x type %d\n", str, store, type); +} + +static inline void psnext_dump(int level, char *str, struct frame *frm) +{ + uint16_t key, stores, next; + + key = CSR_U16(frm); + stores = CSR_U16(frm); + next = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: key 0x%4.4x stores 0x%4.4x next 0x%4.4x\n", str, key, stores, next); +} + +static inline void pssize_dump(int level, char *str, struct frame *frm) +{ + uint16_t key, length; + + key = CSR_U16(frm); + length = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: key 0x%4.4x %s 0x%4.4x\n", str, key, + frm->in ? "len" : "stores", length); +} + +static inline void psstores_dump(int level, char *str, struct frame *frm) +{ + uint16_t key, stores; + + key = CSR_U16(frm); + stores = CSR_U16(frm); + + p_indent(level, frm); + printf("%s: key 0x%4.4x stores 0x%4.4x\n", str, key, stores); +} + +static inline void pskey_dump(int level, struct frame *frm) +{ + uint16_t key, length, stores; + + key = CSR_U16(frm); + length = CSR_U16(frm); + stores = CSR_U16(frm); + + p_indent(level, frm); + printf("PSKEY: key 0x%4.4x len %d stores 0x%4.4x\n", key, length, stores); + + switch (key) { + case 0x0001: + bdaddr_dump(level + 1, "BDADDR", frm); + break; + case 0x0002: + uint16_dump(level + 1, "COUNTRYCODE", frm); + break; + case 0x0003: + uint32_dump(level + 1, "CLASSOFDEVICE", frm); + break; + case 0x0004: + uint16_dump(level + 1, "DEVICE_DRIFT", frm); + break; + case 0x0005: + uint16_dump(level + 1, "DEVICE_JITTER", frm); + break; + case 0x000d: + uint16_dump(level + 1, "MAX_ACLS", frm); + break; + case 0x000e: + uint16_dump(level + 1, "MAX_SCOS", frm); + break; + case 0x000f: + uint16_dump(level + 1, "MAX_REMOTE_MASTERS", frm); + break; + case 0x00da: + uint16_dump(level + 1, "ENC_KEY_LMIN", frm); + break; + case 0x00db: + uint16_dump(level + 1, "ENC_KEY_LMAX", frm); + break; + case 0x00ef: + features_dump(level + 1, "LOCAL_SUPPORTED_FEATURES", frm); + break; + case 0x0106: + commands_dump(level + 1, "LOCAL_SUPPORTED_COMMANDS", frm); + break; + case 0x010d: + uint16_dump(level + 1, "HCI_LMP_LOCAL_VERSION", frm); + break; + case 0x010e: + uint16_dump(level + 1, "LMP_REMOTE_VERSION", frm); + break; + case 0x01a5: + bool_dump(level + 1, "HOSTIO_USE_HCI_EXTN", frm); + break; + case 0x01ab: + bool_dump(level + 1, "HOSTIO_MAP_SCO_PCM", frm); + break; + case 0x01be: + uint16_dump(level + 1, "UART_BAUDRATE", frm); + break; + case 0x01f6: + uint16_dump(level + 1, "ANA_FTRIM", frm); + break; + case 0x01f9: + uint16_dump(level + 1, "HOST_INTERFACE", frm); + break; + case 0x01fe: + uint16_dump(level + 1, "ANA_FREQ", frm); + break; + case 0x02be: + uint16_dump(level + 1, "USB_VENDOR_ID", frm); + break; + case 0x02bf: + uint16_dump(level + 1, "USB_PRODUCT_ID", frm); + break; + case 0x02cb: + uint16_dump(level + 1, "USB_DFU_PRODUCT_ID", frm); + break; + case 0x03cd: + int16_dump(level + 1, "INITIAL_BOOTMODE", frm); + break; + default: + raw_dump(level + 1, frm); + break; + } +} + +static inline void bccmd_dump(int level, struct frame *frm) +{ + uint16_t type, length, seqno, varid, status; + + type = CSR_U16(frm); + length = CSR_U16(frm); + seqno = CSR_U16(frm); + varid = CSR_U16(frm); + status = CSR_U16(frm); + + p_indent(level, frm); + printf("BCCMD: %s: len %d seqno %d varid 0x%4.4x status %d\n", + type2str(type), length, seqno, varid, status); + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level + 1, frm); + return; + } + + switch (varid) { + case 0x000b: + valueless_dump(level + 1, "PS_CLR_ALL", frm); + break; + case 0x000c: + valueless_dump(level + 1, "PS_FACTORY_SET", frm); + break; + case 0x082d: + uint16_dump(level + 1, "PS_CLR_ALL_STORES", frm); + break; + case 0x2801: + uint16_dump(level + 1, "BC01_STATUS", frm); + break; + case 0x2819: + uint16_dump(level + 1, "BUILDID", frm); + break; + case 0x281a: + uint16_dump(level + 1, "CHIPVER", frm); + break; + case 0x281b: + uint16_dump(level + 1, "CHIPREV", frm); + break; + case 0x2825: + uint16_dump(level + 1, "INTERFACE_VERSION", frm); + break; + case 0x282a: + uint16_dump(level + 1, "RAND", frm); + break; + case 0x282c: + uint16_dump(level + 1, "MAX_CRYPT_KEY_LENGTH", frm); + break; + case 0x2833: + uint16_dump(level + 1, "E2_APP_SIZE", frm); + break; + case 0x2836: + uint16_dump(level + 1, "CHIPANAREV", frm); + break; + case 0x2838: + uint16_dump(level + 1, "BUILDID_LOADER", frm); + break; + case 0x2c00: + uint32_dump(level + 1, "BT_CLOCK", frm); + break; + case 0x3005: + psnext_dump(level + 1, "PS_NEXT", frm); + break; + case 0x3006: + pssize_dump(level + 1, "PS_SIZE", frm); + break; + case 0x3008: + handle_length_dump(level + 1, "CRYPT_KEY_LENGTH", frm); + break; + case 0x3009: + handle_clock_dump(level + 1, "PICONET_INSTANCE", frm); + break; + case 0x300a: + complex_dump(level + 1, "GET_CLR_EVT", frm); + break; + case 0x300b: + complex_dump(level + 1, "GET_NEXT_BUILDDEF", frm); + break; + case 0x300e: + complex_dump(level + 1, "E2_DEVICE", frm); + break; + case 0x300f: + complex_dump(level + 1, "E2_APP_DATA", frm); + break; + case 0x3012: + psmemtype_dump(level + 1, "PS_MEMORY_TYPE", frm); + break; + case 0x301c: + complex_dump(level + 1, "READ_BUILD_NAME", frm); + break; + case 0x4001: + valueless_dump(level + 1, "COLD_RESET", frm); + break; + case 0x4002: + valueless_dump(level + 1, "WARM_RESET", frm); + break; + case 0x4003: + valueless_dump(level + 1, "COLD_HALT", frm); + break; + case 0x4004: + valueless_dump(level + 1, "WARM_HALT", frm); + break; + case 0x4005: + valueless_dump(level + 1, "INIT_BT_STACK", frm); + break; + case 0x4006: + valueless_dump(level + 1, "ACTIVATE_BT_STACK", frm); + break; + case 0x4007: + valueless_dump(level + 1, "ENABLE_TX", frm); + break; + case 0x4008: + valueless_dump(level + 1, "DISABLE_TX", frm); + break; + case 0x4009: + valueless_dump(level + 1, "RECAL", frm); + break; + case 0x400d: + valueless_dump(level + 1, "PS_FACTORY_RESTORE", frm); + break; + case 0x400e: + valueless_dump(level + 1, "PS_FACTORY_RESTORE_ALL", frm); + break; + case 0x400f: + valueless_dump(level + 1, "PS_DEFRAG_RESET", frm); + break; + case 0x4011: + valueless_dump(level + 1, "HOPPING_ON", frm); + break; + case 0x4012: + valueless_dump(level + 1, "CANCEL_PAGE", frm); + break; + case 0x4818: + uint16_dump(level + 1, "PS_CLR", frm); + break; + case 0x481c: + uint16_dump(level + 1, "MAP_SCO_PCM", frm); + break; + case 0x482e: + uint16_dump(level + 1, "SINGLE_CHAN", frm); + break; + case 0x5004: + radiotest_dump(level + 1, "RADIOTEST", frm); + break; + case 0x500c: + psstores_dump(level + 1, "PS_CLR_STORES", frm); + break; + case 0x6000: + valueless_dump(level + 1, "NO_VARIABLE", frm); + break; + case 0x6802: + uint16_dump(level + 1, "CONFIG_UART", frm); + break; + case 0x6805: + uint16_dump(level + 1, "PANIC_ARG", frm); + break; + case 0x6806: + uint16_dump(level + 1, "FAULT_ARG", frm); + break; + case 0x6827: + int8_dump(level + 1, "MAX_TX_POWER", frm); + break; + case 0x682b: + int8_dump(level + 1, "DEFAULT_TX_POWER", frm); + break; + case 0x7003: + pskey_dump(level + 1, frm); + break; + default: + raw_dump(level + 1, frm); + break; + } +} + +static char *cid2str(uint8_t cid) +{ + switch (cid & 0x3f) { + case 0: + return "BCSP Internal"; + case 1: + return "BCSP Link"; + case 2: + return "BCCMD"; + case 3: + return "HQ"; + case 4: + return "Device Mgt"; + case 5: + return "HCI Cmd/Evt"; + case 6: + return "HCI ACL"; + case 7: + return "HCI SCO"; + case 8: + return "L2CAP"; + case 9: + return "RFCOMM"; + case 10: + return "SDP"; + case 11: + return "Debug"; + case 12: + return "DFU"; + case 13: + return "VM"; + case 14: + return "Unused"; + case 15: + return "Reserved"; + default: + return "Unknown"; + } +} + +static char *frag2str(uint8_t frag) +{ + switch (frag & 0xc0) { + case 0x00: + return " middle fragment"; + case 0x40: + return " first fragment"; + case 0x80: + return " last fragment"; + default: + return ""; + } +} + +void csr_dump(int level, struct frame *frm) +{ + uint8_t desc, cid, type; + uint16_t handle, master, addr; + + desc = CSR_U8(frm); + + cid = desc & 0x3f; + + switch (cid) { + case 2: + bccmd_dump(level, frm); + break; + + case 20: + type = CSR_U8(frm); + + if (!p_filter(FILT_LMP)) { + switch (type) { + case 0x0f: + frm->handle = ((uint8_t *) frm->ptr)[17]; + frm->master = 0; + frm->len--; + lmp_dump(level, frm); + return; + case 0x10: + frm->handle = ((uint8_t *) frm->ptr)[17]; + frm->master = 1; + frm->len--; + lmp_dump(level, frm); + return; + case 0x12: + handle = CSR_U16(frm); + master = CSR_U16(frm); + addr = CSR_U16(frm); + p_indent(level, frm); + printf("FHS: handle %d addr %d (%s)\n", handle, + addr, master ? "master" : "slave"); + if (!master) { + char addr[18]; + p_ba2str((bdaddr_t *) frm->ptr, addr); + p_indent(level + 1, frm); + printf("bdaddr %s class " + "0x%2.2x%2.2x%2.2x\n", addr, + ((uint8_t *) frm->ptr)[8], + ((uint8_t *) frm->ptr)[7], + ((uint8_t *) frm->ptr)[6]); + } + return; + case 0x7b: + p_indent(level, frm); + printf("LMP(r): duplicate (same SEQN)\n"); + return; + } + } + + p_indent(level, frm); + printf("CSR: Debug (type 0x%2.2x)\n", type); + raw_dump(level, frm); + break; + + default: + p_indent(level, frm); + printf("CSR: %s (channel %d)%s\n", cid2str(cid), cid, frag2str(desc)); + raw_dump(level, frm); + break; + } +} diff --git a/parser/ericsson.c b/parser/ericsson.c new file mode 100644 index 0000000..a9546d6 --- /dev/null +++ b/parser/ericsson.c @@ -0,0 +1,53 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "parser.h" + +void ericsson_dump(int level, struct frame *frm) +{ + uint8_t event = get_u8(frm); + uint8_t *buf = (uint8_t *) frm->ptr; + + if (event != 0x10) { + p_indent(level, frm); + printf("Ericsson: event 0x%2.2x\n", event); + raw_dump(level, frm); + } + + frm->master = !(buf[0] & 0x01); + frm->handle = buf[1] | (buf[2] << 8); + + buf[5] = (buf[5] << 1) | (buf[3] & 0x01); + + frm->ptr += 5; + frm->len -= 5; + + lmp_dump(level, frm); +} diff --git a/parser/hci.c b/parser/hci.c new file mode 100644 index 0000000..c0ca27e --- /dev/null +++ b/parser/hci.c @@ -0,0 +1,3322 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2002 Maxim Krasnyansky + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "parser.h" + +static uint16_t manufacturer = DEFAULT_COMPID; + +static inline uint16_t get_manufacturer(void) +{ + return (manufacturer == DEFAULT_COMPID ? parser.defcompid : manufacturer); +} + +#define EVENT_NUM 61 +static char *event_str[EVENT_NUM + 1] = { + "Unknown", + "Inquiry Complete", + "Inquiry Result", + "Connect Complete", + "Connect Request", + "Disconn Complete", + "Auth Complete", + "Remote Name Req Complete", + "Encrypt Change", + "Change Connection Link Key Complete", + "Master Link Key Complete", + "Read Remote Supported Features", + "Read Remote Ver Info Complete", + "QoS Setup Complete", + "Command Complete", + "Command Status", + "Hardware Error", + "Flush Occurred", + "Role Change", + "Number of Completed Packets", + "Mode Change", + "Return Link Keys", + "PIN Code Request", + "Link Key Request", + "Link Key Notification", + "Loopback Command", + "Data Buffer Overflow", + "Max Slots Change", + "Read Clock Offset Complete", + "Connection Packet Type Changed", + "QoS Violation", + "Page Scan Mode Change", + "Page Scan Repetition Mode Change", + "Flow Specification Complete", + "Inquiry Result with RSSI", + "Read Remote Extended Features", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Synchronous Connect Complete", + "Synchronous Connect Changed", + "Sniff Subrate", + "Extended Inquiry Result", + "Encryption Key Refresh Complete", + "IO Capability Request", + "IO Capability Response", + "User Confirmation Request", + "User Passkey Request", + "Remote OOB Data Request", + "Simple Pairing Complete", + "Unknown", + "Link Supervision Timeout Change", + "Enhanced Flush Complete", + "Unknown", + "User Passkey Notification", + "Keypress Notification", + "Remote Host Supported Features Notification", +}; + +#define CMD_LINKCTL_NUM 52 +static char *cmd_linkctl_str[CMD_LINKCTL_NUM + 1] = { + "Unknown", + "Inquiry", + "Inquiry Cancel", + "Periodic Inquiry Mode", + "Exit Periodic Inquiry Mode", + "Create Connection", + "Disconnect", + "Add SCO Connection", + "Create Connection Cancel", + "Accept Connection Request", + "Reject Connection Request", + "Link Key Request Reply", + "Link Key Request Negative Reply", + "PIN Code Request Reply", + "PIN Code Request Negative Reply", + "Change Connection Packet Type", + "Unknown", + "Authentication Requested", + "Unknown", + "Set Connection Encryption", + "Unknown", + "Change Connection Link Key", + "Unknown", + "Master Link Key", + "Unknown", + "Remote Name Request", + "Remote Name Request Cancel", + "Read Remote Supported Features", + "Read Remote Extended Features", + "Read Remote Version Information", + "Unknown", + "Read Clock Offset", + "Read LMP Handle", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Setup Synchronous Connection", + "Accept Synchronous Connection", + "Reject Synchronous Connection", + "IO Capability Request Reply", + "User Confirmation Request Reply", + "User Confirmation Request Negative Reply", + "User Passkey Request Reply", + "User Passkey Request Negative Reply", + "Remote OOB Data Request Reply", + "Unknown", + "Unknown", + "Remote OOB Data Request Negative Reply", + "IO Capability Request Negative Reply", +}; + +#define CMD_LINKPOL_NUM 17 +static char *cmd_linkpol_str[CMD_LINKPOL_NUM + 1] = { + "Unknown", + "Hold Mode", + "Unknown", + "Sniff Mode", + "Exit Sniff Mode", + "Park State", + "Exit Park State", + "QoS Setup", + "Unknown", + "Role Discovery", + "Unknown", + "Switch Role", + "Read Link Policy Settings", + "Write Link Policy Settings", + "Read Default Link Policy Settings", + "Write Default Link Policy Settings", + "Flow Specification", + "Sniff Subrating", +}; + +#define CMD_HOSTCTL_NUM 95 +static char *cmd_hostctl_str[CMD_HOSTCTL_NUM + 1] = { + "Unknown", + "Set Event Mask", + "Unknown", + "Reset", + "Unknown", + "Set Event Filter", + "Unknown", + "Unknown", + "Flush", + "Read PIN Type ", + "Write PIN Type", + "Create New Unit Key", + "Unknown", + "Read Stored Link Key", + "Unknown", + "Unknown", + "Unknown", + "Write Stored Link Key", + "Delete Stored Link Key", + "Write Local Name", + "Read Local Name", + "Read Connection Accept Timeout", + "Write Connection Accept Timeout", + "Read Page Timeout", + "Write Page Timeout", + "Read Scan Enable", + "Write Scan Enable", + "Read Page Scan Activity", + "Write Page Scan Activity", + "Read Inquiry Scan Activity", + "Write Inquiry Scan Activity", + "Read Authentication Enable", + "Write Authentication Enable", + "Read Encryption Mode", + "Write Encryption Mode", + "Read Class of Device", + "Write Class of Device", + "Read Voice Setting", + "Write Voice Setting", + "Read Automatic Flush Timeout", + "Write Automatic Flush Timeout", + "Read Num Broadcast Retransmissions", + "Write Num Broadcast Retransmissions", + "Read Hold Mode Activity ", + "Write Hold Mode Activity", + "Read Transmit Power Level", + "Read Synchronous Flow Control Enable", + "Write Synchronous Flow Control Enable", + "Unknown", + "Set Host Controller To Host Flow Control", + "Unknown", + "Host Buffer Size", + "Unknown", + "Host Number of Completed Packets", + "Read Link Supervision Timeout", + "Write Link Supervision Timeout", + "Read Number of Supported IAC", + "Read Current IAC LAP", + "Write Current IAC LAP", + "Read Page Scan Period Mode", + "Write Page Scan Period Mode", + "Read Page Scan Mode", + "Write Page Scan Mode", + "Set AFH Host Channel Classification", + "Unknown", + "Unknown", + "Read Inquiry Scan Type", + "Write Inquiry Scan Type", + "Read Inquiry Mode", + "Write Inquiry Mode", + "Read Page Scan Type", + "Write Page Scan Type", + "Read AFH Channel Assessment Mode", + "Write AFH Channel Assessment Mode", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Read Extended Inquiry Response", + "Write Extended Inquiry Response", + "Refresh Encryption Key", + "Unknown", + "Read Simple Pairing Mode", + "Write Simple Pairing Mode", + "Read Local OOB Data", + "Read Inquiry Response Transmit Power Level", + "Write Inquiry Response Transmit Power Level", + "Read Default Erroneous Data Reporting", + "Write Default Erroneous Data Reporting", + "Unknown", + "Unknown", + "Unknown", + "Enhanced Flush" + "Unknown", +}; + +#define CMD_INFO_NUM 9 +static char *cmd_info_str[CMD_INFO_NUM + 1] = { + "Unknown", + "Read Local Version Information", + "Read Local Supported Commands", + "Read Local Supported Features", + "Read Local Extended Features", + "Read Buffer Size", + "Unknown", + "Read Country Code", + "Unknown", + "Read BD ADDR", +}; + +#define CMD_STATUS_NUM 7 +static char *cmd_status_str[CMD_STATUS_NUM + 1] = { + "Unknown", + "Read Failed Contact Counter", + "Reset Failed Contact Counter", + "Read Link Quality", + "Unknown", + "Read RSSI", + "Read AFH Channel Map", + "Read Clock", +}; + +#define CMD_TESTING_NUM 4 +static char *cmd_testing_str[CMD_TESTING_NUM + 1] = { + "Unknown", + "Read Loopback Mode", + "Write Loopback Mode", + "Enable Device Under Test mode", + "Unknown", +}; + +#define ERROR_CODE_NUM 56 +static char *error_code_str[ERROR_CODE_NUM + 1] = { + "Success", + "Unknown HCI Command", + "Unknown Connection Identifier", + "Hardware Failure", + "Page Timeout", + "Authentication Failure", + "PIN or Key Missing", + "Memory Capacity Exceeded", + "Connection Timeout", + "Connection Limit Exceeded", + "Synchronous Connection to a Device Exceeded", + "ACL Connection Already Exists", + "Command Disallowed", + "Connection Rejected due to Limited Resources", + "Connection Rejected due to Security Reasons", + "Connection Rejected due to Unacceptable BD_ADDR", + "Connection Accept Timeout Exceeded", + "Unsupported Feature or Parameter Value", + "Invalid HCI Command Parameters", + "Remote User Terminated Connection", + "Remote Device Terminated Connection due to Low Resources", + "Remote Device Terminated Connection due to Power Off", + "Connection Terminated by Local Host", + "Repeated Attempts", + "Pairing Not Allowed", + "Unknown LMP PDU", + "Unsupported Remote Feature / Unsupported LMP Feature", + "SCO Offset Rejected", + "SCO Interval Rejected", + "SCO Air Mode Rejected", + "Invalid LMP Parameters", + "Unspecified Error", + "Unsupported LMP Parameter Value", + "Role Change Not Allowed", + "LMP Response Timeout", + "LMP Error Transaction Collision", + "LMP PDU Not Allowed", + "Encryption Mode Not Acceptable", + "Link Key Can Not be Changed", + "Requested QoS Not Supported", + "Instant Passed", + "Pairing with Unit Key Not Supported", + "Different Transaction Collision", + "Reserved", + "QoS Unacceptable Parameter", + "QoS Rejected", + "Channel Classification Not Supported", + "Insufficient Security", + "Parameter out of Mandatory Range", + "Reserved", + "Role Switch Pending", + "Reserved", + "Reserved Slot Violation", + "Role Switch Failed", + "Extended Inquiry Response Too Large", + "Simple Pairing Not Supported by Host", + "Host Busy - Pairing", +}; + +static char *status2str(uint8_t status) +{ + char *str; + + if (status <= ERROR_CODE_NUM) + str = error_code_str[status]; + else + str = "Unknown"; + + return str; +} + +static char *opcode2str(uint16_t opcode) +{ + uint16_t ogf = cmd_opcode_ogf(opcode); + uint16_t ocf = cmd_opcode_ocf(opcode); + char *cmd; + + switch (ogf) { + case OGF_INFO_PARAM: + if (ocf <= CMD_INFO_NUM) + cmd = cmd_info_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_HOST_CTL: + if (ocf <= CMD_HOSTCTL_NUM) + cmd = cmd_hostctl_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_LINK_CTL: + if (ocf <= CMD_LINKCTL_NUM) + cmd = cmd_linkctl_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_LINK_POLICY: + if (ocf <= CMD_LINKPOL_NUM) + cmd = cmd_linkpol_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_STATUS_PARAM: + if (ocf <= CMD_STATUS_NUM) + cmd = cmd_status_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_TESTING_CMD: + if (ocf <= CMD_TESTING_NUM) + cmd = cmd_testing_str[ocf]; + else + cmd = "Unknown"; + break; + + case OGF_VENDOR_CMD: + cmd = "Vendor"; + break; + + default: + cmd = "Unknown"; + break; + } + + return cmd; +} + +static char *linktype2str(uint8_t type) +{ + switch (type) { + case 0x00: + return "SCO"; + case 0x01: + return "ACL"; + case 0x02: + return "eSCO"; + default: + return "Unknown"; + } +} + +static char *role2str(uint8_t role) +{ + switch (role) { + case 0x00: + return "Master"; + case 0x01: + return "Slave"; + default: + return "Unknown"; + } +} + +static char *mode2str(uint8_t mode) +{ + switch (mode) { + case 0x00: + return "Active"; + case 0x01: + return "Hold"; + case 0x02: + return "Sniff"; + case 0x03: + return "Park"; + default: + return "Unknown"; + } +} + +static char *airmode2str(uint8_t mode) +{ + switch (mode) { + case 0x00: + return "u-law log"; + case 0x01: + return "A-law log"; + case 0x02: + return "CVSD"; + case 0x04: + return "Transparent data"; + default: + return "Reserved"; + } +} + +static char *keytype2str(uint8_t type) +{ + switch (type) { + case 0x00: + return "Combination Key"; + case 0x01: + return "Local Unit Key"; + case 0x02: + return "Remote Unit Key"; + case 0x03: + return "Debug Combination Key"; + case 0x04: + return "Unauthenticated Combination Key"; + case 0x05: + return "Authenticated Combination Key"; + case 0x06: + return "Changed Combination Key"; + default: + return "Reserved"; + } +} + +static char *capability2str(uint8_t capability) +{ + switch (capability) { + case 0x00: + return "DisplayOnly"; + case 0x01: + return "DisplayYesNo"; + case 0x02: + return "KeyboardOnly"; + case 0x03: + return "NoInputNoOutput"; + default: + return "Reserved"; + } +} + +static char *authentication2str(uint8_t authentication) +{ + switch (authentication) { + case 0x00: + return "No Bonding (No MITM Protection)"; + case 0x01: + return "No Bonding (MITM Protection)"; + case 0x02: + return "Dedicated Bonding (No MITM Protection)"; + case 0x03: + return "Dedicated Bonding (MITM Protection)"; + case 0x04: + return "General Bonding (No MITM Protection)"; + case 0x05: + return "General Bonding (MITM Protection)"; + default: + return "Reserved"; + } +} + +static inline void ext_inquiry_response_dump(int level, struct frame *frm) +{ + void *ptr = frm->ptr; + uint32_t len = frm->len; + uint8_t type, length; + char *str; + int i; + + length = get_u8(frm); + + while (length > 0) { + type = get_u8(frm); + length--; + + switch (type) { + case 0x01: + p_indent(level, frm); + printf("Flags:"); + for (i = 0; i < length; i++) + printf(" 0x%2.2x", *((uint8_t *) (frm->ptr + i))); + printf("\n"); + break; + + case 0x02: + case 0x03: + p_indent(level, frm); + printf("%s service classes:", + type == 0x02 ? "Shortened" : "Complete"); + for (i = 0; i < length / 2; i++) { + uint16_t val = btohs(bt_get_unaligned((uint16_t *) (frm->ptr + (i * 2)))); + printf(" 0x%4.4x", val); + } + printf("\n"); + break; + + case 0x08: + case 0x09: + str = malloc(length + 1); + if (str) { + snprintf(str, length + 1, "%s", (char *) frm->ptr); + for (i = 0; i < length; i++) + if (!isprint(str[i])) + str[i] = '.'; + p_indent(level, frm); + printf("%s local name: \'%s\'\n", + type == 0x08 ? "Shortened" : "Complete", str); + free(str); + } + break; + + case 0x0a: + p_indent(level, frm); + printf("TX power level: %d\n", *((uint8_t *) frm->ptr)); + break; + + default: + p_indent(level, frm); + printf("Unknown type 0x%02x with %d bytes data\n", + type, length); + break; + } + + frm->ptr += length; + frm->len -= length; + + length = get_u8(frm); + } + + frm->ptr = ptr + (EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE); + frm->len = len + (EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE); +} + +static inline void bdaddr_command_dump(int level, struct frame *frm) +{ + bdaddr_t *bdaddr = frm->ptr; + char addr[18]; + + frm->ptr += sizeof(bdaddr_t); + frm->len -= sizeof(bdaddr_t); + + p_indent(level, frm); + p_ba2str(bdaddr, addr); + printf("bdaddr %s\n", addr); + + raw_dump(level, frm); +} + +static inline void generic_command_dump(int level, struct frame *frm) +{ + uint16_t handle = btohs(htons(get_u16(frm))); + + p_indent(level, frm); + printf("handle %d\n", handle); + + raw_dump(level, frm); +} + +static inline void generic_write_mode_dump(int level, struct frame *frm) +{ + uint8_t mode = get_u8(frm); + + p_indent(level, frm); + printf("mode 0x%2.2x\n", mode); +} + +static inline void inquiry_dump(int level, struct frame *frm) +{ + inquiry_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("lap 0x%2.2x%2.2x%2.2x len %d num %d\n", + cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp); +} + +static inline void periodic_inquiry_dump(int level, struct frame *frm) +{ + periodic_inquiry_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("max %d min %d lap 0x%2.2x%2.2x%2.2x len %d num %d\n", + btohs(cp->max_period), btohs(cp->min_period), + cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp); +} + +static inline void create_conn_dump(int level, struct frame *frm) +{ + create_conn_cp *cp = frm->ptr; + uint16_t ptype = btohs(cp->pkt_type); + uint16_t clkoffset = btohs(cp->clock_offset); + char addr[18], *str; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s ptype 0x%4.4x rswitch 0x%2.2x clkoffset 0x%4.4x%s\n", + addr, ptype, cp->role_switch, + clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : ""); + + str = hci_ptypetostr(ptype); + if (str) { + p_indent(level, frm); + printf("Packet type: %s\n", str); + free(str); + } +} + +static inline void disconnect_dump(int level, struct frame *frm) +{ + disconnect_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d reason 0x%2.2x\n", btohs(cp->handle), cp->reason); + + p_indent(level, frm); + printf("Reason: %s\n", status2str(cp->reason)); +} + +static inline void add_sco_dump(int level, struct frame *frm) +{ + add_sco_cp *cp = frm->ptr; + uint16_t ptype = btohs(cp->pkt_type); + char *str; + + p_indent(level, frm); + printf("handle %d ptype 0x%4.4x\n", btohs(cp->handle), ptype); + + str = hci_ptypetostr(ptype); + if (str) { + p_indent(level, frm); + printf("Packet type: %s\n", str); + free(str); + } +} + +static inline void accept_conn_req_dump(int level, struct frame *frm) +{ + accept_conn_req_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s role 0x%2.2x\n", addr, cp->role); + + p_indent(level, frm); + printf("Role: %s\n", role2str(cp->role)); +} + +static inline void reject_conn_req_dump(int level, struct frame *frm) +{ + reject_conn_req_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s reason 0x%2.2x\n", addr, cp->reason); + + p_indent(level, frm); + printf("Reason: %s\n", status2str(cp->reason)); +} + +static inline void pin_code_reply_dump(int level, struct frame *frm) +{ + pin_code_reply_cp *cp = frm->ptr; + char addr[18], pin[17]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + memset(pin, 0, sizeof(pin)); + if (parser.flags & DUMP_NOVENDOR) + memset(pin, '*', cp->pin_len); + else + memcpy(pin, cp->pin_code, cp->pin_len); + printf("bdaddr %s len %d pin \'%s\'\n", addr, cp->pin_len, pin); +} + +static inline void link_key_reply_dump(int level, struct frame *frm) +{ + link_key_reply_cp *cp = frm->ptr; + char addr[18]; + int i; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s key ", addr); + for (i = 0; i < 16; i++) + if (parser.flags & DUMP_NOVENDOR) + printf("**"); + else + printf("%2.2X", cp->link_key[i]); + printf("\n"); +} + +static inline void pin_code_neg_reply_dump(int level, struct frame *frm) +{ + bdaddr_t *bdaddr = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(bdaddr, addr); + printf("bdaddr %s\n", addr); +} + +static inline void user_passkey_reply_dump(int level, struct frame *frm) +{ + user_passkey_reply_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s passkey %d\n", addr, btohl(cp->passkey)); +} + +static inline void remote_oob_data_reply_dump(int level, struct frame *frm) +{ + remote_oob_data_reply_cp *cp = frm->ptr; + char addr[18]; + int i; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s\n", addr); + + p_indent(level, frm); + printf("hash 0x"); + for (i = 0; i < 16; i++) + printf("%02x", cp->hash[i]); + printf("\n"); + + p_indent(level, frm); + printf("randomizer 0x"); + for (i = 0; i < 16; i++) + printf("%02x", cp->randomizer[i]); + printf("\n"); +} + +static inline void io_capability_reply_dump(int level, struct frame *frm) +{ + io_capability_reply_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n", + addr, cp->capability, cp->oob_data, + cp->authentication); + + p_indent(level, frm); + printf("Capability: %s (OOB data %s)\n", + capability2str(cp->capability), + cp->oob_data == 0x00 ? "not present" : "available"); + + p_indent(level, frm); + printf("Authentication: %s\n", authentication2str(cp->authentication)); +} + +static inline void set_conn_encrypt_dump(int level, struct frame *frm) +{ + set_conn_encrypt_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d encrypt 0x%2.2x\n", btohs(cp->handle), cp->encrypt); +} + +static inline void remote_name_req_dump(int level, struct frame *frm) +{ + remote_name_req_cp *cp = frm->ptr; + uint16_t clkoffset = btohs(cp->clock_offset); + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s mode %d clkoffset 0x%4.4x%s\n", + addr, cp->pscan_rep_mode, + clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : ""); +} + +static inline void master_link_key_dump(int level, struct frame *frm) +{ + master_link_key_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("flag %d\n", cp->key_flag); +} + +static inline void read_remote_ext_features_dump(int level, struct frame *frm) +{ + read_remote_ext_features_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d page %d\n", btohs(cp->handle), cp->page_num); +} + +static inline void setup_sync_conn_dump(int level, struct frame *frm) +{ + setup_sync_conn_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d voice setting 0x%4.4x\n", btohs(cp->handle), + btohs(cp->voice_setting)); +} + +static inline void hold_mode_dump(int level, struct frame *frm) +{ + hold_mode_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d max %d min %d\n", btohs(cp->handle), + btohs(cp->max_interval), btohs(cp->min_interval)); +} + +static inline void sniff_mode_dump(int level, struct frame *frm) +{ + sniff_mode_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d max %d min %d attempt %d timeout %d\n", + btohs(cp->handle), btohs(cp->max_interval), + btohs(cp->min_interval), btohs(cp->attempt), btohs(cp->timeout)); +} + +static inline void qos_setup_dump(int level, struct frame *frm) +{ + qos_setup_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d flags 0x%2.2x\n", btohs(cp->handle), cp->flags); + + p_indent(level, frm); + printf("Service type: %d\n", cp->qos.service_type); + p_indent(level, frm); + printf("Token rate: %d\n", btohl(cp->qos.token_rate)); + p_indent(level, frm); + printf("Peak bandwith: %d\n", btohl(cp->qos.peak_bandwidth)); + p_indent(level, frm); + printf("Latency: %d\n", btohl(cp->qos.latency)); + p_indent(level, frm); + printf("Delay variation: %d\n", btohl(cp->qos.delay_variation)); +} + +static inline void write_link_policy_dump(int level, struct frame *frm) +{ + write_link_policy_cp *cp = frm->ptr; + uint16_t policy = btohs(cp->policy); + char *str; + + p_indent(level, frm); + printf("handle %d policy 0x%2.2x\n", btohs(cp->handle), policy); + + str = hci_lptostr(policy); + if (str) { + p_indent(level, frm); + printf("Link policy: %s\n", str); + free(str); + } +} + +static inline void write_default_link_policy_dump(int level, struct frame *frm) +{ + uint16_t policy = btohs(htons(get_u16(frm))); + char *str; + + p_indent(level, frm); + printf("policy 0x%2.2x\n", policy); + + str = hci_lptostr(policy); + if (str) { + p_indent(level, frm); + printf("Link policy: %s\n", str); + free(str); + } +} + +static inline void sniff_subrating_dump(int level, struct frame *frm) +{ + sniff_subrating_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d\n", btohs(cp->handle)); + + p_indent(level, frm); + printf("max latency %d\n", btohs(cp->max_latency)); + + p_indent(level, frm); + printf("min timeout remote %d local %d\n", + btohs(cp->min_remote_timeout), btohs(cp->min_local_timeout)); +} + +static inline void set_event_mask_dump(int level, struct frame *frm) +{ + set_event_mask_cp *cp = frm->ptr; + int i; + + p_indent(level, frm); + printf("Mask: 0x"); + for (i = 0; i < 8; i++) + printf("%2.2x", cp->mask[i]); + printf("\n"); +} + +static inline void set_event_flt_dump(int level, struct frame *frm) +{ + set_event_flt_cp *cp = frm->ptr; + uint8_t dev_class[3], dev_mask[3]; + char addr[18]; + + p_indent(level, frm); + printf("type %d condition %d\n", cp->flt_type, + (cp->flt_type == 0) ? 0 : cp->cond_type); + + switch (cp->flt_type) { + case FLT_CLEAR_ALL: + p_indent(level, frm); + printf("Clear all filters\n"); + break; + case FLT_INQ_RESULT: + p_indent(level, frm); + printf("Inquiry result"); + switch (cp->cond_type) { + case INQ_RESULT_RETURN_ALL: + printf(" for all devices\n"); + break; + case INQ_RESULT_RETURN_CLASS: + memcpy(dev_class, cp->condition, 3); + memcpy(dev_mask, cp->condition + 3, 3); + printf(" with class 0x%2.2x%2.2x%2.2x mask 0x%2.2x%2.2x%2.2x\n", + dev_class[2], dev_class[1], dev_class[0], + dev_mask[2], dev_mask[1], dev_mask[0]); + break; + case INQ_RESULT_RETURN_BDADDR: + p_ba2str((bdaddr_t *) cp->condition, addr); + printf(" with bdaddr %s\n", addr); + break; + default: + printf("\n"); + break; + } + break; + case FLT_CONN_SETUP: + p_indent(level, frm); + printf("Connection setup"); + switch (cp->cond_type) { + case CONN_SETUP_ALLOW_ALL: + case CONN_SETUP_ALLOW_CLASS: + case CONN_SETUP_ALLOW_BDADDR: + default: + printf("\n"); + break; + } + break; + } +} + +static inline void write_pin_type_dump(int level, struct frame *frm) +{ + write_pin_type_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("type %d\n", cp->pin_type); +} + +static inline void request_stored_link_key_dump(int level, struct frame *frm) +{ + read_stored_link_key_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s all %d\n", addr, cp->read_all); +} + +static inline void return_link_keys_dump(int level, struct frame *frm) +{ + uint8_t num = get_u8(frm); + uint8_t key[16]; + char addr[18]; + int i, n; + + for (n = 0; n < num; n++) { + p_ba2str(frm->ptr, addr); + memcpy(key, frm->ptr + 6, 16); + + p_indent(level, frm); + printf("bdaddr %s key ", addr); + for (i = 0; i < 16; i++) + if (parser.flags & DUMP_NOVENDOR) + printf("**"); + else + printf("%2.2X", key[i]); + printf("\n"); + + frm->ptr += 2; + frm->len -= 2; + } +} + +static inline void change_local_name_dump(int level, struct frame *frm) +{ + change_local_name_cp *cp = frm->ptr; + char name[249]; + int i; + + memset(name, 0, sizeof(name)); + for (i = 0; i < 248 && cp->name[i]; i++) + if (isprint(cp->name[i])) + name[i] = cp->name[i]; + else + name[i] = '.'; + + p_indent(level, frm); + printf("name \'%s\'\n", name); +} + +static inline void write_class_of_dev_dump(int level, struct frame *frm) +{ + write_class_of_dev_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("class 0x%2.2x%2.2x%2.2x\n", + cp->dev_class[2], cp->dev_class[1], cp->dev_class[0]); +} + +static inline void write_voice_setting_dump(int level, struct frame *frm) +{ + write_voice_setting_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("voice setting 0x%4.4x\n", btohs(cp->voice_setting)); +} + +static inline void write_current_iac_lap_dump(int level, struct frame *frm) +{ + write_current_iac_lap_cp *cp = frm->ptr; + int i; + + for (i = 0; i < cp->num_current_iac; i++) { + p_indent(level, frm); + printf("IAC 0x%2.2x%2.2x%2.2x", cp->lap[i][2], cp->lap[i][1], cp->lap[i][0]); + if (cp->lap[i][2] == 0x9e && cp->lap[i][1] == 0x8b) { + switch (cp->lap[i][0]) { + case 0x00: + printf(" (Limited Inquiry Access Code)"); + break; + case 0x33: + printf(" (General Inquiry Access Code)"); + break; + } + } + printf("\n"); + } +} + +static inline void write_scan_enable_dump(int level, struct frame *frm) +{ + uint8_t enable = get_u8(frm); + + p_indent(level, frm); + printf("enable %d\n", enable); +} + +static inline void write_page_timeout_dump(int level, struct frame *frm) +{ + write_page_timeout_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("timeout %d\n", btohs(cp->timeout)); +} + +static inline void write_page_activity_dump(int level, struct frame *frm) +{ + write_page_activity_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("interval %d window %d\n", btohs(cp->interval), btohs(cp->window)); +} + +static inline void write_inquiry_scan_type_dump(int level, struct frame *frm) +{ + write_inquiry_scan_type_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("type %d\n", cp->type); +} + +static inline void write_inquiry_mode_dump(int level, struct frame *frm) +{ + write_inquiry_mode_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("mode %d\n", cp->mode); +} + +static inline void set_afh_classification_dump(int level, struct frame *frm) +{ + set_afh_classification_cp *cp = frm->ptr; + int i; + + p_indent(level, frm); + printf("map 0x"); + for (i = 0; i < 10; i++) + printf("%02x", cp->map[i]); + printf("\n"); +} + +static inline void write_link_supervision_timeout_dump(int level, struct frame *frm) +{ + write_link_supervision_timeout_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d timeout %d\n", + btohs(cp->handle), btohs(cp->timeout)); +} + +static inline void write_ext_inquiry_response_dump(int level, struct frame *frm) +{ + write_ext_inquiry_response_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("fec 0x%2.2x\n", cp->fec); + + frm->ptr++; + frm->len--; + + ext_inquiry_response_dump(level, frm); +} + +static inline void write_inquiry_transmit_power_level_dump(int level, struct frame *frm) +{ + write_inquiry_transmit_power_level_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("level %d\n", cp->level); +} + +static inline void write_default_error_data_reporting_dump(int level, struct frame *frm) +{ + write_default_error_data_reporting_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("reporting %d\n", cp->reporting); +} + +static inline void enhanced_flush_dump(int level, struct frame *frm) +{ + enhanced_flush_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d type %d\n", btohs(cp->handle), cp->type); +} + +static inline void send_keypress_notify_dump(int level, struct frame *frm) +{ + send_keypress_notify_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s type %d\n", addr, cp->type); +} + +static inline void request_transmit_power_level_dump(int level, struct frame *frm) +{ + read_transmit_power_level_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d type %d (%s)\n", + btohs(cp->handle), cp->type, + cp->type ? "maximum" : "current"); +} + +static inline void request_local_ext_features_dump(int level, struct frame *frm) +{ + read_local_ext_features_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("page %d\n", cp->page_num); +} + +static inline void request_clock_dump(int level, struct frame *frm) +{ + read_clock_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("handle %d which %d (%s)\n", + btohs(cp->handle), cp->which_clock, + cp->which_clock ? "piconet" : "local"); +} + +static inline void host_buffer_size_dump(int level, struct frame *frm) +{ + host_buffer_size_cp *cp = frm->ptr; + + p_indent(level, frm); + printf("ACL MTU %d:%d SCO MTU %d:%d\n", + btohs(cp->acl_mtu), btohs(cp->acl_max_pkt), + cp->sco_mtu, btohs(cp->sco_max_pkt)); +} + +static inline void num_comp_pkts_dump(int level, struct frame *frm) +{ + uint8_t num = get_u8(frm); + uint16_t handle, packets; + int i; + + for (i = 0; i < num; i++) { + handle = btohs(htons(get_u16(frm))); + packets = btohs(htons(get_u16(frm))); + + p_indent(level, frm); + printf("handle %d packets %d\n", handle, packets); + } +} + +static inline void command_dump(int level, struct frame *frm) +{ + hci_command_hdr *hdr = frm->ptr; + uint16_t opcode = btohs(hdr->opcode); + uint16_t ogf = cmd_opcode_ogf(opcode); + uint16_t ocf = cmd_opcode_ocf(opcode); + + if (p_filter(FILT_HCI)) + return; + + if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR)) + return; + + p_indent(level, frm); + printf("HCI Command: %s (0x%2.2x|0x%4.4x) plen %d\n", + opcode2str(opcode), ogf, ocf, hdr->plen); + + frm->ptr += HCI_COMMAND_HDR_SIZE; + frm->len -= HCI_COMMAND_HDR_SIZE; + + if (ogf == OGF_VENDOR_CMD) { + if (ocf == 0 && get_manufacturer() == 10) { + csr_dump(level + 1, frm); + return; + } + } + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level, frm); + return; + } + + switch (ogf) { + case OGF_LINK_CTL: + switch (ocf) { + case OCF_INQUIRY: + inquiry_dump(level + 1, frm); + return; + case OCF_PERIODIC_INQUIRY: + periodic_inquiry_dump(level + 1, frm); + return; + case OCF_INQUIRY_CANCEL: + case OCF_EXIT_PERIODIC_INQUIRY: + return; + case OCF_CREATE_CONN: + create_conn_dump(level + 1, frm); + return; + case OCF_DISCONNECT: + disconnect_dump(level + 1, frm); + return; + case OCF_CREATE_CONN_CANCEL: + case OCF_REMOTE_NAME_REQ_CANCEL: + case OCF_ACCEPT_SYNC_CONN_REQ: + bdaddr_command_dump(level + 1, frm); + return; + case OCF_ADD_SCO: + case OCF_SET_CONN_PTYPE: + add_sco_dump(level + 1, frm); + return; + case OCF_ACCEPT_CONN_REQ: + accept_conn_req_dump(level + 1, frm); + return; + case OCF_REJECT_CONN_REQ: + case OCF_REJECT_SYNC_CONN_REQ: + case OCF_IO_CAPABILITY_NEG_REPLY: + reject_conn_req_dump(level + 1, frm); + return; + case OCF_PIN_CODE_REPLY: + pin_code_reply_dump(level + 1, frm); + return; + case OCF_LINK_KEY_REPLY: + link_key_reply_dump(level + 1, frm); + return; + case OCF_PIN_CODE_NEG_REPLY: + case OCF_LINK_KEY_NEG_REPLY: + case OCF_USER_CONFIRM_REPLY: + case OCF_USER_CONFIRM_NEG_REPLY: + case OCF_USER_PASSKEY_NEG_REPLY: + case OCF_REMOTE_OOB_DATA_NEG_REPLY: + pin_code_neg_reply_dump(level + 1, frm); + return; + case OCF_USER_PASSKEY_REPLY: + user_passkey_reply_dump(level + 1, frm); + return; + case OCF_REMOTE_OOB_DATA_REPLY: + remote_oob_data_reply_dump(level + 1, frm); + return; + case OCF_IO_CAPABILITY_REPLY: + io_capability_reply_dump(level + 1, frm); + return; + case OCF_SET_CONN_ENCRYPT: + set_conn_encrypt_dump(level + 1, frm); + return; + case OCF_AUTH_REQUESTED: + case OCF_CHANGE_CONN_LINK_KEY: + case OCF_READ_REMOTE_FEATURES: + case OCF_READ_REMOTE_VERSION: + case OCF_READ_CLOCK_OFFSET: + case OCF_READ_LMP_HANDLE: + generic_command_dump(level + 1, frm); + return; + case OCF_MASTER_LINK_KEY: + master_link_key_dump(level + 1, frm); + return; + case OCF_READ_REMOTE_EXT_FEATURES: + read_remote_ext_features_dump(level + 1, frm); + return; + case OCF_REMOTE_NAME_REQ: + remote_name_req_dump(level + 1, frm); + return; + case OCF_SETUP_SYNC_CONN: + setup_sync_conn_dump(level + 1, frm); + return; + } + break; + + case OGF_LINK_POLICY: + switch (ocf) { + case OCF_HOLD_MODE: + case OCF_PARK_MODE: + hold_mode_dump(level + 1, frm); + return; + case OCF_SNIFF_MODE: + sniff_mode_dump(level + 1, frm); + return; + case OCF_EXIT_SNIFF_MODE: + case OCF_EXIT_PARK_MODE: + case OCF_ROLE_DISCOVERY: + case OCF_READ_LINK_POLICY: + generic_command_dump(level + 1, frm); + return; + case OCF_READ_DEFAULT_LINK_POLICY: + return; + case OCF_SWITCH_ROLE: + accept_conn_req_dump(level + 1, frm); + return; + case OCF_QOS_SETUP: + qos_setup_dump(level + 1, frm); + return; + case OCF_WRITE_LINK_POLICY: + write_link_policy_dump(level + 1, frm); + return; + case OCF_WRITE_DEFAULT_LINK_POLICY: + write_default_link_policy_dump(level + 1, frm); + return; + case OCF_SNIFF_SUBRATING: + sniff_subrating_dump(level + 1, frm); + return; + } + break; + + case OGF_HOST_CTL: + switch (ocf) { + case OCF_RESET: + case OCF_CREATE_NEW_UNIT_KEY: + return; + case OCF_SET_EVENT_MASK: + set_event_mask_dump(level + 1, frm); + return; + case OCF_SET_EVENT_FLT: + set_event_flt_dump(level + 1, frm); + return; + case OCF_WRITE_PIN_TYPE: + write_pin_type_dump(level + 1, frm); + return; + case OCF_READ_STORED_LINK_KEY: + case OCF_DELETE_STORED_LINK_KEY: + request_stored_link_key_dump(level + 1, frm); + return; + case OCF_WRITE_STORED_LINK_KEY: + return_link_keys_dump(level + 1, frm); + return; + case OCF_CHANGE_LOCAL_NAME: + change_local_name_dump(level + 1, frm); + return; + case OCF_WRITE_CLASS_OF_DEV: + write_class_of_dev_dump(level + 1, frm); + return; + case OCF_WRITE_VOICE_SETTING: + write_voice_setting_dump(level + 1, frm); + return; + case OCF_WRITE_CURRENT_IAC_LAP: + write_current_iac_lap_dump(level + 1, frm); + return; + case OCF_WRITE_SCAN_ENABLE: + case OCF_WRITE_AUTH_ENABLE: + case OCF_SET_CONTROLLER_TO_HOST_FC: + write_scan_enable_dump(level + 1, frm); + return; + case OCF_WRITE_CONN_ACCEPT_TIMEOUT: + case OCF_WRITE_PAGE_TIMEOUT: + write_page_timeout_dump(level + 1, frm); + return; + case OCF_WRITE_PAGE_ACTIVITY: + case OCF_WRITE_INQ_ACTIVITY: + write_page_activity_dump(level + 1, frm); + return; + case OCF_WRITE_INQUIRY_SCAN_TYPE: + write_inquiry_scan_type_dump(level + 1, frm); + return; + case OCF_WRITE_ENCRYPT_MODE: + case OCF_WRITE_INQUIRY_MODE: + case OCF_WRITE_AFH_MODE: + write_inquiry_mode_dump(level + 1, frm); + return; + case OCF_SET_AFH_CLASSIFICATION: + set_afh_classification_dump(level + 1, frm); + return; + case OCF_READ_TRANSMIT_POWER_LEVEL: + request_transmit_power_level_dump(level + 1, frm); + return; + case OCF_HOST_BUFFER_SIZE: + host_buffer_size_dump(level + 1, frm); + return; + case OCF_HOST_NUM_COMP_PKTS: + num_comp_pkts_dump(level + 1, frm); + return; + case OCF_FLUSH: + case OCF_READ_LINK_SUPERVISION_TIMEOUT: + case OCF_REFRESH_ENCRYPTION_KEY: + generic_command_dump(level + 1, frm); + return; + case OCF_WRITE_LINK_SUPERVISION_TIMEOUT: + write_link_supervision_timeout_dump(level + 1, frm); + return; + case OCF_WRITE_EXT_INQUIRY_RESPONSE: + write_ext_inquiry_response_dump(level + 1, frm); + return; + case OCF_WRITE_SIMPLE_PAIRING_MODE: + generic_write_mode_dump(level + 1, frm); + return; + case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL: + write_inquiry_transmit_power_level_dump(level + 1, frm); + return; + case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING: + write_default_error_data_reporting_dump(level + 1, frm); + return; + case OCF_ENHANCED_FLUSH: + enhanced_flush_dump(level + 1, frm); + return; + case OCF_SEND_KEYPRESS_NOTIFY: + send_keypress_notify_dump(level + 1, frm); + return; + } + break; + + case OGF_INFO_PARAM: + switch (ocf) { + case OCF_READ_LOCAL_EXT_FEATURES: + request_local_ext_features_dump(level + 1, frm); + return; + } + break; + + case OGF_STATUS_PARAM: + switch (ocf) { + case OCF_READ_LINK_QUALITY: + case OCF_READ_RSSI: + case OCF_READ_AFH_MAP: + generic_command_dump(level + 1, frm); + return; + case OCF_READ_CLOCK: + request_clock_dump(level + 1, frm); + return; + } + break; + + case OGF_TESTING_CMD: + switch (ocf) { + case OCF_WRITE_LOOPBACK_MODE: + case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE: + generic_write_mode_dump(level + 1, frm); + return; + } + break; + } + + raw_dump(level, frm); +} + +static inline void status_response_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + + p_indent(level, frm); + printf("status 0x%2.2x\n", status); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } + + raw_dump(level, frm); +} + +static inline void handle_response_dump(int level, struct frame *frm) +{ + uint16_t handle = btohs(htons(get_u16(frm))); + + p_indent(level, frm); + printf("handle %d\n", handle); + + raw_dump(level, frm); +} + +static inline void bdaddr_response_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + bdaddr_t *bdaddr = frm->ptr; + char addr[18]; + + frm->ptr += sizeof(bdaddr_t); + frm->len -= sizeof(bdaddr_t); + + p_indent(level, frm); + p_ba2str(bdaddr, addr); + printf("status 0x%2.2x bdaddr %s\n", status, addr); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } + + raw_dump(level, frm); +} + +static inline void generic_response_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + uint16_t handle = btohs(htons(get_u16(frm))); + + p_indent(level, frm); + printf("status 0x%2.2x handle %d\n", status, handle); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } + + raw_dump(level, frm); +} + +static inline void status_mode_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + uint8_t mode = get_u8(frm); + + p_indent(level, frm); + printf("status 0x%2.2x mode 0x%2.2x\n", status, mode); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } +} + +static inline void read_link_policy_dump(int level, struct frame *frm) +{ + read_link_policy_rp *rp = frm->ptr; + uint16_t policy = btohs(rp->policy); + char *str; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d policy 0x%2.2x\n", + rp->status, btohs(rp->handle), policy); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + str = hci_lptostr(policy); + if (str) { + p_indent(level, frm); + printf("Link policy: %s\n", str); + free(str); + } + } +} + +static inline void read_default_link_policy_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + uint16_t policy = btohs(htons(get_u16(frm))); + char *str; + + p_indent(level, frm); + printf("status 0x%2.2x policy 0x%2.2x\n", status, policy); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } else { + str = hci_lptostr(policy); + if (str) { + p_indent(level, frm); + printf("Link policy: %s\n", str); + free(str); + } + } +} + +static inline void read_pin_type_dump(int level, struct frame *frm) +{ + read_pin_type_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x type %d\n", rp->status, rp->pin_type); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_stored_link_key_dump(int level, struct frame *frm) +{ + read_stored_link_key_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x max %d num %d\n", + rp->status, rp->max_keys, rp->num_keys); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void write_stored_link_key_dump(int level, struct frame *frm) +{ + write_stored_link_key_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x written %d\n", rp->status, rp->num_keys); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void delete_stored_link_key_dump(int level, struct frame *frm) +{ + delete_stored_link_key_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x deleted %d\n", rp->status, btohs(rp->num_keys)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_local_name_dump(int level, struct frame *frm) +{ + read_local_name_rp *rp = frm->ptr; + char name[249]; + int i; + + memset(name, 0, sizeof(name)); + for (i = 0; i < 248 && rp->name[i]; i++) + if (isprint(rp->name[i])) + name[i] = rp->name[i]; + else + name[i] = '.'; + + p_indent(level, frm); + printf("status 0x%2.2x name \'%s\'\n", rp->status, name); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_class_of_dev_dump(int level, struct frame *frm) +{ + read_class_of_dev_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x class 0x%2.2x%2.2x%2.2x\n", rp->status, + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_voice_setting_dump(int level, struct frame *frm) +{ + read_voice_setting_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x voice setting 0x%4.4x\n", + rp->status, btohs(rp->voice_setting)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_current_iac_lap_dump(int level, struct frame *frm) +{ + read_current_iac_lap_rp *rp = frm->ptr; + int i; + + for (i = 0; i < rp->num_current_iac; i++) { + p_indent(level, frm); + printf("IAC 0x%2.2x%2.2x%2.2x", rp->lap[i][2], rp->lap[i][1], rp->lap[i][0]); + if (rp->lap[i][2] == 0x9e && rp->lap[i][1] == 0x8b) { + switch (rp->lap[i][0]) { + case 0x00: + printf(" (Limited Inquiry Access Code)"); + break; + case 0x33: + printf(" (General Inquiry Access Code)"); + break; + } + } + printf("\n"); + } +} + +static inline void read_scan_enable_dump(int level, struct frame *frm) +{ + uint8_t status = get_u8(frm); + uint8_t enable = get_u8(frm); + + p_indent(level, frm); + printf("status 0x%2.2x enable %d\n", status, enable); + + if (status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(status)); + } +} + +static inline void read_page_timeout_dump(int level, struct frame *frm) +{ + read_page_timeout_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x timeout %d\n", rp->status, btohs(rp->timeout)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_page_activity_dump(int level, struct frame *frm) +{ + read_page_activity_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x interval %d window %d\n", + rp->status, btohs(rp->interval), btohs(rp->window)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_inquiry_scan_type_dump(int level, struct frame *frm) +{ + read_inquiry_scan_type_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x type %d\n", rp->status, rp->type); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_inquiry_mode_dump(int level, struct frame *frm) +{ + read_inquiry_mode_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x mode %d\n", rp->status, rp->mode); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_link_supervision_timeout_dump(int level, struct frame *frm) +{ + read_link_supervision_timeout_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d timeout %d\n", + rp->status, btohs(rp->handle), btohs(rp->timeout)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_transmit_power_level_dump(int level, struct frame *frm) +{ + read_transmit_power_level_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d level %d\n", + rp->status, btohs(rp->handle), rp->level); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_ext_inquiry_response_dump(int level, struct frame *frm) +{ + read_ext_inquiry_response_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x fec 0x%2.2x\n", rp->status, rp->fec); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + frm->ptr += 2; + frm->len -= 2; + + ext_inquiry_response_dump(level, frm); + } +} + +static inline void read_inquiry_transmit_power_level_dump(int level, struct frame *frm) +{ + read_inquiry_transmit_power_level_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x level %d\n", rp->status, rp->level); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_default_error_data_reporting_dump(int level, struct frame *frm) +{ + read_default_error_data_reporting_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x reporting %d\n", rp->status, rp->reporting); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_local_oob_data_dump(int level, struct frame *frm) +{ + read_local_oob_data_rp *rp = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x\n", rp->status); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("hash 0x"); + for (i = 0; i < 16; i++) + printf("%02x", rp->hash[i]); + printf("\n"); + + p_indent(level, frm); + printf("randomizer 0x"); + for (i = 0; i < 16; i++) + printf("%02x", rp->randomizer[i]); + printf("\n"); + } +} + +static inline void read_local_version_dump(int level, struct frame *frm) +{ + read_local_version_rp *rp = frm->ptr; + uint16_t manufacturer = btohs(rp->manufacturer); + + p_indent(level, frm); + printf("status 0x%2.2x\n", rp->status); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("HCI Version: %s (0x%x) HCI Revision: 0x%x\n", + hci_vertostr(rp->hci_ver), + rp->hci_ver, btohs(rp->hci_rev)); + p_indent(level, frm); + printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n", + lmp_vertostr(rp->lmp_ver), + rp->lmp_ver, btohs(rp->lmp_subver)); + p_indent(level, frm); + printf("Manufacturer: %s (%d)\n", + bt_compidtostr(manufacturer), manufacturer); + } +} + +static inline void read_local_commands_dump(int level, struct frame *frm) +{ + read_local_commands_rp *rp = frm->ptr; + int i, max = 0; + + p_indent(level, frm); + printf("status 0x%2.2x\n", rp->status); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + for (i = 0; i < 64; i++) + if (rp->commands[i]) + max = i + 1; + p_indent(level, frm); + printf("Commands: "); + for (i = 0; i < (max > 32 ? 32 : max); i++) + printf("%2.2x", rp->commands[i]); + printf("\n"); + if (max > 32) { + p_indent(level, frm); + printf(" "); + for (i = 32; i < max; i++) + printf("%2.2x", rp->commands[i]); + printf("\n"); + } + } +} + +static inline void read_local_features_dump(int level, struct frame *frm) +{ + read_local_features_rp *rp = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x\n", rp->status); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("Features:"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", rp->features[i]); + printf("\n"); + } +} + +static inline void read_local_ext_features_dump(int level, struct frame *frm) +{ + read_local_ext_features_rp *rp = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x page %d max %d\n", + rp->status, rp->page_num, rp->max_page_num); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("Features:"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", rp->features[i]); + printf("\n"); + } +} + +static inline void read_buffer_size_dump(int level, struct frame *frm) +{ + read_buffer_size_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x\n", rp->status); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("ACL MTU %d:%d SCO MTU %d:%d\n", + btohs(rp->acl_mtu), btohs(rp->acl_max_pkt), + rp->sco_mtu, btohs(rp->sco_max_pkt)); + } +} + +static inline void read_link_quality_dump(int level, struct frame *frm) +{ + read_link_quality_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d lq %d\n", + rp->status, btohs(rp->handle), rp->link_quality); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_rssi_dump(int level, struct frame *frm) +{ + read_rssi_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d rssi %d\n", + rp->status, btohs(rp->handle), rp->rssi); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void read_afh_map_dump(int level, struct frame *frm) +{ + read_afh_map_rp *rp = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d mode %d\n", + rp->status, btohs(rp->handle), rp->mode); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } else { + p_indent(level, frm); + printf("AFH map: 0x"); + for (i = 0; i < 10; i++) + printf("%2.2x", rp->map[i]); + printf("\n"); + } +} + +static inline void read_clock_dump(int level, struct frame *frm) +{ + read_clock_rp *rp = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d clock 0x%4.4x accuracy %d\n", + rp->status, btohs(rp->handle), + btohl(rp->clock), btohs(rp->accuracy)); + + if (rp->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(rp->status)); + } +} + +static inline void cmd_complete_dump(int level, struct frame *frm) +{ + evt_cmd_complete *evt = frm->ptr; + uint16_t opcode = btohs(evt->opcode); + uint16_t ogf = cmd_opcode_ogf(opcode); + uint16_t ocf = cmd_opcode_ocf(opcode); + + if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR)) + return; + + p_indent(level, frm); + printf("%s (0x%2.2x|0x%4.4x) ncmd %d\n", + opcode2str(opcode), ogf, ocf, evt->ncmd); + + frm->ptr += EVT_CMD_COMPLETE_SIZE; + frm->len -= EVT_CMD_COMPLETE_SIZE; + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level, frm); + return; + } + + switch (ogf) { + case OGF_LINK_CTL: + switch (ocf) { + case OCF_INQUIRY_CANCEL: + case OCF_PERIODIC_INQUIRY: + case OCF_EXIT_PERIODIC_INQUIRY: + case OCF_READ_REMOTE_EXT_FEATURES: + status_response_dump(level, frm); + return; + case OCF_CREATE_CONN_CANCEL: + case OCF_REMOTE_NAME_REQ_CANCEL: + case OCF_PIN_CODE_REPLY: + case OCF_LINK_KEY_REPLY: + case OCF_PIN_CODE_NEG_REPLY: + case OCF_LINK_KEY_NEG_REPLY: + case OCF_USER_CONFIRM_REPLY: + case OCF_USER_CONFIRM_NEG_REPLY: + case OCF_USER_PASSKEY_REPLY: + case OCF_USER_PASSKEY_NEG_REPLY: + case OCF_REMOTE_OOB_DATA_REPLY: + case OCF_REMOTE_OOB_DATA_NEG_REPLY: + case OCF_IO_CAPABILITY_REPLY: + case OCF_IO_CAPABILITY_NEG_REPLY: + bdaddr_response_dump(level, frm); + return; + } + break; + + case OGF_LINK_POLICY: + switch (ocf) { + case OCF_READ_LINK_POLICY: + read_link_policy_dump(level, frm); + return; + case OCF_WRITE_LINK_POLICY: + case OCF_SNIFF_SUBRATING: + generic_response_dump(level, frm); + return; + case OCF_READ_DEFAULT_LINK_POLICY: + read_default_link_policy_dump(level, frm); + return; + case OCF_WRITE_DEFAULT_LINK_POLICY: + status_response_dump(level, frm); + return; + } + break; + + case OGF_HOST_CTL: + switch (ocf) { + case OCF_READ_PIN_TYPE: + read_pin_type_dump(level, frm); + return; + case OCF_READ_STORED_LINK_KEY: + read_stored_link_key_dump(level, frm); + return; + case OCF_WRITE_STORED_LINK_KEY: + write_stored_link_key_dump(level, frm); + return; + case OCF_DELETE_STORED_LINK_KEY: + delete_stored_link_key_dump(level, frm); + return; + case OCF_READ_LOCAL_NAME: + read_local_name_dump(level, frm); + return; + case OCF_READ_CLASS_OF_DEV: + read_class_of_dev_dump(level, frm); + return; + case OCF_READ_VOICE_SETTING: + read_voice_setting_dump(level, frm); + return; + case OCF_READ_CURRENT_IAC_LAP: + read_current_iac_lap_dump(level, frm); + return; + case OCF_READ_SCAN_ENABLE: + case OCF_READ_AUTH_ENABLE: + read_scan_enable_dump(level, frm); + return; + case OCF_READ_CONN_ACCEPT_TIMEOUT: + case OCF_READ_PAGE_TIMEOUT: + read_page_timeout_dump(level, frm); + return; + case OCF_READ_PAGE_ACTIVITY: + case OCF_READ_INQ_ACTIVITY: + read_page_activity_dump(level, frm); + return; + case OCF_READ_INQUIRY_SCAN_TYPE: + read_inquiry_scan_type_dump(level, frm); + return; + case OCF_READ_ENCRYPT_MODE: + case OCF_READ_INQUIRY_MODE: + case OCF_READ_AFH_MODE: + read_inquiry_mode_dump(level, frm); + return; + case OCF_READ_LINK_SUPERVISION_TIMEOUT: + read_link_supervision_timeout_dump(level, frm); + return; + case OCF_READ_TRANSMIT_POWER_LEVEL: + read_transmit_power_level_dump(level, frm); + return; + case OCF_READ_EXT_INQUIRY_RESPONSE: + read_ext_inquiry_response_dump(level, frm); + return; + case OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL: + read_inquiry_transmit_power_level_dump(level, frm); + return; + case OCF_READ_DEFAULT_ERROR_DATA_REPORTING: + read_default_error_data_reporting_dump(level, frm); + return; + case OCF_READ_LOCAL_OOB_DATA: + read_local_oob_data_dump(level, frm); + return; + case OCF_READ_SIMPLE_PAIRING_MODE: + status_mode_dump(level, frm); + return; + case OCF_FLUSH: + case OCF_WRITE_LINK_SUPERVISION_TIMEOUT: + generic_response_dump(level, frm); + return; + case OCF_RESET: + case OCF_SET_EVENT_MASK: + case OCF_SET_EVENT_FLT: + case OCF_WRITE_PIN_TYPE: + case OCF_CREATE_NEW_UNIT_KEY: + case OCF_CHANGE_LOCAL_NAME: + case OCF_WRITE_CLASS_OF_DEV: + case OCF_WRITE_VOICE_SETTING: + case OCF_WRITE_CURRENT_IAC_LAP: + case OCF_WRITE_SCAN_ENABLE: + case OCF_WRITE_AUTH_ENABLE: + case OCF_WRITE_ENCRYPT_MODE: + case OCF_WRITE_CONN_ACCEPT_TIMEOUT: + case OCF_WRITE_PAGE_TIMEOUT: + case OCF_WRITE_PAGE_ACTIVITY: + case OCF_WRITE_INQ_ACTIVITY: + case OCF_WRITE_INQUIRY_SCAN_TYPE: + case OCF_WRITE_INQUIRY_MODE: + case OCF_WRITE_AFH_MODE: + case OCF_SET_AFH_CLASSIFICATION: + case OCF_WRITE_EXT_INQUIRY_RESPONSE: + case OCF_WRITE_SIMPLE_PAIRING_MODE: + case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL: + case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING: + case OCF_SET_CONTROLLER_TO_HOST_FC: + case OCF_HOST_BUFFER_SIZE: + case OCF_REFRESH_ENCRYPTION_KEY: + case OCF_SEND_KEYPRESS_NOTIFY: + status_response_dump(level, frm); + return; + } + break; + + case OGF_INFO_PARAM: + switch (ocf) { + case OCF_READ_LOCAL_VERSION: + read_local_version_dump(level, frm); + return; + case OCF_READ_LOCAL_COMMANDS: + read_local_commands_dump(level, frm); + return; + case OCF_READ_LOCAL_FEATURES: + read_local_features_dump(level, frm); + return; + case OCF_READ_LOCAL_EXT_FEATURES: + read_local_ext_features_dump(level, frm); + return; + case OCF_READ_BUFFER_SIZE: + read_buffer_size_dump(level, frm); + return; + case OCF_READ_BD_ADDR: + bdaddr_response_dump(level, frm); + return; + } + break; + + case OGF_STATUS_PARAM: + switch (ocf) { + case OCF_READ_FAILED_CONTACT_COUNTER: + case OCF_RESET_FAILED_CONTACT_COUNTER: + status_response_dump(level, frm); + return; + case OCF_READ_LINK_QUALITY: + read_link_quality_dump(level, frm); + return; + case OCF_READ_RSSI: + read_rssi_dump(level, frm); + return; + case OCF_READ_AFH_MAP: + read_afh_map_dump(level, frm); + return; + case OCF_READ_CLOCK: + read_clock_dump(level, frm); + return; + } + break; + + case OGF_TESTING_CMD: + switch (ocf) { + case OCF_READ_LOOPBACK_MODE: + status_mode_dump(level, frm); + return; + case OCF_WRITE_LOOPBACK_MODE: + case OCF_ENABLE_DEVICE_UNDER_TEST_MODE: + case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE: + status_response_dump(level, frm); + return; + } + break; + } + + raw_dump(level, frm); +} + +static inline void cmd_status_dump(int level, struct frame *frm) +{ + evt_cmd_status *evt = frm->ptr; + uint16_t opcode = btohs(evt->opcode); + uint16_t ogf = cmd_opcode_ogf(opcode); + uint16_t ocf = cmd_opcode_ocf(opcode); + + if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR)) + return; + + p_indent(level, frm); + printf("%s (0x%2.2x|0x%4.4x) status 0x%2.2x ncmd %d\n", + opcode2str(opcode), ogf, ocf, evt->status, evt->ncmd); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void hardware_error_dump(int level, struct frame *frm) +{ + evt_hardware_error *evt = frm->ptr; + + p_indent(level, frm); + printf("code %d\n", evt->code); +} + +static inline void inq_result_dump(int level, struct frame *frm) +{ + uint8_t num = get_u8(frm); + char addr[18]; + int i; + + for (i = 0; i < num; i++) { + inquiry_info *info = frm->ptr; + + p_ba2str(&info->bdaddr, addr); + + p_indent(level, frm); + printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x\n", + addr, info->pscan_rep_mode, btohs(info->clock_offset), + info->dev_class[2], info->dev_class[1], info->dev_class[0]); + + frm->ptr += INQUIRY_INFO_SIZE; + frm->len -= INQUIRY_INFO_SIZE; + } +} + +static inline void conn_complete_dump(int level, struct frame *frm) +{ + evt_conn_complete *evt = frm->ptr; + char addr[18]; + + p_ba2str(&evt->bdaddr, addr); + + p_indent(level, frm); + printf("status 0x%2.2x handle %d bdaddr %s type %s encrypt 0x%2.2x\n", + evt->status, btohs(evt->handle), addr, + linktype2str(evt->link_type), evt->encr_mode); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void conn_request_dump(int level, struct frame *frm) +{ + evt_conn_request *evt = frm->ptr; + char addr[18]; + + p_ba2str(&evt->bdaddr, addr); + + p_indent(level, frm); + printf("bdaddr %s class 0x%2.2x%2.2x%2.2x type %s\n", + addr, evt->dev_class[2], evt->dev_class[1], + evt->dev_class[0], linktype2str(evt->link_type)); +} + +static inline void disconn_complete_dump(int level, struct frame *frm) +{ + evt_disconn_complete *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d reason 0x%2.2x\n", + evt->status, btohs(evt->handle), evt->reason); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else if (evt->reason > 0) { + p_indent(level, frm); + printf("Reason: %s\n", status2str(evt->reason)); + } +} + +static inline void remote_name_req_complete_dump(int level, struct frame *frm) +{ + evt_remote_name_req_complete *evt = frm->ptr; + char addr[18], name[249]; + int i; + + p_ba2str(&evt->bdaddr, addr); + + memset(name, 0, sizeof(name)); + for (i = 0; i < 248 && evt->name[i]; i++) + if (isprint(evt->name[i])) + name[i] = evt->name[i]; + else + name[i] = '.'; + + p_indent(level, frm); + printf("status 0x%2.2x bdaddr %s name '%s'\n", evt->status, addr, name); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void master_link_key_complete_dump(int level, struct frame *frm) +{ + evt_master_link_key_complete *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d flag %d\n", + evt->status, btohs(evt->handle), evt->key_flag); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void encrypt_change_dump(int level, struct frame *frm) +{ + evt_encrypt_change *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d encrypt 0x%2.2x\n", + evt->status, btohs(evt->handle), evt->encrypt); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void read_remote_features_complete_dump(int level, struct frame *frm) +{ + evt_read_remote_features_complete *evt = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Features:"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", evt->features[i]); + printf("\n"); + } +} + +static inline void read_remote_version_complete_dump(int level, struct frame *frm) +{ + evt_read_remote_version_complete *evt = frm->ptr; + uint16_t manufacturer = btohs(evt->manufacturer); + + p_indent(level, frm); + printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n", + lmp_vertostr(evt->lmp_ver), evt->lmp_ver, + btohs(evt->lmp_subver)); + p_indent(level, frm); + printf("Manufacturer: %s (%d)\n", + bt_compidtostr(manufacturer), manufacturer); + } +} + +static inline void qos_setup_complete_dump(int level, struct frame *frm) +{ + evt_qos_setup_complete *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d flags %d\n", + evt->status, btohs(evt->handle), evt->flags); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Service type: %d\n", evt->qos.service_type); + p_indent(level, frm); + printf("Token rate: %d\n", btohl(evt->qos.token_rate)); + p_indent(level, frm); + printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth)); + p_indent(level, frm); + printf("Latency: %d\n", btohl(evt->qos.latency)); + p_indent(level, frm); + printf("Delay variation: %d\n", btohl(evt->qos.delay_variation)); + } +} + +static inline void role_change_dump(int level, struct frame *frm) +{ + evt_role_change *evt = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("status 0x%2.2x bdaddr %s role 0x%2.2x\n", + evt->status, addr, evt->role); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Role: %s\n", role2str(evt->role)); + } +} + +static inline void mode_change_dump(int level, struct frame *frm) +{ + evt_mode_change *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d mode 0x%2.2x interval %d\n", + evt->status, btohs(evt->handle), evt->mode, btohs(evt->interval)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Mode: %s\n", mode2str(evt->mode)); + } +} + +static inline void pin_code_req_dump(int level, struct frame *frm) +{ + evt_pin_code_req *evt = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s\n", addr); +} + +static inline void link_key_notify_dump(int level, struct frame *frm) +{ + evt_link_key_notify *evt = frm->ptr; + char addr[18]; + int i; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s key ", addr); + for (i = 0; i < 16; i++) + if (parser.flags & DUMP_NOVENDOR) + printf("**"); + else + printf("%2.2X", evt->link_key[i]); + printf(" type %d\n", evt->key_type); + + p_indent(level, frm); + printf("Type: %s\n", keytype2str(evt->key_type)); +} + +static inline void max_slots_change_dump(int level, struct frame *frm) +{ + evt_max_slots_change *evt = frm->ptr; + + p_indent(level, frm); + printf("handle %d slots %d\n", btohs(evt->handle), evt->max_slots); +} + +static inline void data_buffer_overflow_dump(int level, struct frame *frm) +{ + evt_data_buffer_overflow *evt = frm->ptr; + + p_indent(level, frm); + printf("type %s\n", linktype2str(evt->link_type)); +} + +static inline void read_clock_offset_complete_dump(int level, struct frame *frm) +{ + evt_read_clock_offset_complete *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d clkoffset 0x%4.4x\n", + evt->status, btohs(evt->handle), btohs(evt->clock_offset)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void conn_ptype_changed_dump(int level, struct frame *frm) +{ + evt_conn_ptype_changed *evt = frm->ptr; + uint16_t ptype = btohs(evt->ptype); + char *str; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d ptype 0x%4.4x\n", + evt->status, btohs(evt->handle), ptype); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + str = hci_ptypetostr(ptype); + if (str) { + p_indent(level, frm); + printf("Packet type: %s\n", str); + free(str); + } + } +} + +static inline void pscan_rep_mode_change_dump(int level, struct frame *frm) +{ + evt_pscan_rep_mode_change *evt = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s mode %d\n", addr, evt->pscan_rep_mode); +} + +static inline void flow_spec_complete_dump(int level, struct frame *frm) +{ + evt_flow_spec_complete *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d flags %d %s\n", + evt->status, btohs(evt->handle), evt->flags, + evt->direction == 0 ? "outgoing" : "incoming"); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Service type: %d\n", evt->qos.service_type); + p_indent(level, frm); + printf("Token rate: %d\n", btohl(evt->qos.token_rate)); + p_indent(level, frm); + printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth)); + p_indent(level, frm); + printf("Latency: %d\n", btohl(evt->qos.latency)); + p_indent(level, frm); + printf("Delay variation: %d\n", btohl(evt->qos.delay_variation)); + } +} + +static inline void inq_result_with_rssi_dump(int level, struct frame *frm) +{ + uint8_t num = get_u8(frm); + char addr[18]; + int i; + + if (!num) + return; + + if (frm->len / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) { + for (i = 0; i < num; i++) { + inquiry_info_with_rssi_and_pscan_mode *info = frm->ptr; + + p_indent(level, frm); + + p_ba2str(&info->bdaddr, addr); + printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n", + addr, info->pscan_rep_mode, btohs(info->clock_offset), + info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi); + + frm->ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE; + frm->len -= INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE; + } + } else { + for (i = 0; i < num; i++) { + inquiry_info_with_rssi *info = frm->ptr; + + p_indent(level, frm); + + p_ba2str(&info->bdaddr, addr); + printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n", + addr, info->pscan_rep_mode, btohs(info->clock_offset), + info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi); + + frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE; + frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE; + } + } +} + +static inline void read_remote_ext_features_complete_dump(int level, struct frame *frm) +{ + evt_read_remote_ext_features_complete *evt = frm->ptr; + int i; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d page %d max %d\n", + evt->status, btohs(evt->handle), + evt->page_num, evt->max_page_num); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Features:"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", evt->features[i]); + printf("\n"); + } +} + +static inline void sync_conn_complete_dump(int level, struct frame *frm) +{ + evt_sync_conn_complete *evt = frm->ptr; + char addr[18]; + + p_ba2str(&evt->bdaddr, addr); + + p_indent(level, frm); + printf("status 0x%2.2x handle %d bdaddr %s type %s\n", + evt->status, btohs(evt->handle), addr, + evt->link_type == 0 ? "SCO" : "eSCO"); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("Air mode: %s\n", airmode2str(evt->air_mode)); + } +} + +static inline void sync_conn_changed_dump(int level, struct frame *frm) +{ + evt_sync_conn_changed *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } +} + +static inline void sniff_subrating_event_dump(int level, struct frame *frm) +{ + evt_sniff_subrating *evt = frm->ptr; + + p_indent(level, frm); + printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle)); + + if (evt->status > 0) { + p_indent(level, frm); + printf("Error: %s\n", status2str(evt->status)); + } else { + p_indent(level, frm); + printf("max latency transmit %d receive %d\n", + btohs(evt->max_tx_latency), + btohs(evt->max_rx_latency)); + + p_indent(level, frm); + printf("min timeout remote %d local %d\n", + btohs(evt->min_remote_timeout), + btohs(evt->min_local_timeout)); + } +} + +static inline void extended_inq_result_dump(int level, struct frame *frm) +{ + uint8_t num = get_u8(frm); + char addr[18]; + int i; + + for (i = 0; i < num; i++) { + extended_inquiry_info *info = frm->ptr; + + p_ba2str(&info->bdaddr, addr); + + p_indent(level, frm); + printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n", + addr, info->pscan_rep_mode, btohs(info->clock_offset), + info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi); + + frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE; + frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE; + + ext_inquiry_response_dump(level, frm); + } +} + +static inline void link_supervision_timeout_changed_dump(int level, struct frame *frm) +{ + evt_link_supervision_timeout_changed *evt = frm->ptr; + + p_indent(level, frm); + printf("handle %d timeout %d\n", + btohs(evt->handle), btohs(evt->timeout)); +} + +static inline void user_passkey_notify_dump(int level, struct frame *frm) +{ + evt_user_passkey_notify *evt = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s passkey %d\n", addr, btohl(evt->passkey)); +} + +static inline void keypress_notify_dump(int level, struct frame *frm) +{ + evt_keypress_notify *evt = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s type %d\n", addr, evt->type); +} + +static inline void remote_host_features_notify_dump(int level, struct frame *frm) +{ + evt_remote_host_features_notify *evt = frm->ptr; + char addr[18]; + int i; + + p_indent(level, frm); + p_ba2str(&evt->bdaddr, addr); + printf("bdaddr %s\n", addr); + + p_indent(level, frm); + printf("Features:"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", evt->features[i]); + printf("\n"); +} + +static inline void event_dump(int level, struct frame *frm) +{ + hci_event_hdr *hdr = frm->ptr; + uint8_t event = hdr->evt; + + if (p_filter(FILT_HCI)) + return; + + if (event <= EVENT_NUM) { + p_indent(level, frm); + printf("HCI Event: %s (0x%2.2x) plen %d\n", + event_str[hdr->evt], hdr->evt, hdr->plen); + } else if (hdr->evt == EVT_TESTING) { + p_indent(level, frm); + printf("HCI Event: Testing (0x%2.2x) plen %d\n", hdr->evt, hdr->plen); + } else if (hdr->evt == EVT_VENDOR) { + uint16_t manufacturer; + + if (parser.flags & DUMP_NOVENDOR) + return; + + p_indent(level, frm); + printf("HCI Event: Vendor (0x%2.2x) plen %d\n", hdr->evt, hdr->plen); + + manufacturer = get_manufacturer(); + + switch (manufacturer) { + case 0: + case 37: + case 48: + frm->ptr += HCI_EVENT_HDR_SIZE; + frm->len -= HCI_EVENT_HDR_SIZE; + ericsson_dump(level + 1, frm); + return; + case 10: + frm->ptr += HCI_EVENT_HDR_SIZE; + frm->len -= HCI_EVENT_HDR_SIZE; + csr_dump(level + 1, frm); + return; + } + } else { + p_indent(level, frm); + printf("HCI Event: code 0x%2.2x plen %d\n", hdr->evt, hdr->plen); + } + + frm->ptr += HCI_EVENT_HDR_SIZE; + frm->len -= HCI_EVENT_HDR_SIZE; + + if (event == EVT_CMD_COMPLETE) { + evt_cmd_complete *cc = frm->ptr; + if (cc->opcode == cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION)) { + read_local_version_rp *rp = frm->ptr + EVT_CMD_COMPLETE_SIZE; + manufacturer = rp->manufacturer; + } + } + + if (event == EVT_DISCONN_COMPLETE) { + evt_disconn_complete *evt = frm->ptr; + l2cap_clear(btohs(evt->handle)); + } + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level, frm); + return; + } + + switch (event) { + case EVT_LOOPBACK_COMMAND: + command_dump(level + 1, frm); + break; + case EVT_CMD_COMPLETE: + cmd_complete_dump(level + 1, frm); + break; + case EVT_CMD_STATUS: + cmd_status_dump(level + 1, frm); + break; + case EVT_HARDWARE_ERROR: + hardware_error_dump(level + 1, frm); + break; + case EVT_FLUSH_OCCURRED: + case EVT_QOS_VIOLATION: + handle_response_dump(level + 1, frm); + break; + case EVT_INQUIRY_COMPLETE: + status_response_dump(level + 1, frm); + break; + case EVT_INQUIRY_RESULT: + inq_result_dump(level + 1, frm); + break; + case EVT_CONN_COMPLETE: + conn_complete_dump(level + 1, frm); + break; + case EVT_CONN_REQUEST: + conn_request_dump(level + 1, frm); + break; + case EVT_DISCONN_COMPLETE: + disconn_complete_dump(level + 1, frm); + break; + case EVT_AUTH_COMPLETE: + case EVT_CHANGE_CONN_LINK_KEY_COMPLETE: + generic_response_dump(level + 1, frm); + break; + case EVT_MASTER_LINK_KEY_COMPLETE: + master_link_key_complete_dump(level + 1, frm); + break; + case EVT_REMOTE_NAME_REQ_COMPLETE: + remote_name_req_complete_dump(level + 1, frm); + break; + case EVT_ENCRYPT_CHANGE: + encrypt_change_dump(level + 1, frm); + break; + case EVT_READ_REMOTE_FEATURES_COMPLETE: + read_remote_features_complete_dump(level + 1, frm); + break; + case EVT_READ_REMOTE_VERSION_COMPLETE: + read_remote_version_complete_dump(level + 1, frm); + break; + case EVT_QOS_SETUP_COMPLETE: + qos_setup_complete_dump(level + 1, frm); + break; + case EVT_ROLE_CHANGE: + role_change_dump(level + 1, frm); + break; + case EVT_NUM_COMP_PKTS: + num_comp_pkts_dump(level + 1, frm); + break; + case EVT_MODE_CHANGE: + mode_change_dump(level + 1, frm); + break; + case EVT_RETURN_LINK_KEYS: + return_link_keys_dump(level + 1, frm); + break; + case EVT_PIN_CODE_REQ: + case EVT_LINK_KEY_REQ: + case EVT_IO_CAPABILITY_REQUEST: + case EVT_USER_PASSKEY_REQUEST: + case EVT_REMOTE_OOB_DATA_REQUEST: + pin_code_req_dump(level + 1, frm); + break; + case EVT_LINK_KEY_NOTIFY: + link_key_notify_dump(level + 1, frm); + break; + case EVT_DATA_BUFFER_OVERFLOW: + data_buffer_overflow_dump(level + 1, frm); + break; + case EVT_MAX_SLOTS_CHANGE: + max_slots_change_dump(level + 1, frm); + break; + case EVT_READ_CLOCK_OFFSET_COMPLETE: + read_clock_offset_complete_dump(level + 1, frm); + break; + case EVT_CONN_PTYPE_CHANGED: + conn_ptype_changed_dump(level + 1, frm); + break; + case EVT_PSCAN_REP_MODE_CHANGE: + pscan_rep_mode_change_dump(level + 1, frm); + break; + case EVT_FLOW_SPEC_COMPLETE: + flow_spec_complete_dump(level + 1, frm); + break; + case EVT_INQUIRY_RESULT_WITH_RSSI: + inq_result_with_rssi_dump(level + 1, frm); + break; + case EVT_READ_REMOTE_EXT_FEATURES_COMPLETE: + read_remote_ext_features_complete_dump(level + 1, frm); + break; + case EVT_SYNC_CONN_COMPLETE: + sync_conn_complete_dump(level + 1, frm); + break; + case EVT_SYNC_CONN_CHANGED: + sync_conn_changed_dump(level + 1, frm); + break; + case EVT_SNIFF_SUBRATING: + sniff_subrating_event_dump(level + 1, frm); + break; + case EVT_EXTENDED_INQUIRY_RESULT: + extended_inq_result_dump(level + 1, frm); + break; + case EVT_ENCRYPTION_KEY_REFRESH_COMPLETE: + generic_response_dump(level + 1, frm); + break; + case EVT_SIMPLE_PAIRING_COMPLETE: + bdaddr_response_dump(level + 1, frm); + break; + case EVT_LINK_SUPERVISION_TIMEOUT_CHANGED: + link_supervision_timeout_changed_dump(level + 1, frm); + break; + case EVT_ENHANCED_FLUSH_COMPLETE: + generic_command_dump(level + 1, frm); + break; + case EVT_IO_CAPABILITY_RESPONSE: + io_capability_reply_dump(level + 1, frm); + break; + case EVT_USER_CONFIRM_REQUEST: + case EVT_USER_PASSKEY_NOTIFY: + user_passkey_notify_dump(level + 1, frm); + break; + case EVT_KEYPRESS_NOTIFY: + keypress_notify_dump(level + 1, frm); + break; + case EVT_REMOTE_HOST_FEATURES_NOTIFY: + remote_host_features_notify_dump(level + 1, frm); + break; + default: + raw_dump(level, frm); + break; + } +} + +static inline void acl_dump(int level, struct frame *frm) +{ + hci_acl_hdr *hdr = (void *) frm->ptr; + uint16_t handle = btohs(hdr->handle); + uint16_t dlen = btohs(hdr->dlen); + uint8_t flags = acl_flags(handle); + + if (!p_filter(FILT_HCI)) { + p_indent(level, frm); + printf("ACL data: handle %d flags 0x%2.2x dlen %d\n", + acl_handle(handle), flags, dlen); + level++; + } + + frm->ptr += HCI_ACL_HDR_SIZE; + frm->len -= HCI_ACL_HDR_SIZE; + frm->flags = flags; + frm->handle = acl_handle(handle); + + if (parser.filter & ~FILT_HCI) + l2cap_dump(level, frm); + else + raw_dump(level, frm); +} + +static inline void sco_dump(int level, struct frame *frm) +{ + hci_sco_hdr *hdr = (void *) frm->ptr; + uint16_t handle = btohs(hdr->handle); + uint8_t flags = acl_flags(handle); + int len; + + if (frm->audio_fd > fileno(stderr)) + len = write(frm->audio_fd, frm->ptr + HCI_SCO_HDR_SIZE, hdr->dlen); + + if (!p_filter(FILT_SCO)) { + p_indent(level, frm); + printf("SCO data: handle %d flags 0x%2.2x dlen %d\n", + acl_handle(handle), flags, hdr->dlen); + level++; + + frm->ptr += HCI_SCO_HDR_SIZE; + frm->len -= HCI_SCO_HDR_SIZE; + raw_dump(level, frm); + } +} + +static inline void vendor_dump(int level, struct frame *frm) +{ + if (p_filter(FILT_HCI)) + return; + + if (frm->dev_id == HCI_DEV_NONE) { + uint16_t device = btohs(htons(get_u16(frm))); + uint16_t proto = btohs(htons(get_u16(frm))); + uint16_t type = btohs(htons(get_u16(frm))); + uint16_t plen = btohs(htons(get_u16(frm))); + + p_indent(level, frm); + + printf("System %s: device hci%d proto 0x%2.2x type 0x%2.2x plen %d\n", + frm->in ? "event" : "command", device, proto, type, plen); + + raw_dump(level, frm); + return; + } + + if (parser.flags & DUMP_NOVENDOR) + return; + + if (get_manufacturer() == 12) { + bpa_dump(level, frm); + return; + } + + p_indent(level, frm); + printf("Vendor data: len %d\n", frm->len); + raw_dump(level, frm); +} + +void hci_dump(int level, struct frame *frm) +{ + uint8_t type = *(uint8_t *)frm->ptr; + + frm->ptr++; frm->len--; + + switch (type) { + case HCI_COMMAND_PKT: + command_dump(level, frm); + break; + + case HCI_EVENT_PKT: + event_dump(level, frm); + break; + + case HCI_ACLDATA_PKT: + acl_dump(level, frm); + break; + + case HCI_SCODATA_PKT: + sco_dump(level, frm); + break; + + case HCI_VENDOR_PKT: + vendor_dump(level, frm); + break; + + default: + if (p_filter(FILT_HCI)) + break; + + p_indent(level, frm); + printf("Unknown: type 0x%2.2x len %d\n", type, frm->len); + raw_dump(level, frm); + break; + } +} diff --git a/parser/hcrp.c b/parser/hcrp.c new file mode 100644 index 0000000..e7ada69 --- /dev/null +++ b/parser/hcrp.c @@ -0,0 +1,116 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +static char *pid2str(uint16_t pid) +{ + switch (pid) { + case 0x0001: + return "CreditGrant"; + case 0x0002: + return "CreditRequest"; + case 0x0003: + return "CreditReturn"; + case 0x0004: + return "CreditQuery"; + case 0x0005: + return "GetLPTStatus"; + case 0x0006: + return "Get1284ID"; + case 0x0007: + return "SoftReset"; + case 0x0008: + return "HardRest"; + case 0x0009: + return "RegisterNotification"; + case 0x000A: + return "NotificationConnectionAlive"; + default: + return "Reserved"; + } +} + +static char *status2str(uint16_t status) +{ + switch (status) { + case 0x0000: + return "Feature unsupported"; + case 0x0001: + return "Success"; + case 0x0002: + return "Credit synchronization error"; + case 0xFFFF: + return "Generic error"; + default: + return "Unknown"; + } +} + +void hcrp_dump(int level, struct frame *frm) +{ + uint16_t pid, tid, plen, status; + uint32_t credits; + + pid = get_u16(frm); + tid = get_u16(frm); + plen = get_u16(frm); + + p_indent(level, frm); + + printf("HCRP %s %s: tid 0x%x plen %d", + pid2str(pid), frm->in ? "rsp" : "cmd", tid, plen); + + if (frm->in) { + status = get_u16(frm); + printf(" status %d (%s)\n", status, status2str(status)); + } else + printf("\n"); + + if (pid == 0x0001 && !frm->in) { + credits = get_u32(frm); + p_indent(level + 1, frm); + printf("credits %d\n", credits); + } + + if (pid == 0x0002 && frm->in) { + credits = get_u32(frm); + p_indent(level + 1, frm); + printf("credits %d\n", credits); + } + + raw_dump(level + 1, frm); +} diff --git a/parser/hidp.c b/parser/hidp.c new file mode 100644 index 0000000..16aef85 --- /dev/null +++ b/parser/hidp.c @@ -0,0 +1,171 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +static char *type2str(uint8_t head) +{ + switch (head & 0xf0) { + case 0x00: + return "Handshake"; + case 0x10: + return "Control"; + case 0x40: + return "Get report"; + case 0x50: + return "Set report"; + case 0x60: + return "Get protocol"; + case 0x70: + return "Set protocol"; + case 0x80: + return "Get idle"; + case 0x90: + return "Set idle"; + case 0xa0: + return "Data"; + case 0xb0: + return "Data continuation"; + default: + return "Reserved"; + } +} + +static char *result2str(uint8_t head) +{ + switch (head & 0x0f) { + case 0x00: + return "Successful"; + case 0x01: + return "Not ready"; + case 0x02: + return "Invalid report ID"; + case 0x03: + return "Unsupported request"; + case 0x04: + return "Invalid parameter"; + case 0x0e: + return "Unknown"; + case 0x0f: + return "Fatal"; + default: + return "Reserved"; + } +} + +static char *operation2str(uint8_t head) +{ + switch (head & 0x0f) { + case 0x00: + return "No operation"; + case 0x01: + return "Hard reset"; + case 0x02: + return "Soft reset"; + case 0x03: + return "Suspend"; + case 0x04: + return "Exit suspend"; + case 0x05: + return "Virtual cable unplug"; + default: + return "Reserved"; + } +} + +static char *report2str(uint8_t head) +{ + switch (head & 0x03) { + case 0x00: + return "Other report"; + case 0x01: + return "Input report"; + case 0x02: + return "Output report"; + case 0x03: + return "Feature report"; + default: + return "Reserved"; + } +} + +static char *protocol2str(uint8_t head) +{ + switch (head & 0x01) { + case 0x00: + return "Boot protocol"; + case 0x01: + return "Report protocol"; + default: + return "Reserved"; + } +} + +void hidp_dump(int level, struct frame *frm) +{ + uint8_t hdr; + char *param; + + hdr = get_u8(frm); + + switch (hdr & 0xf0) { + case 0x00: + param = result2str(hdr); + break; + case 0x10: + param = operation2str(hdr); + break; + case 0x60: + case 0x70: + param = protocol2str(hdr); + break; + case 0x40: + case 0x50: + case 0xa0: + case 0xb0: + param = report2str(hdr); + break; + default: + param = ""; + break; + } + + p_indent(level, frm); + + printf("HIDP: %s: %s\n", type2str(hdr), param); + + raw_dump(level, frm); +} diff --git a/parser/l2cap.c b/parser/l2cap.c new file mode 100644 index 0000000..a906f42 --- /dev/null +++ b/parser/l2cap.c @@ -0,0 +1,971 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2002 Maxim Krasnyansky + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "parser.h" +#include "sdp.h" + +typedef struct { + uint16_t handle; + struct frame frm; +} handle_info; +#define HANDLE_TABLE_SIZE 10 + +static handle_info handle_table[HANDLE_TABLE_SIZE]; + +typedef struct { + uint16_t handle; + uint16_t cid; + uint16_t psm; + uint16_t num; + uint8_t mode; +} cid_info; +#define CID_TABLE_SIZE 20 + +static cid_info cid_table[2][CID_TABLE_SIZE]; + +#define SCID cid_table[0] +#define DCID cid_table[1] + +static struct frame *add_handle(uint16_t handle) +{ + register handle_info *t = handle_table; + register int i; + + for (i = 0; i < HANDLE_TABLE_SIZE; i++) + if (!t[i].handle) { + t[i].handle = handle; + return &t[i].frm; + } + return NULL; +} + +static struct frame *get_frame(uint16_t handle) +{ + register handle_info *t = handle_table; + register int i; + + for (i = 0; i < HANDLE_TABLE_SIZE; i++) + if (t[i].handle == handle) + return &t[i].frm; + + return add_handle(handle); +} + +static void add_cid(int in, uint16_t handle, uint16_t cid, uint16_t psm) +{ + register cid_info *table = cid_table[in]; + register int i, pos = -1; + uint16_t num = 1; + + for (i = 0; i < CID_TABLE_SIZE; i++) { + if ((pos < 0 && !table[i].cid) || table[i].cid == cid) + pos = i; + if (table[i].psm == psm) + num++; + } + + if (pos >= 0) { + table[pos].handle = handle; + table[pos].cid = cid; + table[pos].psm = psm; + table[pos].num = num; + table[pos].mode = 0; + } +} + +static void del_cid(int in, uint16_t dcid, uint16_t scid) +{ + register int t, i; + uint16_t cid[2]; + + if (!in) { + cid[0] = dcid; + cid[1] = scid; + } else { + cid[0] = scid; + cid[1] = dcid; + } + + for (t = 0; t < 2; t++) { + for (i = 0; i < CID_TABLE_SIZE; i++) + if (cid_table[t][i].cid == cid[t]) { + cid_table[t][i].handle = 0; + cid_table[t][i].cid = 0; + cid_table[t][i].psm = 0; + cid_table[t][i].num = 0; + cid_table[t][i].mode = 0; + break; + } + } +} + +static void del_handle(uint16_t handle) +{ + register int t, i; + + for (t = 0; t < 2; t++) { + for (i = 0; i < CID_TABLE_SIZE; i++) + if (cid_table[t][i].handle == handle) { + cid_table[t][i].handle = 0; + cid_table[t][i].cid = 0; + cid_table[t][i].psm = 0; + cid_table[t][i].num = 0; + cid_table[t][i].mode = 0; + break; + } + } +} +static uint16_t get_psm(int in, uint16_t cid) +{ + register cid_info *table = cid_table[in]; + register int i; + + for (i = 0; i < CID_TABLE_SIZE; i++) + if (table[i].cid == cid) + return table[i].psm; + return parser.defpsm; +} + +static uint16_t get_num(int in, uint16_t cid) +{ + register cid_info *table = cid_table[in]; + register int i; + + for (i = 0; i < CID_TABLE_SIZE; i++) + if (table[i].cid == cid) + return table[i].num; + return 0; +} + +static void set_mode(int in, uint16_t cid, uint8_t mode) +{ + register cid_info *table = cid_table[in]; + register int i; + + for (i = 0; i < CID_TABLE_SIZE; i++) + if (table[i].cid == cid) + table[i].mode = mode; +} + +static uint8_t get_mode(int in, uint16_t cid) +{ + register cid_info *table = cid_table[in]; + register int i; + + for (i = 0; i < CID_TABLE_SIZE; i++) + if (table[i].cid == cid) + return table[i].mode; + return 0; +} + +static uint32_t get_val(uint8_t *ptr, uint8_t len) +{ + switch (len) { + case 1: + return *ptr; + case 2: + return btohs(bt_get_unaligned((uint16_t *) ptr)); + case 4: + return btohl(bt_get_unaligned((uint32_t *) ptr)); + } + return 0; +} + +static char *reason2str(uint16_t reason) +{ + switch (reason) { + case 0x0000: + return "Command not understood"; + case 0x0001: + return "Signalling MTU exceeded"; + case 0x0002: + return "Invalid CID in request"; + default: + return "Reserved"; + } +} + +static char *connresult2str(uint16_t result) +{ + switch (result) { + case 0x0000: + return "Connection successful"; + case 0x0001: + return "Connection pending"; + case 0x0002: + return "Connection refused - PSM not supported"; + case 0x0003: + return "Connection refused - security block"; + case 0x0004: + return "Connection refused - no resources available"; + default: + return "Reserved"; + } +} + +static char *status2str(uint16_t status) +{ + switch (status) { + case 0x0000: + return "No futher information available"; + case 0x0001: + return "Authentication pending"; + case 0x0002: + return "Authorization pending"; + default: + return "Reserved"; + } +} + +static char *confresult2str(uint16_t result) +{ + switch (result) { + case 0x0000: + return "Success"; + case 0x0001: + return "Failure - unacceptable parameters"; + case 0x0002: + return "Failure - rejected (no reason provided)"; + case 0x0003: + return "Failure - unknown options"; + default: + return "Reserved"; + } +} +static char *inforesult2str(uint16_t result) +{ + switch (result) { + case 0x0000: + return "Success"; + case 0x0001: + return "Not supported"; + default: + return "Reserved"; + } +} + +static char *type2str(uint8_t type) +{ + switch (type) { + case 0x00: + return "No traffic"; + case 0x01: + return "Best effort"; + case 0x02: + return "Guaranteed"; + default: + return "Reserved"; + } +} + +static char *mode2str(uint8_t mode) +{ + switch (mode) { + case 0x00: + return "Basic"; + case 0x01: + return "Retransmission"; + case 0x02: + return "Flow control"; + default: + return "Reserved"; + } +} + +static char *sar2str(uint8_t sar) +{ + switch (sar) { + case 0x00: + return "Unsegmented"; + case 0x01: + return "Start"; + case 0x02: + return "End"; + case 0x03: + return "Continuation"; + default: + return "Bad SAR"; + + } +} + +static char *supervisory2str(uint8_t supervisory) +{ + switch (supervisory) { + case 0x00: + return "Receiver Ready (RR)"; + case 0x01: + return "Reject (REJ)"; + case 0x02: + case 0x03: + return "Reserved Supervisory"; + default: + return "Bad Supervisory"; + } +} + +static inline void command_rej(int level, struct frame *frm) +{ + l2cap_cmd_rej *h = frm->ptr; + uint16_t reason = btohs(h->reason); + uint32_t cid; + + printf("Command rej: reason %d", reason); + + switch (reason) { + case 0x0001: + printf(" mtu %d\n", get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 2)); + break; + case 0x0002: + cid = get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 4); + printf(" dcid 0x%4.4x scid 0x%4.4x\n", cid & 0xffff, cid >> 16); + break; + default: + printf("\n"); + break; + } + + p_indent(level + 1, frm); + printf("%s\n", reason2str(reason)); +} + +static inline void conn_req(int level, struct frame *frm) +{ + l2cap_conn_req *h = frm->ptr; + uint16_t psm = btohs(h->psm); + uint16_t scid = btohs(h->scid); + + add_cid(frm->in, frm->handle, scid, psm); + + if (p_filter(FILT_L2CAP)) + return; + + printf("Connect req: psm %d scid 0x%4.4x\n", psm, scid); +} + +static inline void conn_rsp(int level, struct frame *frm) +{ + l2cap_conn_rsp *h = frm->ptr; + uint16_t scid = btohs(h->scid); + uint16_t dcid = btohs(h->dcid); + uint16_t result = btohs(h->result); + uint16_t status = btohs(h->status); + uint16_t psm; + + switch (h->result) { + case L2CAP_CR_SUCCESS: + if ((psm = get_psm(!frm->in, scid))) + add_cid(frm->in, frm->handle, dcid, psm); + break; + + case L2CAP_CR_PEND: + break; + + default: + del_cid(frm->in, dcid, scid); + break; + } + + if (p_filter(FILT_L2CAP)) + return; + + printf("Connect rsp: dcid 0x%4.4x scid 0x%4.4x result %d status %d\n", + dcid, scid, result, status); + + p_indent(level + 1, frm); + printf("%s", connresult2str(result)); + + if (result == 0x0001) + printf(" - %s\n", status2str(status)); + else + printf("\n"); +} + +static void conf_rfc(void *ptr, int len, int in, uint16_t cid) +{ + uint8_t mode; + + mode = *((uint8_t *) ptr); + set_mode(in, cid, mode); + + printf("RFC 0x%02x (%s", mode, mode2str(mode)); + if (mode == 0x01 || mode == 0x02) { + uint8_t txwin, maxtrans; + uint16_t rto, mto, mps; + txwin = *((uint8_t *) (ptr + 1)); + maxtrans = *((uint8_t *) (ptr + 2)); + rto = btohs(bt_get_unaligned((uint16_t *) (ptr + 3))); + mto = btohs(bt_get_unaligned((uint16_t *) (ptr + 5))); + mps = btohs(bt_get_unaligned((uint16_t *) (ptr + 7))); + printf(", TxWin %d, MaxTx %d, RTo %d, MTo %d, MPS %d", + txwin, maxtrans, rto, mto, mps); + } + printf(")"); +} + +static void conf_opt(int level, void *ptr, int len, int in, uint16_t cid) +{ + p_indent(level, 0); + while (len > 0) { + l2cap_conf_opt *h = ptr; + + ptr += L2CAP_CONF_OPT_SIZE + h->len; + len -= L2CAP_CONF_OPT_SIZE + h->len; + + if (h->type & 0x80) + printf("["); + + switch (h->type & 0x7f) { + case L2CAP_CONF_MTU: + set_mode(in, cid, 0x00); + printf("MTU"); + if (h->len > 0) + printf(" %d", get_val(h->val, h->len)); + break; + + case L2CAP_CONF_FLUSH_TO: + printf("FlushTO"); + if (h->len > 0) + printf(" %d", get_val(h->val, h->len)); + break; + + case L2CAP_CONF_QOS: + printf("QoS"); + if (h->len > 0) + printf(" 0x%02x (%s)", *(h->val + 1), type2str(*(h->val + 1))); + break; + + case L2CAP_CONF_RFC: + conf_rfc(h->val, h->len, in, cid); + break; + + default: + printf("Unknown (type %2.2x, len %d)", h->type & 0x7f, h->len); + break; + } + + if (h->type & 0x80) + printf("] "); + else + printf(" "); + } + printf("\n"); +} + +static void conf_list(int level, uint8_t *list, int len) +{ + int i; + + p_indent(level, 0); + for (i = 0; i < len; i++) { + switch (list[i] & 0x7f) { + case L2CAP_CONF_MTU: + printf("MTU "); + break; + case L2CAP_CONF_FLUSH_TO: + printf("FlushTo "); + break; + case L2CAP_CONF_QOS: + printf("QoS "); + break; + case L2CAP_CONF_RFC: + printf("RFC "); + break; + default: + printf("%2.2x ", list[i] & 0x7f); + break; + } + } + printf("\n"); +} + +static inline void conf_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + l2cap_conf_req *h = frm->ptr; + uint16_t dcid = btohs(h->dcid); + int clen = btohs(cmd->len) - L2CAP_CONF_REQ_SIZE; + + if (p_filter(FILT_L2CAP)) + return; + + printf("Config req: dcid 0x%4.4x flags 0x%2.2x clen %d\n", + dcid, btohs(h->flags), clen); + + if (clen > 0) + conf_opt(level + 1, h->data, clen, frm->in, dcid); +} + +static inline void conf_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + l2cap_conf_rsp *h = frm->ptr; + uint16_t scid = btohs(h->scid); + uint16_t result = btohs(h->result); + int clen = btohs(cmd->len) - L2CAP_CONF_RSP_SIZE; + + if (p_filter(FILT_L2CAP)) + return; + + printf("Config rsp: scid 0x%4.4x flags 0x%2.2x result %d clen %d\n", + scid, btohs(h->flags), result, clen); + + if (clen > 0) { + if (result) { + p_indent(level + 1, frm); + printf("%s\n", confresult2str(result)); + } + if (result == 0x0003) + conf_list(level + 1, h->data, clen); + else + conf_opt(level + 1, h->data, clen, frm->in, scid); + } else { + p_indent(level + 1, frm); + printf("%s\n", confresult2str(result)); + } +} + +static inline void disconn_req(int level, struct frame *frm) +{ + l2cap_disconn_req *h = frm->ptr; + + if (p_filter(FILT_L2CAP)) + return; + + printf("Disconn req: dcid 0x%4.4x scid 0x%4.4x\n", + btohs(h->dcid), btohs(h->scid)); +} + +static inline void disconn_rsp(int level, struct frame *frm) +{ + l2cap_disconn_rsp *h = frm->ptr; + uint16_t dcid = btohs(h->dcid); + uint16_t scid = btohs(h->scid); + + del_cid(frm->in, dcid, scid); + + if (p_filter(FILT_L2CAP)) + return; + + printf("Disconn rsp: dcid 0x%4.4x scid 0x%4.4x\n", + btohs(h->dcid), btohs(h->scid)); +} + +static inline void echo_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + if (p_filter(FILT_L2CAP)) + return; + + printf("Echo req: dlen %d\n", btohs(cmd->len)); + raw_dump(level, frm); +} + +static inline void echo_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + if (p_filter(FILT_L2CAP)) + return; + + printf("Echo rsp: dlen %d\n", btohs(cmd->len)); + raw_dump(level, frm); +} + +static void info_opt(int level, int type, void *ptr, int len) +{ + uint32_t mask; + + p_indent(level, 0); + + switch (type) { + case 0x0001: + printf("Connectionless MTU %d\n", get_val(ptr, len)); + break; + case 0x0002: + mask = get_val(ptr, len); + printf("Extended feature mask 0x%4.4x\n", mask); + if (parser.flags & DUMP_VERBOSE) { + if (mask & 0x01) { + p_indent(level + 1, 0); + printf("Flow control mode\n"); + } + if (mask & 0x02) { + p_indent(level + 1, 0); + printf("Retransmission mode\n"); + } + if (mask & 0x04) { + p_indent(level + 1, 0); + printf("Bi-directional QoS\n"); + } + } + break; + default: + printf("Unknown (len %d)\n", len); + break; + } +} + +static inline void info_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + l2cap_info_req *h = frm->ptr; + + if (p_filter(FILT_L2CAP)) + return; + + printf("Info req: type %d\n", btohs(h->type)); +} + +static inline void info_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) +{ + l2cap_info_rsp *h = frm->ptr; + uint16_t type = btohs(h->type); + uint16_t result = btohs(h->result); + int ilen = btohs(cmd->len) - L2CAP_INFO_RSP_SIZE; + + if (p_filter(FILT_L2CAP)) + return; + + printf("Info rsp: type %d result %d\n", type, result); + + if (ilen > 0) { + info_opt(level + 1, type, h->data, ilen); + } else { + p_indent(level + 1, frm); + printf("%s\n", inforesult2str(result)); + } +} + +static void l2cap_parse(int level, struct frame *frm) +{ + l2cap_hdr *hdr = (void *)frm->ptr; + uint16_t dlen = btohs(hdr->len); + uint16_t cid = btohs(hdr->cid); + uint16_t psm; + + frm->ptr += L2CAP_HDR_SIZE; + frm->len -= L2CAP_HDR_SIZE; + + if (cid == 0x1) { + /* Signaling channel */ + + while (frm->len >= L2CAP_CMD_HDR_SIZE) { + l2cap_cmd_hdr *hdr = frm->ptr; + + frm->ptr += L2CAP_CMD_HDR_SIZE; + frm->len -= L2CAP_CMD_HDR_SIZE; + + if (!p_filter(FILT_L2CAP)) { + p_indent(level, frm); + printf("L2CAP(s): "); + } + + switch (hdr->code) { + case L2CAP_COMMAND_REJ: + command_rej(level, frm); + break; + + case L2CAP_CONN_REQ: + conn_req(level, frm); + break; + + case L2CAP_CONN_RSP: + conn_rsp(level, frm); + break; + + case L2CAP_CONF_REQ: + conf_req(level, hdr, frm); + break; + + case L2CAP_CONF_RSP: + conf_rsp(level, hdr, frm); + break; + + case L2CAP_DISCONN_REQ: + disconn_req(level, frm); + break; + + case L2CAP_DISCONN_RSP: + disconn_rsp(level, frm); + break; + + case L2CAP_ECHO_REQ: + echo_req(level, hdr, frm); + break; + + case L2CAP_ECHO_RSP: + echo_rsp(level, hdr, frm); + break; + + case L2CAP_INFO_REQ: + info_req(level, hdr, frm); + break; + + case L2CAP_INFO_RSP: + info_rsp(level, hdr, frm); + break; + + default: + if (p_filter(FILT_L2CAP)) + break; + printf("code 0x%2.2x ident %d len %d\n", + hdr->code, hdr->ident, btohs(hdr->len)); + raw_dump(level, frm); + } + + if (frm->len > btohs(hdr->len)) { + frm->len -= btohs(hdr->len); + frm->ptr += btohs(hdr->len); + } else + frm->len = 0; + } + } else if (cid == 0x2) { + /* Connectionless channel */ + + if (p_filter(FILT_L2CAP)) + return; + + psm = btohs(bt_get_unaligned((uint16_t *) frm->ptr)); + frm->ptr += 2; + frm->len -= 2; + + p_indent(level, frm); + printf("L2CAP(c): len %d psm %d\n", dlen, psm); + raw_dump(level, frm); + } else { + /* Connection oriented channel */ + + uint8_t mode = get_mode(!frm->in, cid); + uint16_t psm = get_psm(!frm->in, cid); + uint16_t ctrl = 0, fcs = 0; + uint32_t proto; + + frm->cid = cid; + frm->num = get_num(!frm->in, cid); + + if (mode > 0) { + ctrl = btohs(bt_get_unaligned((uint16_t *) frm->ptr)); + frm->ptr += 2; + frm->len -= 4; + fcs = btohs(bt_get_unaligned((uint16_t *) (frm->ptr + frm->len))); + } + + if (!p_filter(FILT_L2CAP)) { + p_indent(level, frm); + printf("L2CAP(d): cid 0x%4.4x len %d", cid, dlen); + if (mode > 0) + printf(" ctrl 0x%4.4x fcs 0x%4.4x", ctrl, fcs); + printf(" [psm %d]\n", psm); + level++; + if (mode > 0) { + p_indent(level, frm); + printf("%s:", ctrl & 0x01 ? "S-frame" : "I-frame"); + if (ctrl & 0x01) { + printf(" %s", supervisory2str((ctrl & 0x0c) >> 2)); + } else { + uint8_t sar = (ctrl & 0xc000) >> 14; + printf(" %s", sar2str(sar)); + if (sar == 1) { + uint16_t len; + len = btohs(bt_get_unaligned((uint16_t *) frm->ptr)); + frm->ptr += 2; + frm->len -= 2; + printf(" (len %d)", len); + } + printf(" TxSeq %d", (ctrl & 0x7e) >> 1); + } + printf(" ReqSeq %d", (ctrl & 0x3f00) >> 8); + if (ctrl & 0x80) + printf(" Retransmission Disable"); + printf("\n"); + } + } + + switch (psm) { + case 0x01: + if (!p_filter(FILT_SDP)) + sdp_dump(level + 1, frm); + else + raw_dump(level + 1, frm); + break; + + case 0x03: + if (!p_filter(FILT_RFCOMM)) + rfcomm_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + case 0x0f: + if (!p_filter(FILT_BNEP)) + bnep_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + case 0x11: + case 0x13: + if (!p_filter(FILT_HIDP)) + hidp_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + case 0x17: + if (!p_filter(FILT_AVCTP)) + avctp_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + case 0x19: + if (!p_filter(FILT_AVDTP)) + avdtp_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + default: + proto = get_proto(frm->handle, psm, 0); + + switch (proto) { + case SDP_UUID_CMTP: + if (!p_filter(FILT_CMTP)) + cmtp_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + case SDP_UUID_HARDCOPY_CONTROL_CHANNEL: + if (!p_filter(FILT_HCRP)) + hcrp_dump(level, frm); + else + raw_dump(level + 1, frm); + break; + + default: + if (p_filter(FILT_L2CAP)) + break; + + raw_dump(level, frm); + break; + } + break; + } + } +} + +void l2cap_dump(int level, struct frame *frm) +{ + struct frame *fr; + l2cap_hdr *hdr; + uint16_t dlen; + + if (frm->flags & ACL_START) { + hdr = frm->ptr; + dlen = btohs(hdr->len); + + if (frm->len == (dlen + L2CAP_HDR_SIZE)) { + /* Complete frame */ + l2cap_parse(level, frm); + return; + } + + if (!(fr = get_frame(frm->handle))) { + fprintf(stderr, "Not enough connection handles\n"); + raw_dump(level, frm); + return; + } + + if (fr->data) + free(fr->data); + + if (!(fr->data = malloc(dlen + L2CAP_HDR_SIZE))) { + perror("Can't allocate L2CAP reassembly buffer"); + return; + } + memcpy(fr->data, frm->ptr, frm->len); + fr->data_len = dlen + L2CAP_HDR_SIZE; + fr->len = frm->len; + fr->ptr = fr->data; + fr->dev_id = frm->dev_id; + fr->in = frm->in; + fr->ts = frm->ts; + fr->handle = frm->handle; + fr->cid = frm->cid; + fr->num = frm->num; + fr->dlci = frm->dlci; + fr->channel = frm->channel; + fr->pppdump_fd = frm->pppdump_fd; + fr->audio_fd = frm->audio_fd; + } else { + if (!(fr = get_frame(frm->handle))) { + fprintf(stderr, "Not enough connection handles\n"); + raw_dump(level, frm); + return; + } + + if (!fr->data) { + /* Unexpected fragment */ + raw_dump(level, frm); + return; + } + + if (frm->len > (fr->data_len - fr->len)) { + /* Bad fragment */ + raw_dump(level, frm); + free(fr->data); fr->data = NULL; + return; + } + + memcpy(fr->data + fr->len, frm->ptr, frm->len); + fr->len += frm->len; + + if (fr->len == fr->data_len) { + /* Complete frame */ + l2cap_parse(level, fr); + + free(fr->data); fr->data = NULL; + return; + } + } +} + +void l2cap_clear(uint16_t handle) +{ + del_handle(handle); +} diff --git a/parser/lmp.c b/parser/lmp.c new file mode 100644 index 0000000..d80b5bf --- /dev/null +++ b/parser/lmp.c @@ -0,0 +1,1349 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "parser.h" + +#define LMP_U8(frm) (get_u8(frm)) +#define LMP_U16(frm) (btohs(htons(get_u16(frm)))) +#define LMP_U32(frm) (btohl(htonl(get_u32(frm)))) + +static enum { + IN_RAND, + COMB_KEY_M, + COMB_KEY_S, + AU_RAND_M, + AU_RAND_S, + SRES_M, + SRES_S, +} pairing_state = IN_RAND; + +static struct { + uint8_t in_rand[16]; + uint8_t comb_key_m[16]; + uint8_t comb_key_s[16]; + uint8_t au_rand_m[16]; + uint8_t au_rand_s[16]; + uint8_t sres_m[4]; + uint8_t sres_s[4]; +} pairing_data; + +static inline void pairing_data_dump(void) +{ + int i; + + p_indent(6, NULL); + printf("IN_RAND "); + for (i = 0; i < 16; i++) + printf("%2.2x", pairing_data.in_rand[i]); + printf("\n"); + + p_indent(6, NULL); + printf("COMB_KEY "); + for (i = 0; i < 16; i++) + printf("%2.2x", pairing_data.comb_key_m[i]); + printf(" (M)\n"); + + p_indent(6, NULL); + printf("COMB_KEY "); + for (i = 0; i < 16; i++) + printf("%2.2x", pairing_data.comb_key_s[i]); + printf(" (S)\n"); + + p_indent(6, NULL); + printf("AU_RAND "); + for (i = 0; i < 16; i++) + printf("%2.2x", pairing_data.au_rand_m[i]); + printf(" SRES "); + for (i = 0; i < 4; i++) + printf("%2.2x", pairing_data.sres_m[i]); + printf(" (M)\n"); + + p_indent(6, NULL); + printf("AU_RAND "); + for (i = 0; i < 16; i++) + printf("%2.2x", pairing_data.au_rand_s[i]); + printf(" SRES "); + for (i = 0; i < 4; i++) + printf("%2.2x", pairing_data.sres_s[i]); + printf(" (S)\n"); +} + +static inline void in_rand(struct frame *frm) +{ + uint8_t *val = frm->ptr; + + memcpy(pairing_data.in_rand, val, 16); + pairing_state = COMB_KEY_M; +} + +static inline void comb_key(struct frame *frm) +{ + uint8_t *val = frm->ptr; + + switch (pairing_state) { + case COMB_KEY_M: + memcpy(pairing_data.comb_key_m, val, 16); + pairing_state = COMB_KEY_S; + break; + case COMB_KEY_S: + memcpy(pairing_data.comb_key_s, val, 16); + pairing_state = AU_RAND_M; + break; + default: + pairing_state = IN_RAND; + break; + } +} + +static inline void au_rand(struct frame *frm) +{ + uint8_t *val = frm->ptr; + + switch (pairing_state) { + case AU_RAND_M: + memcpy(pairing_data.au_rand_m, val, 16); + pairing_state = SRES_M; + break; + case AU_RAND_S: + memcpy(pairing_data.au_rand_s, val, 16); + pairing_state = SRES_S; + break; + default: + pairing_state = IN_RAND; + break; + } +} + +static inline void sres(struct frame *frm) +{ + uint8_t *val = frm->ptr; + + switch (pairing_state) { + case SRES_M: + memcpy(pairing_data.sres_m, val, 4); + pairing_state = AU_RAND_S; + break; + case SRES_S: + memcpy(pairing_data.sres_s, val, 4); + pairing_state = IN_RAND; + pairing_data_dump(); + break; + default: + pairing_state = IN_RAND; + break; + } +} + +static char *opcode2str(uint16_t opcode) +{ + switch (opcode) { + case 1: + return "name_req"; + case 2: + return "name_res"; + case 3: + return "accepted"; + case 4: + return "not_accepted"; + case 5: + return "clkoffset_req"; + case 6: + return "clkoffset_res"; + case 7: + return "detach"; + case 8: + return "in_rand"; + case 9: + return "comb_key"; + case 10: + return "unit_key"; + case 11: + return "au_rand"; + case 12: + return "sres"; + case 13: + return "temp_rand"; + case 14: + return "temp_key"; + case 15: + return "encryption_mode_req"; + case 16: + return "encryption_key_size_req"; + case 17: + return "start_encryption_req"; + case 18: + return "stop_encryption_req"; + case 19: + return "switch_req"; + case 20: + return "hold"; + case 21: + return "hold_req"; + case 22: + return "sniff"; + case 23: + return "sniff_req"; + case 24: + return "unsniff_req"; + case 25: + return "park_req"; + case 26: + return "park"; + case 27: + return "set_broadcast_scan_window"; + case 28: + return "modify_beacon"; + case 29: + return "unpark_BD_ADDR_req"; + case 30: + return "unpark_PM_ADDR_req"; + case 31: + return "incr_power_req"; + case 32: + return "decr_power_req"; + case 33: + return "max_power"; + case 34: + return "min_power"; + case 35: + return "auto_rate"; + case 36: + return "preferred_rate"; + case 37: + return "version_req"; + case 38: + return "version_res"; + case 39: + return "feature_req"; + case 40: + return "feature_res"; + case 41: + return "quality_of_service"; + case 42: + return "quality_of_service_req"; + case 43: + return "SCO_link_req"; + case 44: + return "remove_SCO_link_req"; + case 45: + return "max_slot"; + case 46: + return "max_slot_req"; + case 47: + return "timing_accuracy_req"; + case 48: + return "timing_accuracy_res"; + case 49: + return "setup_complete"; + case 50: + return "use_semi_permanent_key"; + case 51: + return "host_connection_req"; + case 52: + return "slot_offset"; + case 53: + return "page_mode_req"; + case 54: + return "page_scan_mode_req"; + case 55: + return "supervision_timeout"; + case 56: + return "test_activate"; + case 57: + return "test_control"; + case 58: + return "encryption_key_size_mask_req"; + case 59: + return "encryption_key_size_mask_res"; + case 60: + return "set_AFH"; + case 61: + return "encapsulated_header"; + case 62: + return "encapsulated_payload"; + case 63: + return "simple_pairing_confirm"; + case 64: + return "simple_pairing_number"; + case 65: + return "DHkey_check"; + case 127 + (1 << 7): + return "accepted_ext"; + case 127 + (2 << 7): + return "not_accepted_ext"; + case 127 + (3 << 7): + return "features_req_ext"; + case 127 + (4 << 7): + return "features_res_ext"; + case 127 + (11 << 7): + return "packet_type_table_req"; + case 127 + (12 << 7): + return "eSCO_link_req"; + case 127 + (13 << 7): + return "remove_eSCO_link_req"; + case 127 + (16 << 7): + return "channel_classification_req"; + case 127 + (17 << 7): + return "channel_classification"; + case 127 + (21 << 7): + return "sniff_subrating_req"; + case 127 + (22 << 7): + return "sniff_subrating_res"; + case 127 + (23 << 7): + return "pause_encryption_req"; + case 127 + (24 << 7): + return "resume_encryption_req"; + case 127 + (25 << 7): + return "IO_capability_req"; + case 127 + (26 << 7): + return "IO_capability_res"; + case 127 + (27 << 7): + return "numeric_comparison_failed"; + case 127 + (28 << 7): + return "passkey_failed"; + case 127 + (29 << 7): + return "oob_failed"; + case 127 + (30 << 7): + return "keypress_notification"; + default: + return "unknown"; + } +} + +static inline void name_req_dump(int level, struct frame *frm) +{ + uint8_t offset = LMP_U8(frm); + + p_indent(level, frm); + printf("name offset %d\n", offset); +} + +static inline void name_res_dump(int level, struct frame *frm) +{ + uint8_t offset = LMP_U8(frm); + uint8_t length = LMP_U8(frm); + uint8_t *name = frm->ptr; + int i, size; + + frm->ptr += 14; + frm->len -= 14; + + p_indent(level, frm); + printf("name offset %d\n", offset); + + p_indent(level, frm); + printf("name length %d\n", length); + + size = length - offset; + if (size > 14) + size = 14; + + p_indent(level, frm); + printf("name fragment '"); + for (i = 0; i < size; i++) + if (isprint(name[i])) + printf("%c", name[i]); + else + printf("."); + printf("'\n"); +} + +static inline void accepted_dump(int level, struct frame *frm) +{ + uint8_t opcode = LMP_U8(frm); + + p_indent(level, frm); + printf("op code %d (%s)\n", opcode, opcode2str(opcode)); +} + +static inline void not_accepted_dump(int level, struct frame *frm) +{ + uint8_t opcode = LMP_U8(frm); + uint8_t error = LMP_U8(frm); + + p_indent(level, frm); + printf("op code %d (%s)\n", opcode, opcode2str(opcode)); + + p_indent(level, frm); + printf("error code 0x%2.2x\n", error); +} + +static inline void clkoffset_dump(int level, struct frame *frm) +{ + uint16_t clkoffset = LMP_U16(frm); + + p_indent(level, frm); + printf("clock offset 0x%4.4x\n", clkoffset); +} + +static inline void detach_dump(int level, struct frame *frm) +{ + uint8_t error = LMP_U8(frm); + + p_indent(level, frm); + printf("error code 0x%2.2x\n", error); +} + +static inline void random_number_dump(int level, struct frame *frm) +{ + uint8_t *number = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("random number "); + for (i = 0; i < 16; i++) + printf("%2.2x", number[i]); + printf("\n"); +} + +static inline void key_dump(int level, struct frame *frm) +{ + uint8_t *key = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("key "); + for (i = 0; i < 16; i++) + printf("%2.2x", key[i]); + printf("\n"); +} + +static inline void auth_resp_dump(int level, struct frame *frm) +{ + uint8_t *resp = frm->ptr; + int i; + + frm->ptr += 4; + frm->ptr -= 4; + + p_indent(level, frm); + printf("authentication response "); + for (i = 0; i < 4; i++) + printf("%2.2x", resp[i]); + printf("\n"); +} + +static inline void encryption_mode_req_dump(int level, struct frame *frm) +{ + uint8_t mode = LMP_U8(frm); + + p_indent(level, frm); + printf("encryption mode %d\n", mode); +} + +static inline void encryption_key_size_req_dump(int level, struct frame *frm) +{ + uint8_t keysize = LMP_U8(frm); + + p_indent(level, frm); + printf("key size %d\n", keysize); +} + +static inline void switch_req_dump(int level, struct frame *frm) +{ + uint32_t instant = LMP_U32(frm); + + p_indent(level, frm); + printf("switch instant 0x%4.4x\n", instant); +} + +static inline void hold_dump(int level, struct frame *frm) +{ + uint16_t time = LMP_U16(frm); + uint32_t instant = LMP_U32(frm); + + p_indent(level, frm); + printf("hold time 0x%4.4x\n", time); + + p_indent(level, frm); + printf("hold instant 0x%4.4x\n", instant); +} + +static inline void sniff_req_dump(int level, struct frame *frm) +{ + uint8_t timing = LMP_U8(frm); + uint16_t dsniff = LMP_U16(frm); + uint16_t tsniff = LMP_U16(frm); + uint16_t attempt = LMP_U16(frm); + uint16_t timeout = LMP_U16(frm); + + p_indent(level, frm); + printf("timing control flags 0x%2.2x\n", timing); + + p_indent(level, frm); + printf("D_sniff %d T_sniff %d\n", dsniff, tsniff); + + p_indent(level, frm); + printf("sniff attempt %d\n", attempt); + + p_indent(level, frm); + printf("sniff timeout %d\n", timeout); +} + +static inline void park_req_dump(int level, struct frame *frm) +{ + uint8_t timing = LMP_U8(frm); + uint16_t db = LMP_U16(frm); + uint16_t tb = LMP_U16(frm); + uint8_t nb = LMP_U8(frm); + uint8_t xb = LMP_U8(frm); + uint8_t pmaddr = LMP_U8(frm); + uint8_t araddr = LMP_U8(frm); + uint8_t nbsleep = LMP_U8(frm); + uint8_t dbsleep = LMP_U8(frm); + uint8_t daccess = LMP_U8(frm); + uint8_t taccess = LMP_U8(frm); + uint8_t nslots = LMP_U8(frm); + uint8_t npoll = LMP_U8(frm); + uint8_t access = LMP_U8(frm); + + p_indent(level, frm); + printf("timing control flags 0x%2.2x\n", timing); + + p_indent(level, frm); + printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb); + + p_indent(level, frm); + printf("PM_ADDR %d AR_ADDR %d\n", pmaddr, araddr); + + p_indent(level, frm); + printf("N_Bsleep %d D_Bsleep %d\n", nbsleep, dbsleep); + + p_indent(level, frm); + printf("D_access %d T_access %d\n", daccess, taccess); + + p_indent(level, frm); + printf("N_acc-slots %d N_poll %d\n", nslots, npoll); + + p_indent(level, frm); + printf("M_access %d\n", access & 0x0f); + + p_indent(level, frm); + printf("access scheme 0x%2.2x\n", access >> 4); +} + +static inline void modify_beacon_dump(int level, struct frame *frm) +{ + uint8_t timing = LMP_U8(frm); + uint16_t db = LMP_U16(frm); + uint16_t tb = LMP_U16(frm); + uint8_t nb = LMP_U8(frm); + uint8_t xb = LMP_U8(frm); + uint8_t daccess = LMP_U8(frm); + uint8_t taccess = LMP_U8(frm); + uint8_t nslots = LMP_U8(frm); + uint8_t npoll = LMP_U8(frm); + uint8_t access = LMP_U8(frm); + + p_indent(level, frm); + printf("timing control flags 0x%2.2x\n", timing); + + p_indent(level, frm); + printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb); + + p_indent(level, frm); + printf("D_access %d T_access %d\n", daccess, taccess); + + p_indent(level, frm); + printf("N_acc-slots %d N_poll %d\n", nslots, npoll); + + p_indent(level, frm); + printf("M_access %d\n", access & 0x0f); + + p_indent(level, frm); + printf("access scheme 0x%2.2x\n", access >> 4); +} + +static inline void power_req_dump(int level, struct frame *frm) +{ + uint8_t val = LMP_U8(frm); + + p_indent(level, frm); + printf("future use 0x%2.2x\n", val); +} + +static inline void preferred_rate_dump(int level, struct frame *frm) +{ + uint8_t rate = LMP_U8(frm); + + p_indent(level, frm); + printf("data rate 0x%2.2x\n", rate); + + p_indent(level, frm); + printf("Basic: "); + + printf("%suse FEC, ", rate & 0x01 ? "do not " : ""); + + switch ((rate >> 1) & 0x03) { + case 0x00: + printf("no packet-size preference\n"); + break; + case 0x01: + printf("use 1-slot packets\n"); + break; + case 0x02: + printf("use 3-slot packets\n"); + break; + case 0x03: + printf("use 5-slot packets\n"); + break; + } + + p_indent(level, frm); + printf("EDR: "); + + switch ((rate >> 3) & 0x03) { + case 0x00: + printf("use DM1 packets, "); + break; + case 0x01: + printf("use 2 Mbps packets, "); + break; + case 0x02: + printf("use 3 Mbps packets, "); + break; + case 0x03: + printf("reserved, \n"); + break; + } + + switch ((rate >> 5) & 0x03) { + case 0x00: + printf("no packet-size preference\n"); + break; + case 0x01: + printf("use 1-slot packets\n"); + break; + case 0x02: + printf("use 3-slot packets\n"); + break; + case 0x03: + printf("use 5-slot packets\n"); + break; + } +} + +static inline void version_dump(int level, struct frame *frm) +{ + uint8_t ver = LMP_U8(frm); + uint16_t compid = LMP_U16(frm); + uint16_t subver = LMP_U16(frm); + char *tmp; + + p_indent(level, frm); + tmp = lmp_vertostr(ver); + printf("VersNr %d (%s)\n", ver, tmp); + bt_free(tmp); + + p_indent(level, frm); + printf("CompId %d (%s)\n", compid, bt_compidtostr(compid)); + + p_indent(level, frm); + printf("SubVersNr %d\n", subver); +} + +static inline void features_dump(int level, struct frame *frm) +{ + uint8_t *features = frm->ptr; + int i; + + frm->ptr += 8; + frm->len -= 8; + + p_indent(level, frm); + printf("features"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", features[i]); + printf("\n"); +} + +static inline void set_afh_dump(int level, struct frame *frm) +{ + uint32_t instant = LMP_U32(frm); + uint8_t mode = LMP_U8(frm); + uint8_t *map = frm->ptr; + int i; + + frm->ptr += 10; + frm->len -= 10; + + p_indent(level, frm); + printf("AFH_instant 0x%04x\n", instant); + + p_indent(level, frm); + printf("AFH_mode %d\n", mode); + + p_indent(level, frm); + printf("AFH_channel_map 0x"); + for (i = 0; i < 10; i++) + printf("%2.2x", map[i]); + printf("\n"); +} + +static inline void encapsulated_header_dump(int level, struct frame *frm) +{ + uint8_t major = LMP_U8(frm); + uint8_t minor = LMP_U8(frm); + uint8_t length = LMP_U8(frm); + + p_indent(level, frm); + printf("major type %d minor type %d payload length %d\n", + major, minor, length); + + if (major == 1 && minor == 1) { + p_indent(level, frm); + printf("P-192 Public Key\n"); + } +} + +static inline void encapsulated_payload_dump(int level, struct frame *frm) +{ + uint8_t *value = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("data "); + for (i = 0; i < 16; i++) + printf("%2.2x", value[i]); + printf("\n"); +} + +static inline void simple_pairing_confirm_dump(int level, struct frame *frm) +{ + uint8_t *value = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("commitment value "); + for (i = 0; i < 16; i++) + printf("%2.2x", value[i]); + printf("\n"); +} + +static inline void simple_pairing_number_dump(int level, struct frame *frm) +{ + uint8_t *value = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("nounce value "); + for (i = 0; i < 16; i++) + printf("%2.2x", value[i]); + printf("\n"); +} + +static inline void dhkey_check_dump(int level, struct frame *frm) +{ + uint8_t *value = frm->ptr; + int i; + + frm->ptr += 16; + frm->len -= 16; + + p_indent(level, frm); + printf("confirmation value "); + for (i = 0; i < 16; i++) + printf("%2.2x", value[i]); + printf("\n"); +} + +static inline void accepted_ext_dump(int level, struct frame *frm) +{ + uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7); + + p_indent(level, frm); + printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode)); +} + +static inline void not_accepted_ext_dump(int level, struct frame *frm) +{ + uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7); + uint8_t error = LMP_U8(frm); + + p_indent(level, frm); + printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode)); + + p_indent(level, frm); + printf("error code 0x%2.2x\n", error); +} + +static inline void features_ext_dump(int level, struct frame *frm) +{ + uint8_t page = LMP_U8(frm); + uint8_t max = LMP_U8(frm); + uint8_t *features = frm->ptr; + int i; + + frm->ptr += 8; + frm->len -= 8; + + p_indent(level, frm); + printf("features page %d\n", page); + + p_indent(level, frm); + printf("max supported page %d\n", max); + + p_indent(level, frm); + printf("extended features"); + for (i = 0; i < 8; i++) + printf(" 0x%2.2x", features[i]); + printf("\n"); +} + +static inline void quality_of_service_dump(int level, struct frame *frm) +{ + uint16_t interval = LMP_U16(frm); + uint8_t nbc = LMP_U8(frm); + + p_indent(level, frm); + printf("poll interval %d\n", interval); + + p_indent(level, frm); + printf("N_BC %d\n", nbc); +} + +static inline void sco_link_req_dump(int level, struct frame *frm) +{ + uint8_t handle = LMP_U8(frm); + uint8_t timing = LMP_U8(frm); + uint8_t dsco = LMP_U8(frm); + uint8_t tsco = LMP_U8(frm); + uint8_t packet = LMP_U8(frm); + uint8_t airmode = LMP_U8(frm); + + p_indent(level, frm); + printf("SCO handle %d\n", handle); + + p_indent(level, frm); + printf("timing control flags 0x%2.2x\n", timing); + + p_indent(level, frm); + printf("D_SCO %d T_SCO %d\n", dsco, tsco); + + p_indent(level, frm); + printf("SCO packet 0x%2.2x\n", packet); + + p_indent(level, frm); + printf("air mode 0x%2.2x\n", airmode); +} + +static inline void remove_sco_link_req_dump(int level, struct frame *frm) +{ + uint8_t handle = LMP_U8(frm); + uint8_t error = LMP_U8(frm); + + p_indent(level, frm); + printf("SCO handle %d\n", handle); + + p_indent(level, frm); + printf("error code 0x%2.2x\n", error); +} + +static inline void max_slots_dump(int level, struct frame *frm) +{ + uint8_t slots = LMP_U8(frm); + + p_indent(level, frm); + printf("max slots %d\n", slots); +} + +static inline void timing_accuracy_dump(int level, struct frame *frm) +{ + uint8_t drift = LMP_U8(frm); + uint8_t jitter = LMP_U8(frm); + + p_indent(level, frm); + printf("drift %d\n", drift); + + p_indent(level, frm); + printf("jitter %d\n", jitter); +} + +static inline void slot_offset_dump(int level, struct frame *frm) +{ + uint16_t offset = LMP_U16(frm); + char addr[18]; + + p_ba2str((bdaddr_t *) frm->ptr, addr); + + p_indent(level, frm); + printf("slot offset %d\n", offset); + + p_indent(level, frm); + printf("BD_ADDR %s\n", addr); +} + +static inline void page_mode_dump(int level, struct frame *frm) +{ + uint8_t scheme = LMP_U8(frm); + uint8_t settings = LMP_U8(frm); + + p_indent(level, frm); + printf("page scheme %d\n", scheme); + + p_indent(level, frm); + printf("page scheme settings %d\n", settings); +} + +static inline void supervision_timeout_dump(int level, struct frame *frm) +{ + uint16_t timeout = LMP_U16(frm); + + p_indent(level, frm); + printf("supervision timeout %d\n", timeout); +} + +static inline void test_control_dump(int level, struct frame *frm) +{ + uint8_t scenario = LMP_U8(frm); + uint8_t hopping = LMP_U8(frm); + uint8_t txfreq = LMP_U8(frm); + uint8_t rxfreq = LMP_U8(frm); + uint8_t power = LMP_U8(frm); + uint8_t poll = LMP_U8(frm); + uint8_t packet = LMP_U8(frm); + uint16_t length = LMP_U16(frm); + + p_indent(level, frm); + printf("test scenario %d\n", scenario); + + p_indent(level, frm); + printf("hopping mode %d\n", hopping); + + p_indent(level, frm); + printf("TX frequency %d\n", txfreq); + + p_indent(level, frm); + printf("RX frequency %d\n", rxfreq); + + p_indent(level, frm); + printf("power control mode %d\n", power); + + p_indent(level, frm); + printf("poll period %d\n", poll); + + p_indent(level, frm); + printf("poll period %d\n", poll); + + p_indent(level, frm); + printf("packet type 0x%2.2x\n", packet); + + p_indent(level, frm); + printf("length of test data %d\n", length); +} + +static inline void encryption_key_size_mask_res_dump(int level, struct frame *frm) +{ + uint16_t mask = LMP_U16(frm); + + p_indent(level, frm); + printf("key size mask 0x%4.4x\n", mask); +} + +static inline void packet_type_table_dump(int level, struct frame *frm) +{ + uint8_t type = LMP_U8(frm); + + p_indent(level, frm); + printf("packet type table %d ", type); + switch (type) { + case 0: + printf("(1Mbps only)\n"); + break; + case 1: + printf("(2/3Mbps)\n"); + break; + default: + printf("(Reserved)\n"); + break; + } +} + +static inline void esco_link_req_dump(int level, struct frame *frm) +{ + uint8_t handle = LMP_U8(frm); + uint8_t ltaddr = LMP_U8(frm); + uint8_t timing = LMP_U8(frm); + uint8_t desco = LMP_U8(frm); + uint8_t tesco = LMP_U8(frm); + uint8_t wesco = LMP_U8(frm); + uint8_t mspkt = LMP_U8(frm); + uint8_t smpkt = LMP_U8(frm); + uint16_t mslen = LMP_U16(frm); + uint16_t smlen = LMP_U16(frm); + uint8_t airmode = LMP_U8(frm); + uint8_t negstate = LMP_U8(frm); + + p_indent(level, frm); + printf("eSCO handle %d\n", handle); + + p_indent(level, frm); + printf("eSCO LT_ADDR %d\n", ltaddr); + + p_indent(level, frm); + printf("timing control flags 0x%2.2x\n", timing); + + p_indent(level, frm); + printf("D_eSCO %d T_eSCO %d W_eSCO %d\n", desco, tesco, wesco); + + p_indent(level, frm); + printf("eSCO M->S packet type 0x%2.2x length %d\n", mspkt, mslen); + + p_indent(level, frm); + printf("eSCO S->M packet type 0x%2.2x length %d\n", smpkt, smlen); + + p_indent(level, frm); + printf("air mode 0x%2.2x\n", airmode); + + p_indent(level, frm); + printf("negotiation state 0x%2.2x\n", negstate); +} + +static inline void remove_esco_link_req_dump(int level, struct frame *frm) +{ + uint8_t handle = LMP_U8(frm); + uint8_t error = LMP_U8(frm); + + p_indent(level, frm); + printf("eSCO handle %d\n", handle); + + p_indent(level, frm); + printf("error code 0x%2.2x\n", error); +} + +static inline void channel_classification_req_dump(int level, struct frame *frm) +{ + uint8_t mode = LMP_U8(frm); + uint16_t min = LMP_U16(frm); + uint16_t max = LMP_U16(frm); + + p_indent(level, frm); + printf("AFH reporting mode %d\n", mode); + + p_indent(level, frm); + printf("AFH min interval 0x%4.4x\n", min); + + p_indent(level, frm); + printf("AFH max interval 0x%4.4x\n", max); +} + +static inline void channel_classification_dump(int level, struct frame *frm) +{ + uint8_t *map = frm->ptr; + int i; + + frm->ptr += 10; + frm->len -= 10; + + p_indent(level, frm); + printf("AFH channel classification 0x"); + for (i = 0; i < 10; i++) + printf("%2.2x", map[i]); + printf("\n"); +} + +static inline void sniff_subrating_dump(int level, struct frame *frm) +{ + uint8_t subrate = LMP_U8(frm); + uint16_t timeout = LMP_U16(frm); + uint32_t instant = LMP_U32(frm); + + p_indent(level, frm); + printf("max subrate %d\n", subrate); + + p_indent(level, frm); + printf("min sniff timeout %d\n", timeout); + + p_indent(level, frm); + printf("subrate instant 0x%4.4x\n", instant); +} + +static inline void io_capability_dump(int level, struct frame *frm) +{ + uint8_t capability = LMP_U8(frm); + uint8_t oob_data = LMP_U8(frm); + uint8_t authentication = LMP_U8(frm); + + p_indent(level, frm); + printf("capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n", + capability, oob_data, authentication); +} + +static inline void keypress_notification_dump(int level, struct frame *frm) +{ + uint8_t value = LMP_U8(frm); + + p_indent(level, frm); + printf("notification value %d\n", value); +} + +void lmp_dump(int level, struct frame *frm) +{ + uint8_t tmp, tid; + uint16_t opcode; + + p_indent(level, frm); + + tmp = LMP_U8(frm); + tid = tmp & 0x01; + opcode = (tmp & 0xfe) >> 1; + if (opcode > 123) { + tmp = LMP_U8(frm); + opcode += tmp << 7; + } + + printf("LMP(%c): %s(%c): ", frm->master ? 's' : 'r', + opcode2str(opcode), tid ? 's' : 'm'); + + if (opcode > 123) + printf("op code %d/%d", opcode & 0x7f, opcode >> 7); + else + printf("op code %d", opcode); + + if (frm->handle > 17) + printf(" handle %d\n", frm->handle); + else + printf("\n"); + + if (!(parser.flags & DUMP_VERBOSE)) { + raw_dump(level, frm); + return; + } + + switch (opcode) { + case 1: + name_req_dump(level + 1, frm); + return; + case 2: + name_res_dump(level + 1, frm); + return; + case 3: + accepted_dump(level + 1, frm); + return; + case 4: + not_accepted_dump(level + 1, frm); + return; + case 6: + clkoffset_dump(level + 1, frm); + return; + case 7: + detach_dump(level + 1, frm); + return; + case 8: + in_rand(frm); + random_number_dump(level + 1, frm); + return; + case 9: + comb_key(frm); + random_number_dump(level + 1, frm); + return; + case 11: + au_rand(frm); + random_number_dump(level + 1, frm); + return; + case 12: + sres(frm); + auth_resp_dump(level + 1, frm); + return; + case 13: + case 17: + random_number_dump(level + 1, frm); + return; + case 10: + case 14: + key_dump(level + 1, frm); + return; + case 15: + encryption_mode_req_dump(level + 1, frm); + return; + case 16: + encryption_key_size_req_dump(level + 1, frm); + return; + case 19: + switch_req_dump(level + 1, frm); + return; + case 20: + case 21: + hold_dump(level + 1, frm); + return; + case 23: + sniff_req_dump(level + 1, frm); + return; + case 25: + park_req_dump(level + 1, frm); + return; + case 28: + modify_beacon_dump(level + 1, frm); + return; + case 31: + case 32: + power_req_dump(level + 1, frm); + return; + case 36: + preferred_rate_dump(level + 1, frm); + return; + case 37: + case 38: + version_dump(level + 1, frm); + return; + case 39: + case 40: + features_dump(level + 1, frm); + return; + case 41: + case 42: + quality_of_service_dump(level + 1, frm); + return; + case 43: + sco_link_req_dump(level + 1, frm); + return; + case 44: + remove_sco_link_req_dump(level + 1, frm); + return; + case 45: + case 46: + max_slots_dump(level + 1, frm); + return; + case 48: + timing_accuracy_dump(level + 1, frm); + return; + case 52: + slot_offset_dump(level + 1, frm); + return; + case 53: + case 54: + page_mode_dump(level + 1, frm); + return; + case 55: + supervision_timeout_dump(level + 1, frm); + return; + case 57: + test_control_dump(level + 1, frm); + return; + case 59: + encryption_key_size_mask_res_dump(level + 1, frm); + return; + case 60: + set_afh_dump(level + 1, frm); + return; + case 61: + encapsulated_header_dump(level + 1, frm); + return; + case 62: + encapsulated_payload_dump(level + 1, frm); + return; + case 63: + simple_pairing_confirm_dump(level + 1, frm); + return; + case 64: + simple_pairing_number_dump(level + 1, frm); + return; + case 65: + dhkey_check_dump(level + 1, frm); + return; + case 5: + case 18: + case 24: + case 33: + case 34: + case 35: + case 47: + case 49: + case 50: + case 51: + case 56: + case 58: + case 127 + (23 << 7): + case 127 + (24 << 7): + case 127 + (27 << 7): + case 127 + (28 << 7): + case 127 + (29 << 7): + return; + case 127 + (1 << 7): + accepted_ext_dump(level + 1, frm); + return; + case 127 + (2 << 7): + not_accepted_ext_dump(level + 1, frm); + return; + case 127 + (3 << 7): + case 127 + (4 << 7): + features_ext_dump(level + 1, frm); + return; + case 127 + (11 << 7): + packet_type_table_dump(level + 1, frm); + return; + case 127 + (12 << 7): + esco_link_req_dump(level + 1, frm); + return; + case 127 + (13 << 7): + remove_esco_link_req_dump(level + 1, frm); + return; + case 127 + (16 << 7): + channel_classification_req_dump(level + 1, frm); + return; + case 127 + (17 << 7): + channel_classification_dump(level + 1, frm); + return; + case 127 + (21 << 7): + case 127 + (22 << 7): + sniff_subrating_dump(level + 1, frm); + return; + case 127 + (25 << 7): + case 127 + (26 << 7): + io_capability_dump(level + 1, frm); + return; + case 127 + (30 << 7): + keypress_notification_dump(level + 1, frm); + return; + } + + raw_dump(level, frm); +} diff --git a/parser/obex.c b/parser/obex.c new file mode 100644 index 0000000..0f68443 --- /dev/null +++ b/parser/obex.c @@ -0,0 +1,309 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" + +static char *opcode2str(uint8_t opcode) +{ + switch (opcode & 0x7f) { + case 0x00: + return "Connect"; + case 0x01: + return "Disconnect"; + case 0x02: + return "Put"; + case 0x03: + return "Get"; + case 0x04: + return "Reserved"; + case 0x05: + return "SetPath"; + case 0x06: + return "Reserved"; + case 0x07: + return "Session"; + case 0x7f: + return "Abort"; + case 0x10: + return "Continue"; + case 0x20: + return "Success"; + case 0x21: + return "Created"; + case 0x22: + return "Accepted"; + case 0x23: + return "Non-authoritative information"; + case 0x24: + return "No content"; + case 0x25: + return "Reset content"; + case 0x26: + return "Partial content"; + case 0x30: + return "Multiple choices"; + case 0x31: + return "Moved permanently"; + case 0x32: + return "Moved temporarily"; + case 0x33: + return "See other"; + case 0x34: + return "Not modified"; + case 0x35: + return "Use Proxy"; + case 0x40: + return "Bad request"; + case 0x41: + return "Unauthorized"; + case 0x42: + return "Payment required"; + case 0x43: + return "Forbidden"; + case 0x44: + return "Not found"; + case 0x45: + return "Method not allowed"; + case 0x46: + return "Not acceptable"; + case 0x47: + return "Proxy authentication required"; + case 0x48: + return "Request timeout"; + case 0x49: + return "Conflict"; + case 0x4a: + return "Gone"; + case 0x4b: + return "Length required"; + case 0x4c: + return "Precondition failed"; + case 0x4d: + return "Requested entity too large"; + case 0x4e: + return "Requested URL too large"; + case 0x4f: + return "Unsupported media type"; + case 0x50: + return "Internal server error"; + case 0x51: + return "Not implemented"; + case 0x52: + return "Bad gateway"; + case 0x53: + return "Service unavailable"; + case 0x54: + return "Gateway timeout"; + case 0x55: + return "HTTP version not supported"; + case 0x60: + return "Database full"; + case 0x61: + return "Database locked"; + default: + return "Unknown"; + } +} + +static char *hi2str(uint8_t hi) +{ + switch (hi & 0x3f) { + case 0x00: + return "Count"; + case 0x01: + return "Name"; + case 0x02: + return "Type"; + case 0x03: + return "Length"; + case 0x04: + return "Time"; + case 0x05: + return "Description"; + case 0x06: + return "Target"; + case 0x07: + return "HTTP"; + case 0x08: + return "Body"; + case 0x09: + return "End of Body"; + case 0x0a: + return "Who"; + case 0x0b: + return "Connection ID"; + case 0x0c: + return "App. Parameters"; + case 0x0d: + return "Auth. Challenge"; + case 0x0e: + return "Auth. Response"; + case 0x0f: + return "Creator ID"; + case 0x10: + return "WAN UUID"; + case 0x11: + return "Object Class"; + case 0x12: + return "Session Parameters"; + case 0x13: + return "Session Sequence Number"; + default: + return "Unknown"; + } +} + +static void parse_headers(int level, struct frame *frm) +{ + uint8_t hi, hv8; + uint16_t len; + uint32_t hv32; + + while (frm->len > 0) { + hi = get_u8(frm); + + p_indent(level, frm); + + printf("%s (0x%02x)", hi2str(hi), hi); + switch (hi & 0xc0) { + case 0x00: /* Unicode */ + len = get_u16(frm) - 3; + printf(" = Unicode length %d\n", len); + raw_ndump(level, frm, len); + frm->ptr += len; + frm->len -= len; + break; + + case 0x40: /* Byte sequence */ + len = get_u16(frm) - 3; + printf(" = Sequence length %d\n", len); + raw_ndump(level, frm, len); + frm->ptr += len; + frm->len -= len; + break; + + case 0x80: /* One byte */ + hv8 = get_u8(frm); + printf(" = %d\n", hv8); + break; + + case 0xc0: /* Four bytes */ + hv32 = get_u32(frm); + printf(" = %u\n", hv32); + break; + } + } +} + +void obex_dump(int level, struct frame *frm) +{ + uint8_t last_opcode, opcode, status; + uint8_t version, flags, constants; + uint16_t length, pktlen; + + frm = add_frame(frm); + + while (frm->len > 0) { + opcode = get_u8(frm); + length = get_u16(frm); + status = opcode & 0x7f; + + if (frm->len < length - 3) { + frm->ptr -= 3; + frm->len += 3; + return; + } + + p_indent(level, frm); + + last_opcode = get_opcode(frm->handle, frm->dlci); + + if (!(opcode & 0x70)) { + printf("OBEX: %s cmd(%c): len %d", + opcode2str(opcode), + opcode & 0x80 ? 'f' : 'c', length); + set_opcode(frm->handle, frm->dlci, opcode); + } else { + printf("OBEX: %s rsp(%c): status %x%02d len %d", + opcode2str(last_opcode), + opcode & 0x80 ? 'f' : 'c', + status >> 4, status & 0xf, length); + opcode = last_opcode; + } + + if (get_status(frm->handle, frm->dlci) == 0x10) + printf(" (continue)"); + + set_status(frm->handle, frm->dlci, status); + + if (frm->len == 0) { + printf("\n"); + break; + } + + switch (opcode & 0x7f) { + case 0x00: /* Connect */ + version = get_u8(frm); + flags = get_u8(frm); + pktlen = get_u16(frm); + printf(" version %d.%d flags %d mtu %d\n", + version >> 4, version & 0xf, flags, pktlen); + break; + + case 0x05: /* SetPath */ + if (length > 3) { + flags = get_u8(frm); + constants = get_u8(frm); + printf(" flags %d constants %d\n", + flags, constants); + } else + printf("\n"); + break; + + default: + printf("\n"); + } + + if ((status & 0x70) && (parser.flags & DUMP_VERBOSE)) { + p_indent(level, frm); + printf("Status %x%02d = %s\n", + status >> 4, status & 0xf, + opcode2str(status)); + } + + parse_headers(level, frm); + } +} diff --git a/parser/parser.c b/parser/parser.c new file mode 100644 index 0000000..e50590e --- /dev/null +++ b/parser/parser.c @@ -0,0 +1,346 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2002 Maxim Krasnyansky + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" +#include "rfcomm.h" + +struct parser_t parser; + +void init_parser(unsigned long flags, unsigned long filter, + unsigned short defpsm, unsigned short defcompid, + int pppdump_fd, int audio_fd) +{ + if ((flags & DUMP_RAW) && !(flags & DUMP_TYPE_MASK)) + flags |= DUMP_HEX; + + parser.flags = flags; + parser.filter = filter; + parser.defpsm = defpsm; + parser.defcompid = defcompid; + parser.state = 0; + parser.pppdump_fd = pppdump_fd; + parser.audio_fd = audio_fd; +} + +#define PROTO_TABLE_SIZE 20 + +static struct { + uint16_t handle; + uint16_t psm; + uint8_t channel; + uint32_t proto; +} proto_table[PROTO_TABLE_SIZE]; + +void set_proto(uint16_t handle, uint16_t psm, uint8_t channel, uint32_t proto) +{ + int i, pos = -1; + + if (psm > 0 && psm < 0x1000 && !channel) + return; + + if (!psm && channel) + psm = RFCOMM_PSM; + + for (i = 0; i < PROTO_TABLE_SIZE; i++) { + if (proto_table[i].handle == handle && proto_table[i].psm == psm && proto_table[i].channel == channel) { + pos = i; + break; + } + + if (pos < 0 && !proto_table[i].handle && !proto_table[i].psm && !proto_table[i].channel) + pos = i; + } + + if (pos < 0) + return; + + proto_table[pos].handle = handle; + proto_table[pos].psm = psm; + proto_table[pos].channel = channel; + proto_table[pos].proto = proto; +} + +uint32_t get_proto(uint16_t handle, uint16_t psm, uint8_t channel) +{ + int i, pos = -1; + + if (!psm && channel) + psm = RFCOMM_PSM; + + for (i = 0; i < PROTO_TABLE_SIZE; i++) { + if (proto_table[i].handle == handle && proto_table[i].psm == psm && proto_table[i].channel == channel) + return proto_table[i].proto; + + if (!proto_table[i].handle) { + if (proto_table[i].psm == psm && proto_table[i].channel == channel) + pos = i; + } + } + + return (pos < 0) ? 0 : proto_table[pos].proto; +} + +#define FRAME_TABLE_SIZE 20 + +static struct { + uint16_t handle; + uint8_t dlci; + uint8_t opcode; + uint8_t status; + struct frame frm; +} frame_table[FRAME_TABLE_SIZE]; + +void del_frame(uint16_t handle, uint8_t dlci) +{ + int i; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) + if (frame_table[i].handle == handle && + frame_table[i].dlci == dlci) { + frame_table[i].handle = 0; + frame_table[i].dlci = 0; + frame_table[i].opcode = 0; + frame_table[i].status = 0; + if (frame_table[i].frm.data) + free(frame_table[i].frm.data); + memset(&frame_table[i].frm, 0, sizeof(struct frame)); + break; + } +} + +struct frame *add_frame(struct frame *frm) +{ + struct frame *fr; + void *data; + int i, pos = -1; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) { + if (frame_table[i].handle == frm->handle && + frame_table[i].dlci == frm->dlci) { + pos = i; + break; + } + + if (pos < 0 && !frame_table[i].handle && !frame_table[i].dlci) + pos = i; + } + + if (pos < 0) + return frm; + + frame_table[pos].handle = frm->handle; + frame_table[pos].dlci = frm->dlci; + fr = &frame_table[pos].frm; + + data = malloc(fr->len + frm->len); + if (!data) { + perror("Can't allocate frame stream buffer"); + del_frame(frm->handle, frm->dlci); + return frm; + } + + if (fr->len > 0) + memcpy(data, fr->ptr, fr->len); + + if (frm->len > 0) + memcpy(data + fr->len, frm->ptr, frm->len); + + if (fr->data) + free(fr->data); + + fr->data = data; + fr->data_len = fr->len + frm->len; + fr->len = fr->data_len; + fr->ptr = fr->data; + fr->dev_id = frm->dev_id; + fr->in = frm->in; + fr->ts = frm->ts; + fr->handle = frm->handle; + fr->cid = frm->cid; + fr->num = frm->num; + fr->dlci = frm->dlci; + fr->channel = frm->channel; + fr->pppdump_fd = frm->pppdump_fd; + fr->audio_fd = frm->audio_fd; + + return fr; +} + +uint8_t get_opcode(uint16_t handle, uint8_t dlci) +{ + int i; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) + if (frame_table[i].handle == handle && + frame_table[i].dlci == dlci) + return frame_table[i].opcode; + + return 0x00; +} + +void set_opcode(uint16_t handle, uint8_t dlci, uint8_t opcode) +{ + int i; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) + if (frame_table[i].handle == handle && + frame_table[i].dlci == dlci) { + frame_table[i].opcode = opcode; + break; + } +} + +uint8_t get_status(uint16_t handle, uint8_t dlci) +{ + int i; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) + if (frame_table[i].handle == handle && + frame_table[i].dlci == dlci) + return frame_table[i].status; + + return 0x00; +} + +void set_status(uint16_t handle, uint8_t dlci, uint8_t status) +{ + int i; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) + if (frame_table[i].handle == handle && + frame_table[i].dlci == dlci) { + frame_table[i].status = status; + break; + } +} + +void ascii_dump(int level, struct frame *frm, int num) +{ + unsigned char *buf = frm->ptr; + register int i, n; + + if ((num < 0) || (num > frm->len)) + num = frm->len; + + for (i = 0, n = 1; i < num; i++, n++) { + if (n == 1) + p_indent(level, frm); + printf("%1c ", isprint(buf[i]) ? buf[i] : '.'); + if (n == DUMP_WIDTH) { + printf("\n"); + n = 0; + } + } + if (i && n != 1) + printf("\n"); +} + +void hex_dump(int level, struct frame *frm, int num) +{ + unsigned char *buf = frm->ptr; + register int i, n; + + if ((num < 0) || (num > frm->len)) + num = frm->len; + + for (i = 0, n = 1; i < num; i++, n++) { + if (n == 1) + p_indent(level, frm); + printf("%2.2X ", buf[i]); + if (n == DUMP_WIDTH) { + printf("\n"); + n = 0; + } + } + if (i && n != 1) + printf("\n"); +} + +void ext_dump(int level, struct frame *frm, int num) +{ + unsigned char *buf = frm->ptr; + register int i, n = 0, size; + + if ((num < 0) || (num > frm->len)) + num = frm->len; + + while (num > 0) { + p_indent(level, frm); + printf("%04x: ", n); + + size = num > 16 ? 16 : num; + + for (i = 0; i < size; i++) + printf("%02x%s", buf[i], (i + 1) % 8 ? " " : " "); + for (i = size; i < 16; i++) + printf(" %s", (i + 1) % 8 ? " " : " "); + + for (i = 0; i < size; i++) + printf("%1c", isprint(buf[i]) ? buf[i] : '.'); + printf("\n"); + + buf += size; + num -= size; + n += size; + } +} + +void raw_ndump(int level, struct frame *frm, int num) +{ + if (!frm->len) + return; + + switch (parser.flags & DUMP_TYPE_MASK) { + case DUMP_ASCII: + ascii_dump(level, frm, num); + break; + + case DUMP_HEX: + hex_dump(level, frm, num); + break; + + case DUMP_EXT: + ext_dump(level, frm, num); + break; + } +} + +void raw_dump(int level, struct frame *frm) +{ + raw_ndump(level, frm, -1); +} diff --git a/parser/parser.h b/parser/parser.h new file mode 100644 index 0000000..d3a0397 --- /dev/null +++ b/parser/parser.h @@ -0,0 +1,252 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2002 Maxim Krasnyansky + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __PARSER_H +#define __PARSER_H + +#include +#include +#include +#include + +struct frame { + void *data; + uint32_t data_len; + void *ptr; + uint32_t len; + uint16_t dev_id; + uint8_t in; + uint8_t master; + uint16_t handle; + uint16_t cid; + uint16_t num; + uint8_t dlci; + uint8_t channel; + unsigned long flags; + struct timeval ts; + int pppdump_fd; + int audio_fd; +}; + +/* Parser flags */ +#define DUMP_WIDTH 20 + +#define DUMP_ASCII 0x0001 +#define DUMP_HEX 0x0002 +#define DUMP_EXT 0x0004 +#define DUMP_RAW 0x0008 +#define DUMP_BPA 0x0010 +#define DUMP_TSTAMP 0x0100 +#define DUMP_VERBOSE 0x0200 +#define DUMP_BTSNOOP 0x1000 +#define DUMP_PKTLOG 0x2000 +#define DUMP_NOVENDOR 0x4000 +#define DUMP_TYPE_MASK (DUMP_ASCII | DUMP_HEX | DUMP_EXT) + +/* Parser filter */ +#define FILT_LMP 0x0001 +#define FILT_HCI 0x0002 +#define FILT_SCO 0x0004 +#define FILT_L2CAP 0x0008 +#define FILT_RFCOMM 0x0010 +#define FILT_SDP 0x0020 +#define FILT_BNEP 0x0040 +#define FILT_CMTP 0x0080 +#define FILT_HIDP 0x0100 +#define FILT_HCRP 0x0200 +#define FILT_AVDTP 0x0400 +#define FILT_AVCTP 0x0800 + +#define FILT_OBEX 0x00010000 +#define FILT_CAPI 0x00020000 +#define FILT_PPP 0x00040000 +#define FILT_ERICSSON 0x10000000 +#define FILT_CSR 0x1000000a +#define FILT_DGA 0x1000000c + +#define STRUCT_OFFSET(type, member) ((uint8_t *)&(((type *)NULL)->member) - \ + (uint8_t *)((type *)NULL)) + +#define STRUCT_END(type, member) (STRUCT_OFFSET(type, member) + \ + sizeof(((type *)NULL)->member)) + +#define DEFAULT_COMPID 65535 + +struct parser_t { + unsigned long flags; + unsigned long filter; + unsigned short defpsm; + unsigned short defcompid; + int state; + int pppdump_fd; + int audio_fd; +}; + +extern struct parser_t parser; + +void init_parser(unsigned long flags, unsigned long filter, + unsigned short defpsm, unsigned short defcompid, + int pppdump_fd, int audio_fd); + +static inline int p_filter(unsigned long f) +{ + return !(parser.filter & f); +} + +static inline void p_indent(int level, struct frame *f) +{ + if (level < 0) { + parser.state = 0; + return; + } + + if (!parser.state) { + if (parser.flags & DUMP_TSTAMP) { + if (parser.flags & DUMP_VERBOSE) { + struct tm tm; + time_t t = f->ts.tv_sec; + localtime_r(&t, &tm); + printf("%04d-%02d-%02d %02d:%02d:%02d.%06lu ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, f->ts.tv_usec); + } else + printf("%8lu.%06lu ", f->ts.tv_sec, f->ts.tv_usec); + } + printf("%c ", (f->in ? '>' : '<')); + parser.state = 1; + } else + printf(" "); + + if (level) + printf("%*c", (level*2), ' '); +} + +static inline void p_ba2str(const bdaddr_t *ba, char *str) +{ + if (parser.flags & DUMP_NOVENDOR) { + uint8_t b[6]; + + baswap((bdaddr_t *) b, ba); + sprintf(str, "%2.2X:%2.2X:%2.2X:*:*:*", b[0], b[1], b[2]); + } else + ba2str(ba, str); +} + +/* get_uXX functions do byte swaping */ + +static inline uint8_t get_u8(struct frame *frm) +{ + uint8_t *u8_ptr = frm->ptr; + frm->ptr += 1; + frm->len -= 1; + return *u8_ptr; +} + +static inline uint16_t get_u16(struct frame *frm) +{ + uint16_t *u16_ptr = frm->ptr; + frm->ptr += 2; + frm->len -= 2; + return ntohs(bt_get_unaligned(u16_ptr)); +} + +static inline uint32_t get_u32(struct frame *frm) +{ + uint32_t *u32_ptr = frm->ptr; + frm->ptr += 4; + frm->len -= 4; + return ntohl(bt_get_unaligned(u32_ptr)); +} + +static inline uint64_t get_u64(struct frame *frm) +{ + uint64_t *u64_ptr = frm->ptr; + uint64_t u64 = bt_get_unaligned(u64_ptr), tmp; + frm->ptr += 8; + frm->len -= 8; + tmp = ntohl(u64 & 0xffffffff); + u64 = (tmp << 32) | ntohl(u64 >> 32); + return u64; +} + +static inline void get_u128(struct frame *frm, uint64_t *l, uint64_t *h) +{ + *h = get_u64(frm); + *l = get_u64(frm); +} + +char *get_uuid_name(int uuid); + +void set_proto(uint16_t handle, uint16_t psm, uint8_t channel, uint32_t proto); +uint32_t get_proto(uint16_t handle, uint16_t psm, uint8_t channel); + +struct frame *add_frame(struct frame *frm); +void del_frame(uint16_t handle, uint8_t dlci); + +uint8_t get_opcode(uint16_t handle, uint8_t dlci); +void set_opcode(uint16_t handle, uint8_t dlci, uint8_t opcode); + +uint8_t get_status(uint16_t handle, uint8_t dlci); +void set_status(uint16_t handle, uint8_t dlci, uint8_t status); + +void l2cap_clear(uint16_t handle); + +void ascii_dump(int level, struct frame *frm, int num); +void hex_dump(int level, struct frame *frm, int num); +void ext_dump(int level, struct frame *frm, int num); +void raw_dump(int level, struct frame *frm); +void raw_ndump(int level, struct frame *frm, int num); + +void lmp_dump(int level, struct frame *frm); +void hci_dump(int level, struct frame *frm); +void l2cap_dump(int level, struct frame *frm); +void rfcomm_dump(int level, struct frame *frm); +void sdp_dump(int level, struct frame *frm); +void bnep_dump(int level, struct frame *frm); +void cmtp_dump(int level, struct frame *frm); +void hidp_dump(int level, struct frame *frm); +void hcrp_dump(int level, struct frame *frm); +void avdtp_dump(int level, struct frame *frm); +void avctp_dump(int level, struct frame *frm); + +void obex_dump(int level, struct frame *frm); +void capi_dump(int level, struct frame *frm); +void ppp_dump(int level, struct frame *frm); +void arp_dump(int level, struct frame *frm); +void ip_dump(int level, struct frame *frm); +void ericsson_dump(int level, struct frame *frm); +void csr_dump(int level, struct frame *frm); +void bpa_dump(int level, struct frame *frm); + +static inline void parse(struct frame *frm) +{ + p_indent(-1, NULL); + if (parser.flags & DUMP_RAW) + raw_dump(0, frm); + else + hci_dump(0, frm); + fflush(stdout); +} + +#endif /* __PARSER_H */ diff --git a/parser/ppp.c b/parser/ppp.c new file mode 100644 index 0000000..6ef24e9 --- /dev/null +++ b/parser/ppp.c @@ -0,0 +1,229 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "parser.h" + +#define PPP_U8(frm) (get_u8(frm)) +#define PPP_U16(frm) (btohs(htons(get_u16(frm)))) +#define PPP_U32(frm) (btohl(htonl(get_u32(frm)))) + +static int ppp_traffic = 0; + +static unsigned char ppp_magic1[] = { 0x7e, 0xff, 0x03, 0xc0, 0x21 }; +static unsigned char ppp_magic2[] = { 0x7e, 0xff, 0x7d, 0x23, 0xc0, 0x21 }; +static unsigned char ppp_magic3[] = { 0x7e, 0x7d, 0xdf, 0x7d, 0x23, 0xc0, 0x21 }; + +static inline int check_for_ppp_traffic(unsigned char *data, int size) +{ + int i; + + for (i = 0; i < size - sizeof(ppp_magic1); i++) + if (!memcmp(data + i, ppp_magic1, sizeof(ppp_magic1))) { + ppp_traffic = 1; + return i; + } + + for (i = 0; i < size - sizeof(ppp_magic2); i++) + if (!memcmp(data + i, ppp_magic2, sizeof(ppp_magic2))) { + ppp_traffic = 1; + return i; + } + + for (i = 0; i < size - sizeof(ppp_magic3); i++) + if (!memcmp(data + i, ppp_magic3, sizeof(ppp_magic3))) { + ppp_traffic = 1; + return i; + } + + return -1; +} + +static inline char *dir2str(uint8_t in) +{ + return in ? "DCE" : "DTE"; +} + +static inline char *proto2str(uint16_t proto) +{ + switch (proto) { + case 0x0001: + return "Padding Protocol"; + case 0x0021: + return "IP"; + case 0x8021: + return "IP Control Protocol"; + case 0x80fd: + return "Compression Control Protocol"; + case 0xc021: + return "Link Control Protocol"; + case 0xc023: + return "Password Authentication Protocol"; + case 0xc025: + return "Link Quality Report"; + case 0xc223: + return "Challenge Handshake Authentication Protocol"; + default: + return "Unknown Protocol"; + } +} + +static void hdlc_dump(int level, struct frame *frm) +{ + uint8_t addr = get_u8(frm); + uint8_t ctrl = get_u8(frm); + uint16_t fcs, proto; + + fcs = bt_get_unaligned((uint16_t *) (frm->ptr + frm->len - 2)); + frm->len -= 2; + + p_indent(level, frm); + + if (addr != 0xff || ctrl != 0x03) { + frm->ptr -= 2; + frm->len += 2; + printf("HDLC: %s: len %d fcs 0x%04x\n", + dir2str(frm->in), frm->len, fcs); + } else + printf("HDLC: %s: addr 0x%02x ctrl 0x%02x len %d fcs 0x%04x\n", + dir2str(frm->in), addr, ctrl, frm->len, fcs); + + if (*((uint8_t *) frm->ptr) & 0x80) + proto = get_u16(frm); + else + proto = get_u8(frm); + + p_indent(level + 1, frm); + printf("PPP: %s (0x%04x): len %d\n", proto2str(proto), proto, frm->len); + + raw_dump(level + 1, frm); +} + +static inline void unslip_frame(int level, struct frame *frm, int len) +{ + struct frame msg; + unsigned char *data, *ptr; + int i, p = 0; + + data = malloc(len * 2); + if (!data) + return; + + ptr = frm->ptr; + + for (i = 0; i < len; i++) { + if (ptr[i] == 0x7d) { + data[p++] = ptr[i + 1] ^ 0x20; + i++; + } else + data[p++] = ptr[i]; + } + + memset(&msg, 0, sizeof(msg)); + msg.data = data; + msg.data_len = len * 2; + msg.ptr = msg.data; + msg.len = p; + msg.in = frm->in; + msg.ts = frm->ts; + msg.handle = frm->handle; + msg.cid = frm->cid; + + hdlc_dump(level, &msg); + + free(data); +} + +void ppp_dump(int level, struct frame *frm) +{ + void *ptr, *end; + int err, len, pos = 0; + + if (frm->pppdump_fd > fileno(stderr)) { + unsigned char id; + uint16_t len = htons(frm->len); + uint32_t ts = htonl(frm->ts.tv_sec & 0xffffffff); + + id = 0x07; + err = write(frm->pppdump_fd, &id, 1); + err = write(frm->pppdump_fd, &ts, 4); + + id = frm->in ? 0x02 : 0x01; + err = write(frm->pppdump_fd, &id, 1); + err = write(frm->pppdump_fd, &len, 2); + err = write(frm->pppdump_fd, frm->ptr, frm->len); + } + + if (!ppp_traffic) { + pos = check_for_ppp_traffic(frm->ptr, frm->len); + if (pos < 0) { + raw_dump(level, frm); + return; + } + + if (pos > 0) { + raw_ndump(level, frm, pos); + frm->ptr += pos; + frm->len -= pos; + } + } + + frm = add_frame(frm); + + while (frm->len > 0) { + ptr = memchr(frm->ptr, 0x7e, frm->len); + if (!ptr) + break; + + if (frm->ptr != ptr) { + frm->len -= (ptr - frm->ptr); + frm->ptr = ptr; + } + + end = memchr(frm->ptr + 1, 0x7e, frm->len - 1); + if (!end) + break; + + len = end - ptr - 1; + + frm->ptr++; + frm->len--; + + if (len > 0) { + unslip_frame(level, frm, len); + + frm->ptr += len; + frm->len -= len; + } + } +} diff --git a/parser/rfcomm.c b/parser/rfcomm.c new file mode 100644 index 0000000..b85df91 --- /dev/null +++ b/parser/rfcomm.c @@ -0,0 +1,350 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Wayne Lee + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "parser.h" +#include "rfcomm.h" +#include "sdp.h" + +static char *cr_str[] = { + "RSP", + "CMD" +}; + +#define CR_STR(mcc_head) cr_str[mcc_head->type.cr] +#define GET_DLCI(addr) ((addr.server_chn << 1) | (addr.d & 1)) + +static void print_rfcomm_hdr(long_frame_head* head, uint8_t *ptr, int len) +{ + address_field addr = head->addr; + uint8_t ctr = head->control; + uint16_t ilen = head->length.bits.len; + uint8_t ctr_type,pf,dlci,fcs; + + dlci = GET_DLCI(addr); + pf = GET_PF(ctr); + ctr_type = CLR_PF(ctr); + fcs = *(ptr + len - 1); + + printf("cr %d dlci %d pf %d ilen %d fcs 0x%x ", addr.cr, dlci, pf, ilen, fcs); +} + +static void print_mcc(mcc_long_frame_head* mcc_head) +{ + printf("mcc_len %d\n", mcc_head->length.bits.len); +} + +static inline void mcc_test(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + printf("TEST %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); +} +static inline void mcc_fcon(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + printf("FCON %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); +} + +static inline void mcc_fcoff(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + printf("FCOFF %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); +} + +static inline void mcc_msc(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + msc_msg *msc = (void*) (ptr - STRUCT_END(msc_msg, mcc_s_head)); + + printf("MSC %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); + p_indent(level, 0); + printf("dlci %d fc %d rtc %d rtr %d ic %d dv %d", + GET_DLCI(msc->dlci), msc->v24_sigs.fc, msc->v24_sigs.rtc, + msc->v24_sigs.rtr, msc->v24_sigs.ic, msc->v24_sigs.dv ); + + /* Assuming that break_signals field is _not declared_ in struct msc_msg... */ + if (len > STRUCT_OFFSET(msc_msg, fcs) - STRUCT_END(msc_msg, v24_sigs)) { + break_signals *brk = (break_signals *) + (ptr + STRUCT_END(msc_msg, v24_sigs)); + printf(" b1 %d b2 %d b3 %d len %d\n", + brk->b1, brk->b2, brk->b3, brk->len); + } else + printf("\n"); +} + +static inline void mcc_rpn(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + rpn_msg *rpn = (void *) (ptr - STRUCT_END(rpn_msg, mcc_s_head)); + + printf("RPN %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); + + p_indent(level, 0); + printf("dlci %d ", GET_DLCI(rpn->dlci)); + + /* Assuming that rpn_val is _declared_ as a member of rpn_msg... */ + if (len <= STRUCT_OFFSET(rpn_msg, rpn_val) - STRUCT_END(rpn_msg, mcc_s_head)) { + printf("\n"); + return; + } + + printf("br %d db %d sb %d p %d pt %d xi %d xo %d\n", + rpn->rpn_val.bit_rate, rpn->rpn_val.data_bits, + rpn->rpn_val.stop_bit, rpn->rpn_val.parity, + rpn->rpn_val.parity_type, rpn->rpn_val.xon_input, + rpn->rpn_val.xon_output); + + p_indent(level, 0); + printf("rtri %d rtro %d rtci %d rtco %d xon %d xoff %d pm 0x%04x\n", + rpn->rpn_val.rtr_input, rpn->rpn_val.rtr_output, + rpn->rpn_val.rtc_input, rpn->rpn_val.rtc_output, + rpn->rpn_val.xon, rpn->rpn_val.xoff, btohs(rpn->rpn_val.pm)); +} + +static inline void mcc_rls(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + rls_msg* rls = (void*) (ptr - STRUCT_END(rls_msg, mcc_s_head)); + + printf("RLS %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); + printf("dlci %d error: %d", GET_DLCI(rls->dlci), rls->error); +} + +static inline void mcc_pn(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + pn_msg *pn = (void*) (ptr - STRUCT_END(pn_msg, mcc_s_head)); + + printf("PN %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); + + p_indent(level, 0); + printf("dlci %d frame_type %d credit_flow %d pri %d ack_timer %d\n", + pn->dlci, pn->frame_type, pn->credit_flow, pn->prior, pn->ack_timer); + p_indent(level, 0); + printf("frame_size %d max_retrans %d credits %d\n", + btohs(pn->frame_size), pn->max_nbrof_retrans, pn->credits); +} + +static inline void mcc_nsc(int level, uint8_t *ptr, int len, + long_frame_head *head, mcc_long_frame_head *mcc_head) +{ + + nsc_msg *nsc = (void*) (ptr - STRUCT_END(nsc_msg, mcc_s_head)); + + printf("NSC %s: ", CR_STR(mcc_head)); + print_rfcomm_hdr(head, ptr, len); + print_mcc(mcc_head); + + p_indent(level, 0); + printf("cr %d, mcc_cmd_type %x\n", + nsc->command_type.cr, nsc->command_type.type ); +} + +static inline void mcc_frame(int level, struct frame *frm, long_frame_head *head) +{ + mcc_short_frame_head *mcc_short_head_p = frm->ptr; + mcc_long_frame_head mcc_head; + uint8_t hdr_size; + + if ( mcc_short_head_p->length.ea == EA ) { + mcc_head.type = mcc_short_head_p->type; + mcc_head.length.bits.len = mcc_short_head_p->length.len; + hdr_size = sizeof(mcc_short_frame_head); + } else { + mcc_head = *(mcc_long_frame_head *)frm->ptr; + mcc_head.length.val = btohs(mcc_head.length.val); + hdr_size = sizeof(mcc_long_frame_head); + } + + frm->ptr += hdr_size; + frm->len -= hdr_size; + + p_indent(level, frm); + printf("RFCOMM(s): "); + + switch (mcc_head.type.type) { + case TEST: + mcc_test(level, frm->ptr, frm->len, head, &mcc_head); + raw_dump(level, frm); + break; + case FCON: + mcc_fcon(level, frm->ptr, frm->len, head, &mcc_head); + break; + case FCOFF: + mcc_fcoff(level, frm->ptr, frm->len, head, &mcc_head); + break; + case MSC: + mcc_msc(level, frm->ptr, frm->len, head, &mcc_head); + break; + case RPN: + mcc_rpn(level, frm->ptr, frm->len, head, &mcc_head); + break; + case RLS: + mcc_rls(level, frm->ptr, frm->len, head, &mcc_head); + break; + case PN: + mcc_pn(level, frm->ptr, frm->len, head, &mcc_head); + break; + case NSC: + mcc_nsc(level, frm->ptr, frm->len, head, &mcc_head); + break; + default: + printf("MCC message type 0x%02x: ", mcc_head.type.type); + print_rfcomm_hdr(head, frm->ptr, frm->len); + printf("\n"); + + frm->len--; + raw_dump(level, frm); + } +} + +static inline void uih_frame(int level, struct frame *frm, long_frame_head *head) +{ + uint32_t proto; + + if (!head->addr.server_chn) { + mcc_frame(level, frm, head); + } else { + p_indent(level, frm); + printf("RFCOMM(d): UIH: "); + print_rfcomm_hdr(head, frm->ptr, frm->len); + if (GET_PF(head->control)) { + printf("credits %d\n", *(uint8_t *)(frm->ptr)); + frm->ptr++; + frm->len--; + } else + printf("\n"); + + frm->len--; + frm->dlci = GET_DLCI(head->addr); + frm->channel = head->addr.server_chn; + + proto = get_proto(frm->handle, RFCOMM_PSM, frm->channel); + + if (frm->len > 0) { + switch (proto) { + case SDP_UUID_OBEX: + if (!p_filter(FILT_OBEX)) + obex_dump(level + 1, frm); + else + raw_dump(level, frm); + break; + + case SDP_UUID_LAN_ACCESS_PPP: + case SDP_UUID_DIALUP_NETWORKING: + if (!p_filter(FILT_PPP)) + ppp_dump(level + 1, frm); + else + raw_dump(level, frm); + break; + + default: + if (p_filter(FILT_RFCOMM)) + break; + + raw_dump(level, frm); + break; + } + } + } +} + +void rfcomm_dump(int level, struct frame *frm) +{ + uint8_t hdr_size, ctr_type; + short_frame_head *short_head_p = (void *) frm->ptr; + long_frame_head head; + + if (short_head_p->length.ea == EA) { + head.addr = short_head_p->addr; + head.control = short_head_p->control; + head.length.bits.len = short_head_p->length.len; + hdr_size = sizeof(short_frame_head); + } else { + head = *(long_frame_head *) frm->ptr; + head.length.val = btohs(head.length.val); + hdr_size = sizeof(long_frame_head); + } + + frm->ptr += hdr_size; + frm->len -= hdr_size; + + ctr_type = CLR_PF(head.control); + + if (ctr_type == UIH) { + uih_frame(level, frm, &head); + } else { + p_indent(level, frm); + printf("RFCOMM(s): "); + + switch (ctr_type) { + case SABM: + printf("SABM: "); + break; + case UA: + printf("UA: "); + break; + case DM: + printf("DM: "); + break; + case DISC: + printf("DISC: "); + del_frame(frm->handle, GET_DLCI(head.addr)); + break; + default: + printf("ERR: "); + } + print_rfcomm_hdr(&head, frm->ptr, frm->len); + printf("\n"); + } +} diff --git a/parser/rfcomm.h b/parser/rfcomm.h new file mode 100644 index 0000000..4824842 --- /dev/null +++ b/parser/rfcomm.h @@ -0,0 +1,494 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Wayne Lee + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __RFCOMM_H +#define __RFCOMM_H + +#include + +#define RFCOMM_PSM 3 + +#define TRUE 1 +#define FALSE 0 + +#define RFCOMM_MAX_CONN 10 +#define BT_NBR_DATAPORTS RFCOMM_MAX_CONN + +#define GET_BIT(pos,bitfield) ((bitfield[(pos)/32]) & (1 << ((pos) % 32))) +#define SET_BIT(pos,bitfield) ((bitfield[(pos)/32]) |= (1 << ((pos) % 32))) +#define CLR_BIT(pos,bitfield) ((bitfield[(pos)/32]) &= ((1 << ((pos) % 32)) ^ (~0))) + +/* Sets the P/F-bit in the control field */ +#define SET_PF(ctr) ((ctr) | (1 << 4)) +/* Clears the P/F-bit in the control field */ +#define CLR_PF(ctr) ((ctr) & 0xef) +/* Returns the P/F-bit */ +#define GET_PF(ctr) (((ctr) >> 4) & 0x1) + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* Endian-swapping macros for structs */ +#define swap_long_frame(x) ((x)->h.length.val = le16_to_cpu((x)->h.length.val)) +#define swap_mcc_long_frame(x) (swap_long_frame(x)) + +/* Used for UIH packets */ +#define SHORT_CRC_CHECK 2 +/* Used for all packet exepts for the UIH packets */ +#define LONG_CRC_CHECK 3 +/* Short header for short UIH packets */ +#define SHORT_HDR 2 +/* Long header for long UIH packets */ +#define LONG_HDR 3 + +/* FIXME: Should this one be defined here? */ +#define SHORT_PAYLOAD_SIZE 127 +/* Used for setting the EA field in different packets, really neccessary? */ +#define EA 1 +/* Yes the FCS size is only one byte */ +#define FCS_SIZE 1 + +#define RFCOMM_MAX_HDR_SIZE 5 + +#define MAX_CREDITS 30 +#define START_CREDITS 7 +#define MIN_CREDITS 6 + +#define DEF_RFCOMM_MTU 127 + +/* The values in the control field when sending ordinary rfcomm packets */ +#define SABM 0x2f /* set asynchronous balanced mode */ +#define UA 0x63 /* unnumbered acknolodgement */ +#define DM 0x0f /* disconnected mode */ +#define DISC 0x43 /* disconnect */ +#define UIH 0xef /* unnumbered information with header check (only) */ +#define UI 0x03 /* unnumbered information (with all data check) */ + +#define SABM_SIZE 4 +#define UA_SIZE 4 + +/* The values in the type field in a multiplexer command packet */ +#define PN (0x80 >> 2) /* parameter negotiation */ +#define PSC (0x40 >> 2) /* power saving control */ +#define CLD (0xc0 >> 2) /* close down */ +#define TEST (0x20 >> 2) /* test */ +#define FCON (0xa0 >> 2) /* flow control on */ +#define FCOFF (0x60 >> 2) /* flow control off */ +#define MSC (0xe0 >> 2) /* modem status command */ +#define NSC (0x10 >> 2) /* not supported command response */ +#define RPN (0x90 >> 2) /* remote port negotiation */ +#define RLS (0x50 >> 2) /* remote line status */ +#define SNC (0xd0 >> 2) /* service negotiation command */ + +/* Define of some V.24 signals modem control signals in RFCOMM */ +#define DV 0x80 /* data valid */ +#define IC 0x40 /* incoming call */ +#define RTR 0x08 /* ready to receive */ +#define RTC 0x04 /* ready to communicate */ +#define FC 0x02 /* flow control (unable to accept frames) */ + +#define CTRL_CHAN 0 /* The control channel is defined as DLCI 0 in rfcomm */ +#define MCC_CMD 1 /* Multiplexer command */ +#define MCC_RSP 0 /* Multiplexer response */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +typedef struct parameter_mask { + uint8_t bit_rate:1; + uint8_t data_bits:1; + uint8_t stop_bit:1; + uint8_t parity:1; + uint8_t parity_type:1; + uint8_t xon:1; + uint8_t xoff:1; + uint8_t res1:1; + uint8_t xon_input:1; + uint8_t xon_output:1; + uint8_t rtr_input:1; + uint8_t rtr_output:1; + uint8_t rtc_input:1; + uint8_t rtc_output:1; + uint8_t res2:2; +} __attribute__ ((packed)) parameter_mask; + +typedef struct rpn_values { + uint8_t bit_rate; + uint8_t data_bits:2; + uint8_t stop_bit:1; + uint8_t parity:1; + uint8_t parity_type:2; + uint8_t res1:2; + uint8_t xon_input:1; + uint8_t xon_output:1; + uint8_t rtr_input:1; + uint8_t rtr_output:1; + uint8_t rtc_input:1; + uint8_t rtc_output:1; + uint8_t res2:2; + uint8_t xon; + uint8_t xoff; + uint16_t pm; + //parameter_mask pm; +} __attribute__ ((packed)) rpn_values; + +#elif __BYTE_ORDER == __BIG_ENDIAN + +typedef struct parameter_mask { + uint8_t res1:1; + uint8_t xoff:1; + uint8_t xon:1; + uint8_t parity_type:1; + uint8_t parity:1; + uint8_t stop_bit:1; + uint8_t data_bits:1; + uint8_t bit_rate:1; + uint8_t res2:2; + uint8_t rtc_output:1; + uint8_t rtc_input:1; + uint8_t rtr_output:1; + uint8_t rtr_input:1; + uint8_t xon_output:1; + uint8_t xon_input:1; + +} __attribute__ ((packed)) parameter_mask; + +typedef struct rpn_values { + uint8_t bit_rate; + uint8_t res1:2; + uint8_t parity_type:2; + uint8_t parity:1; + uint8_t stop_bit:1; + uint8_t data_bits:2; + uint8_t res2:2; + uint8_t rtc_output:1; + uint8_t rtc_input:1; + uint8_t rtr_output:1; + uint8_t rtr_input:1; + uint8_t xon_output:1; + uint8_t xon_input:1; + uint8_t xon; + uint8_t xoff; + uint16_t pm; + //parameter_mask pm; +} __attribute__ ((packed)) rpn_values; + +#else +#error "Unknown byte order" +#endif + +/* Typedefinitions of stuctures used for creating and parsing packets, for a + * further description of the structures please se the bluetooth core + * specification part F:1 and the ETSI TS 07.10 specification */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +typedef struct address_field { + uint8_t ea:1; + uint8_t cr:1; + uint8_t d:1; + uint8_t server_chn:5; +} __attribute__ ((packed)) address_field; + +typedef struct short_length { + uint8_t ea:1; + uint8_t len:7; +} __attribute__ ((packed)) short_length; + +typedef union long_length { + struct bits { + uint8_t ea:1; + unsigned short len:15; + } __attribute__ ((packed)) bits ; + uint16_t val ; +} __attribute__ ((packed)) long_length; + +typedef struct short_frame_head { + address_field addr; + uint8_t control; + short_length length; +} __attribute__ ((packed)) short_frame_head; + +typedef struct short_frame { + short_frame_head h; + uint8_t data[0]; +} __attribute__ ((packed)) short_frame; + +typedef struct long_frame_head { + address_field addr; + uint8_t control; + long_length length; + uint8_t data[0]; +} __attribute__ ((packed)) long_frame_head; + +typedef struct long_frame { + long_frame_head h; + uint8_t data[0]; +} __attribute__ ((packed)) long_frame; + +/* Typedefinitions for structures used for the multiplexer commands */ +typedef struct mcc_type { + uint8_t ea:1; + uint8_t cr:1; + uint8_t type:6; +} __attribute__ ((packed)) mcc_type; + +typedef struct mcc_short_frame_head { + mcc_type type; + short_length length; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_short_frame_head; + +typedef struct mcc_short_frame { + mcc_short_frame_head h; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_short_frame; + +typedef struct mcc_long_frame_head { + mcc_type type; + long_length length; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_long_frame_head; + +typedef struct mcc_long_frame { + mcc_long_frame_head h; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_long_frame; + +/* MSC-command */ +typedef struct v24_signals { + uint8_t ea:1; + uint8_t fc:1; + uint8_t rtc:1; + uint8_t rtr:1; + uint8_t reserved:2; + uint8_t ic:1; + uint8_t dv:1; +} __attribute__ ((packed)) v24_signals; + +typedef struct break_signals { + uint8_t ea:1; + uint8_t b1:1; + uint8_t b2:1; + uint8_t b3:1; + uint8_t len:4; +} __attribute__ ((packed)) break_signals; + +typedef struct msc_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + v24_signals v24_sigs; + //break_signals break_sigs; + uint8_t fcs; +} __attribute__ ((packed)) msc_msg; + +typedef struct rpn_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + rpn_values rpn_val; + uint8_t fcs; +} __attribute__ ((packed)) rpn_msg; + +/* RLS-command */ +typedef struct rls_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + uint8_t error:4; + uint8_t res:4; + uint8_t fcs; +} __attribute__ ((packed)) rls_msg; + +/* PN-command */ +typedef struct pn_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; +/* The res1, res2 and res3 values have to be set to 0 by the sender */ + uint8_t dlci:6; + uint8_t res1:2; + uint8_t frame_type:4; + uint8_t credit_flow:4; + uint8_t prior:6; + uint8_t res2:2; + uint8_t ack_timer; + uint16_t frame_size:16; + uint8_t max_nbrof_retrans; + uint8_t credits; + uint8_t fcs; +} __attribute__ ((packed)) pn_msg; + +/* NSC-command */ +typedef struct nsc_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + mcc_type command_type; + uint8_t fcs; +} __attribute__ ((packed)) nsc_msg; + +#elif __BYTE_ORDER == __BIG_ENDIAN + +typedef struct address_field { + uint8_t server_chn:5; + uint8_t d:1; + uint8_t cr:1; + uint8_t ea:1; +} __attribute__ ((packed)) address_field; + +typedef struct short_length { + uint8_t len:7; + uint8_t ea:1; +} __attribute__ ((packed)) short_length; + +typedef union long_length { + struct bits { + unsigned short len:15; + uint8_t ea:1; + } __attribute__ ((packed)) bits; + uint16_t val; +} __attribute__ ((packed)) long_length; + +typedef struct short_frame_head { + address_field addr; + uint8_t control; + short_length length; +} __attribute__ ((packed)) short_frame_head; + +typedef struct short_frame { + short_frame_head h; + uint8_t data[0]; +} __attribute__ ((packed)) short_frame; + +typedef struct long_frame_head { + address_field addr; + uint8_t control; + long_length length; + uint8_t data[0]; +} __attribute__ ((packed)) long_frame_head; + +typedef struct long_frame { + long_frame_head h; + uint8_t data[0]; +} __attribute__ ((packed)) long_frame; + +typedef struct mcc_type { + uint8_t type:6; + uint8_t cr:1; + uint8_t ea:1; +} __attribute__ ((packed)) mcc_type; + +typedef struct mcc_short_frame_head { + mcc_type type; + short_length length; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_short_frame_head; + +typedef struct mcc_short_frame { + mcc_short_frame_head h; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_short_frame; + +typedef struct mcc_long_frame_head { + mcc_type type; + long_length length; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_long_frame_head; + +typedef struct mcc_long_frame { + mcc_long_frame_head h; + uint8_t value[0]; +} __attribute__ ((packed)) mcc_long_frame; + +typedef struct v24_signals { + uint8_t dv:1; + uint8_t ic:1; + uint8_t reserved:2; + uint8_t rtr:1; + uint8_t rtc:1; + uint8_t fc:1; + uint8_t ea:1; +} __attribute__ ((packed)) v24_signals; + +typedef struct break_signals { + uint8_t len:4; + uint8_t b3:1; + uint8_t b2:1; + uint8_t b1:1; + uint8_t ea:1; +} __attribute__ ((packed)) break_signals; + +typedef struct msc_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + v24_signals v24_sigs; + //break_signals break_sigs; + uint8_t fcs; +} __attribute__ ((packed)) msc_msg; + +typedef struct rpn_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + rpn_values rpn_val; + uint8_t fcs; +} __attribute__ ((packed)) rpn_msg; + +typedef struct rls_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + address_field dlci; + uint8_t res:4; + uint8_t error:4; + uint8_t fcs; +} __attribute__ ((packed)) rls_msg; + +typedef struct pn_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + uint8_t res1:2; + uint8_t dlci:6; + uint8_t credit_flow:4; + uint8_t frame_type:4; + uint8_t res2:2; + uint8_t prior:6; + uint8_t ack_timer; + uint16_t frame_size:16; + uint8_t max_nbrof_retrans; + uint8_t credits; + uint8_t fcs; +} __attribute__ ((packed)) pn_msg; + +typedef struct nsc_msg { + short_frame_head s_head; + mcc_short_frame_head mcc_s_head; + mcc_type command_type; + uint8_t fcs; +} __attribute__ ((packed)) nsc_msg; + +#else +#error "Unknown byte order" +#error Processor endianness unknown! +#endif + +#endif /* __RFCOMM_H */ diff --git a/parser/sdp.c b/parser/sdp.c new file mode 100644 index 0000000..f0ffea3 --- /dev/null +++ b/parser/sdp.c @@ -0,0 +1,808 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Ricky Yuen + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "parser.h" +#include "sdp.h" + +#define SDP_ERROR_RSP 0x01 +#define SDP_SERVICE_SEARCH_REQ 0x02 +#define SDP_SERVICE_SEARCH_RSP 0x03 +#define SDP_SERVICE_ATTR_REQ 0x04 +#define SDP_SERVICE_ATTR_RSP 0x05 +#define SDP_SERVICE_SEARCH_ATTR_REQ 0x06 +#define SDP_SERVICE_SEARCH_ATTR_RSP 0x07 + +typedef struct { + uint8_t pid; + uint16_t tid; + uint16_t len; +} __attribute__ ((packed)) sdp_pdu_hdr; +#define SDP_PDU_HDR_SIZE 5 + +/* Data element type descriptor */ +#define SDP_DE_NULL 0 +#define SDP_DE_UINT 1 +#define SDP_DE_INT 2 +#define SDP_DE_UUID 3 +#define SDP_DE_STRING 4 +#define SDP_DE_BOOL 5 +#define SDP_DE_SEQ 6 +#define SDP_DE_ALT 7 +#define SDP_DE_URL 8 + +/* Data element size index lookup table */ +typedef struct { + int addl_bits; + int num_bytes; +} sdp_siz_idx_lookup_table_t; + +static sdp_siz_idx_lookup_table_t sdp_siz_idx_lookup_table[] = { + { 0, 1 }, /* Size index = 0 */ + { 0, 2 }, /* 1 */ + { 0, 4 }, /* 2 */ + { 0, 8 }, /* 3 */ + { 0, 16 }, /* 4 */ + { 1, 1 }, /* 5 */ + { 1, 2 }, /* 6 */ + { 1, 4 }, /* 7 */ +}; + +/* UUID name lookup table */ +typedef struct { + int uuid; + char* name; +} sdp_uuid_nam_lookup_table_t; + +static sdp_uuid_nam_lookup_table_t sdp_uuid_nam_lookup_table[] = { + { SDP_UUID_SDP, "SDP" }, + { SDP_UUID_UDP, "UDP" }, + { SDP_UUID_RFCOMM, "RFCOMM" }, + { SDP_UUID_TCP, "TCP" }, + { SDP_UUID_TCS_BIN, "TCS-BIN" }, + { SDP_UUID_TCS_AT, "TCS-AT" }, + { SDP_UUID_OBEX, "OBEX" }, + { SDP_UUID_IP, "IP" }, + { SDP_UUID_FTP, "FTP" }, + { SDP_UUID_HTTP, "HTTP" }, + { SDP_UUID_WSP, "WSP" }, + { SDP_UUID_L2CAP, "L2CAP" }, + { SDP_UUID_BNEP, "BNEP" }, /* PAN */ + { SDP_UUID_HIDP, "HIDP" }, /* HID */ + { SDP_UUID_AVCTP, "AVCTP" }, /* AVCTP */ + { SDP_UUID_AVDTP, "AVDTP" }, /* AVDTP */ + { SDP_UUID_CMTP, "CMTP" }, /* CIP */ + { SDP_UUID_UDI_C_PLANE, "UDI_C-Plane" }, /* UDI */ + { SDP_UUID_SERVICE_DISCOVERY_SERVER, "SDServer" }, + { SDP_UUID_BROWSE_GROUP_DESCRIPTOR, "BrwsGrpDesc" }, + { SDP_UUID_PUBLIC_BROWSE_GROUP, "PubBrwsGrp" }, + { SDP_UUID_SERIAL_PORT, "SP" }, + { SDP_UUID_LAN_ACCESS_PPP, "LAN" }, + { SDP_UUID_DIALUP_NETWORKING, "DUN" }, + { SDP_UUID_IR_MC_SYNC, "IRMCSync" }, + { SDP_UUID_OBEX_OBJECT_PUSH, "OBEXObjPush" }, + { SDP_UUID_OBEX_FILE_TRANSFER, "OBEXObjTrnsf" }, + { SDP_UUID_IR_MC_SYNC_COMMAND, "IRMCSyncCmd" }, + { SDP_UUID_HEADSET, "Headset" }, + { SDP_UUID_CORDLESS_TELEPHONY, "CordlessTel" }, + { SDP_UUID_AUDIO_SOURCE, "AudioSource" }, /* A2DP */ + { SDP_UUID_AUDIO_SINK, "AudioSink" }, /* A2DP */ + { SDP_UUID_AV_REMOTE_TARGET, "AVRemTarget" }, /* AVRCP */ + { SDP_UUID_ADVANCED_AUDIO, "AdvAudio" }, /* A2DP */ + { SDP_UUID_AV_REMOTE, "AVRemote" }, /* AVRCP */ + { SDP_UUID_VIDEO_CONFERENCING, "VideoConf" }, /* VCP */ + { SDP_UUID_INTERCOM, "Intercom" }, + { SDP_UUID_FAX, "Fax" }, + { SDP_UUID_HEADSET_AUDIO_GATEWAY, "Headset AG" }, + { SDP_UUID_WAP, "WAP" }, + { SDP_UUID_WAP_CLIENT, "WAP Client" }, + { SDP_UUID_PANU, "PANU" }, /* PAN */ + { SDP_UUID_NAP, "NAP" }, /* PAN */ + { SDP_UUID_GN, "GN" }, /* PAN */ + { SDP_UUID_DIRECT_PRINTING, "DirectPrint" }, /* BPP */ + { SDP_UUID_REFERENCE_PRINTING, "RefPrint" }, /* BPP */ + { SDP_UUID_IMAGING, "Imaging" }, /* BIP */ + { SDP_UUID_IMAGING_RESPONDER, "ImagingResp" }, /* BIP */ + { SDP_UUID_HANDSFREE, "Handsfree" }, + { SDP_UUID_HANDSFREE_AUDIO_GATEWAY, "Handsfree AG" }, + { SDP_UUID_DIRECT_PRINTING_REF_OBJS, "RefObjsPrint" }, /* BPP */ + { SDP_UUID_REFLECTED_UI, "ReflectedUI" }, /* BPP */ + { SDP_UUID_BASIC_PRINTING, "BasicPrint" }, /* BPP */ + { SDP_UUID_PRINTING_STATUS, "PrintStatus" }, /* BPP */ + { SDP_UUID_HUMAN_INTERFACE_DEVICE, "HID" }, /* HID */ + { SDP_UUID_HARDCOPY_CABLE_REPLACE, "HCRP" }, /* HCRP */ + { SDP_UUID_HCR_PRINT, "HCRPrint" }, /* HCRP */ + { SDP_UUID_HCR_SCAN, "HCRScan" }, /* HCRP */ + { SDP_UUID_COMMON_ISDN_ACCESS, "CIP" }, /* CIP */ + { SDP_UUID_VIDEO_CONFERENCING_GW, "VideoConf GW" }, /* VCP */ + { SDP_UUID_UDI_MT, "UDI MT" }, /* UDI */ + { SDP_UUID_UDI_TA, "UDI TA" }, /* UDI */ + { SDP_UUID_AUDIO_VIDEO, "AudioVideo" }, /* VCP */ + { SDP_UUID_SIM_ACCESS, "SAP" }, /* SAP */ + { SDP_UUID_PHONEBOOK_ACCESS_PCE, "PBAP PCE" }, /* PBAP */ + { SDP_UUID_PHONEBOOK_ACCESS_PSE, "PBAP PSE" }, /* PBAP */ + { SDP_UUID_PHONEBOOK_ACCESS, "PBAP" }, /* PBAP */ + { SDP_UUID_PNP_INFORMATION, "PNPInfo" }, + { SDP_UUID_GENERIC_NETWORKING, "Networking" }, + { SDP_UUID_GENERIC_FILE_TRANSFER, "FileTrnsf" }, + { SDP_UUID_GENERIC_AUDIO, "Audio" }, + { SDP_UUID_GENERIC_TELEPHONY, "Telephony" }, + { SDP_UUID_UPNP_SERVICE, "UPNP" }, /* ESDP */ + { SDP_UUID_UPNP_IP_SERVICE, "UPNP IP" }, /* ESDP */ + { SDP_UUID_ESDP_UPNP_IP_PAN, "UPNP PAN" }, /* ESDP */ + { SDP_UUID_ESDP_UPNP_IP_LAP, "UPNP LAP" }, /* ESDP */ + { SDP_UUID_ESDP_UPNP_L2CAP, "UPNP L2CAP" }, /* ESDP */ + { SDP_UUID_VIDEO_SOURCE, "VideoSource" }, /* VDP */ + { SDP_UUID_VIDEO_SINK, "VideoSink" }, /* VDP */ + { SDP_UUID_VIDEO_DISTRIBUTION, "VideoDist" }, /* VDP */ + { SDP_UUID_APPLE_AGENT, "AppleAgent" }, +}; + +#define SDP_UUID_NAM_LOOKUP_TABLE_SIZE \ + (sizeof(sdp_uuid_nam_lookup_table)/sizeof(sdp_uuid_nam_lookup_table_t)) + +/* AttrID name lookup table */ +typedef struct { + int attr_id; + char* name; +} sdp_attr_id_nam_lookup_table_t; + +static sdp_attr_id_nam_lookup_table_t sdp_attr_id_nam_lookup_table[] = { + { SDP_ATTR_ID_SERVICE_RECORD_HANDLE, "SrvRecHndl" }, + { SDP_ATTR_ID_SERVICE_CLASS_ID_LIST, "SrvClassIDList" }, + { SDP_ATTR_ID_SERVICE_RECORD_STATE, "SrvRecState" }, + { SDP_ATTR_ID_SERVICE_SERVICE_ID, "SrvID" }, + { SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST, "ProtocolDescList" }, + { SDP_ATTR_ID_BROWSE_GROUP_LIST, "BrwGrpList" }, + { SDP_ATTR_ID_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, "LangBaseAttrIDList" }, + { SDP_ATTR_ID_SERVICE_INFO_TIME_TO_LIVE, "SrvInfoTimeToLive" }, + { SDP_ATTR_ID_SERVICE_AVAILABILITY, "SrvAvail" }, + { SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, "BTProfileDescList" }, + { SDP_ATTR_ID_DOCUMENTATION_URL, "DocURL" }, + { SDP_ATTR_ID_CLIENT_EXECUTABLE_URL, "ClientExeURL" }, + { SDP_ATTR_ID_ICON_10, "Icon10" }, + { SDP_ATTR_ID_ICON_URL, "IconURL" }, + { SDP_ATTR_ID_SERVICE_NAME, "SrvName" }, + { SDP_ATTR_ID_SERVICE_DESCRIPTION, "SrvDesc" }, + { SDP_ATTR_ID_PROVIDER_NAME, "ProviderName" }, + { SDP_ATTR_ID_VERSION_NUMBER_LIST, "VersionNumList" }, + { SDP_ATTR_ID_GROUP_ID, "GrpID" }, + { SDP_ATTR_ID_SERVICE_DATABASE_STATE, "SrvDBState" }, + { SDP_ATTR_ID_SERVICE_VERSION, "SrvVersion" }, + { SDP_ATTR_ID_SECURITY_DESCRIPTION, "SecurityDescription"}, /* PAN */ + { SDP_ATTR_ID_SUPPORTED_DATA_STORES_LIST, "SuppDataStoresList" }, /* Synchronization */ + { SDP_ATTR_ID_SUPPORTED_FORMATS_LIST, "SuppFormatsList" }, /* OBEX Object Push */ + { SDP_ATTR_ID_NET_ACCESS_TYPE, "NetAccessType" }, /* PAN */ + { SDP_ATTR_ID_MAX_NET_ACCESS_RATE, "MaxNetAccessRate" }, /* PAN */ + { SDP_ATTR_ID_IPV4_SUBNET, "IPv4Subnet" }, /* PAN */ + { SDP_ATTR_ID_IPV6_SUBNET, "IPv6Subnet" }, /* PAN */ + { SDP_ATTR_ID_SUPPORTED_CAPABILITIES, "SuppCapabilities" }, /* Imaging */ + { SDP_ATTR_ID_SUPPORTED_FEATURES, "SuppFeatures" }, /* Imaging and Hansfree */ + { SDP_ATTR_ID_SUPPORTED_FUNCTIONS, "SuppFunctions" }, /* Imaging */ + { SDP_ATTR_ID_TOTAL_IMAGING_DATA_CAPACITY, "SuppTotalCapacity" }, /* Imaging */ + { SDP_ATTR_ID_SUPPORTED_REPOSITORIES, "SuppRepositories" }, /* PBAP */ +}; + +#define SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE \ + (sizeof(sdp_attr_id_nam_lookup_table)/sizeof(sdp_attr_id_nam_lookup_table_t)) + +char* get_uuid_name(int uuid) +{ + int i; + + for (i = 0; i < SDP_UUID_NAM_LOOKUP_TABLE_SIZE; i++) { + if (sdp_uuid_nam_lookup_table[i].uuid == uuid) + return sdp_uuid_nam_lookup_table[i].name; + } + + return 0; +} + +static inline char* get_attr_id_name(int attr_id) +{ + int i; + + for (i = 0; i < SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE; i++) + if (sdp_attr_id_nam_lookup_table[i].attr_id == attr_id) + return sdp_attr_id_nam_lookup_table[i].name; + return 0; +} + +static inline uint8_t parse_de_hdr(struct frame *frm, int *n) +{ + uint8_t de_hdr = get_u8(frm); + uint8_t de_type = de_hdr >> 3; + uint8_t siz_idx = de_hdr & 0x07; + + /* Get the number of bytes */ + if (sdp_siz_idx_lookup_table[siz_idx].addl_bits) { + switch(sdp_siz_idx_lookup_table[siz_idx].num_bytes) { + case 1: + *n = get_u8(frm); break; + case 2: + *n = get_u16(frm); break; + case 4: + *n = get_u32(frm); break; + case 8: + *n = get_u64(frm); break; + } + } else + *n = sdp_siz_idx_lookup_table[siz_idx].num_bytes; + + return de_type; +} + +static inline void print_int(uint8_t de_type, int level, int n, struct frame *frm, uint16_t *psm, uint8_t *channel) +{ + uint64_t val, val2; + + switch(de_type) { + case SDP_DE_UINT: + printf(" uint"); + break; + case SDP_DE_INT: + printf(" int"); + break; + case SDP_DE_BOOL: + printf(" bool"); + break; + } + + switch(n) { + case 1: /* 8-bit */ + val = get_u8(frm); + if (channel && de_type == SDP_DE_UINT) + if (*channel == 0) + *channel = val; + break; + case 2: /* 16-bit */ + val = get_u16(frm); + if (psm && de_type == SDP_DE_UINT) + if (*psm == 0) + *psm = val; + break; + case 4: /* 32-bit */ + val = get_u32(frm); + break; + case 8: /* 64-bit */ + val = get_u64(frm); + break; + case 16:/* 128-bit */ + get_u128(frm, &val, &val2); + printf(" 0x%jx", val2); + if (val < 0x1000000000000000LL) + printf("0"); + printf("%jx", val); + return; + default: /* syntax error */ + printf(" err"); + frm->ptr += n; + frm->len -= n; + return; + } + + printf(" 0x%jx", val); +} + +static inline void print_uuid(int n, struct frame *frm, uint16_t *psm, uint8_t *channel) +{ + uint32_t uuid = 0; + char* s; + int i; + + switch(n) { + case 2: /* 16-bit UUID */ + uuid = get_u16(frm); + s = "uuid-16"; + break; + case 4: /* 32_bit UUID */ + uuid = get_u32(frm); + s = "uuid-32"; + break; + case 16: /* 128-bit UUID */ + printf(" uuid-128 "); + for (i = 0; i < 16; i++) { + printf("%02x", ((unsigned char *) frm->ptr)[i]); + if (i == 3 || i == 5 || i == 7 || i == 9) + printf("-"); + } + frm->ptr += 16; + frm->len -= 16; + return; + default: /* syntax error */ + printf(" *err*"); + frm->ptr += n; + frm->len -= n; + return; + } + + if (psm && *psm > 0 && *psm != 0xffff) { + set_proto(frm->handle, *psm, 0, uuid); + *psm = 0xffff; + } + + if (channel && *channel > 0 && *channel != 0xff) { + set_proto(frm->handle, *psm, *channel, uuid); + *channel = 0xff; + } + + printf(" %s 0x%04x", s, uuid); + if ((s = get_uuid_name(uuid))) + printf(" (%s)", s); +} + +static inline void print_string(int n, struct frame *frm, const char *name) +{ + int i, hex = 0; + + for (i = 0; i < n; i++) { + if (i == (n - 1) && ((char *) frm->ptr)[i] == '\0') + break; + + if (!isprint(((char *) frm->ptr)[i])) { + hex = 1; + break; + } + } + + printf(" %s", name); + if (hex) { + for (i = 0; i < n; i++) + printf(" %02x", ((unsigned char *) frm->ptr)[i]); + } else { + printf(" \""); + for (i = 0; i < n; i++) + printf("%c", ((char *) frm->ptr)[i]); + printf("\""); + } + + frm->ptr += n; + frm->len -= n; +} + +static inline void print_de(int, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel); + +static inline void print_des(uint8_t de_type, int level, int n, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel) +{ + int len = frm->len; + while (len - frm->len < n && frm->len > 0) + print_de(level, frm, split, psm, channel); +} + +static inline void print_de(int level, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel) +{ + int n = 0; + uint8_t de_type = parse_de_hdr(frm, &n); + + switch (de_type) { + case SDP_DE_NULL: + printf(" null"); + break; + case SDP_DE_UINT: + case SDP_DE_INT: + case SDP_DE_BOOL: + print_int(de_type, level, n, frm, psm, channel); + break; + case SDP_DE_UUID: + if (split) { + /* Split output by uuids. + * Used for printing Protocol Desc List */ + if (*split) { + printf("\n"); + p_indent(level, NULL); + } + ++*split; + } + print_uuid(n, frm, psm, channel); + break; + case SDP_DE_URL: + case SDP_DE_STRING: + print_string(n, frm, de_type == SDP_DE_URL? "url": "str"); + break; + case SDP_DE_SEQ: + printf(" <"); + print_des(de_type, level, n, frm, split, psm, channel); + printf(" >"); + break; + case SDP_DE_ALT: + printf(" ["); + print_des(de_type, level, n, frm, split, psm, channel); + printf(" ]"); + break; + } +} + +static inline void print_srv_srch_pat(int level, struct frame *frm) +{ + int len, n1 = 0, n2 = 0; + + p_indent(level, frm); + printf("pat"); + + if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) { + len = frm->len; + while (len - frm->len < n1 && frm->len > 0) { + if (parse_de_hdr(frm, &n2) == SDP_DE_UUID) { + print_uuid(n2, frm, NULL, NULL); + } else { + printf("\nERROR: Unexpected syntax (UUID)\n"); + raw_dump(level, frm); + } + } + printf("\n"); + } else { + printf("\nERROR: Unexpected syntax (SEQ)\n"); + raw_dump(level, frm); + } +} + +static inline void print_attr_id_list(int level, struct frame *frm) +{ + uint16_t attr_id; + uint32_t attr_id_range; + int len, n1 = 0, n2 = 0; + + p_indent(level, frm); + printf("aid(s)"); + + if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) { + len = frm->len; + while (len - frm->len < n1 && frm->len > 0) { + /* Print AttributeID */ + if (parse_de_hdr(frm, &n2) == SDP_DE_UINT) { + char *name; + switch(n2) { + case 2: + attr_id = get_u16(frm); + name = get_attr_id_name(attr_id); + if (!name) + name = "unknown"; + printf(" 0x%04x (%s)", attr_id, name); + break; + case 4: + attr_id_range = get_u32(frm); + printf(" 0x%04x - 0x%04x", + (attr_id_range >> 16), + (attr_id_range & 0xFFFF)); + break; + } + } else { + printf("\nERROR: Unexpected syntax\n"); + raw_dump(level, frm); + } + } + printf("\n"); + } else { + printf("\nERROR: Unexpected syntax\n"); + raw_dump(level, frm); + } +} + +static inline void print_attr_list(int level, struct frame *frm) +{ + uint16_t attr_id, psm; + uint8_t channel; + int len, split, n1 = 0, n2 = 0; + + if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) { + len = frm->len; + while (len - frm->len < n1 && frm->len > 0) { + /* Print AttributeID */ + if (parse_de_hdr(frm, &n2) == SDP_DE_UINT && n2 == sizeof(attr_id)) { + char *name; + attr_id = get_u16(frm); + p_indent(level, 0); + name = get_attr_id_name(attr_id); + if (!name) + name = "unknown"; + printf("aid 0x%04x (%s)\n", attr_id, name); + split = (attr_id != SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST); + psm = 0; + channel = 0; + + /* Print AttributeValue */ + p_indent(level + 1, 0); + print_de(level + 1, frm, split ? NULL: &split, + attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &psm : NULL, + attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &channel : NULL); + printf("\n"); + } else { + printf("\nERROR: Unexpected syntax\n"); + raw_dump(level, frm); + break; + } + } + } else { + printf("\nERROR: Unexpected syntax\n"); + raw_dump(level, frm); + } +} + +static inline void print_attr_lists(int level, struct frame *frm) +{ + int n = 0, cnt = 0; + int count = frm->len; + + if (parse_de_hdr(frm, &n) == SDP_DE_SEQ) { + while (count - frm->len < n && frm->len > 0) { + p_indent(level, 0); + printf("record #%d\n", cnt++); + print_attr_list(level + 2, frm); + } + } else { + printf("\nERROR: Unexpected syntax\n"); + raw_dump(level, frm); + } +} + +static inline void print_cont_state(int level, unsigned char *buf) +{ + uint8_t cont = buf[0]; + int i; + + p_indent(level, 0); + printf("cont"); + for (i = 0; i < cont + 1; i++) + printf(" %2.2X", buf[i]); + printf("\n"); +} + +static char *pid2str(uint8_t pid) +{ + switch (pid) { + case SDP_ERROR_RSP: + return "Error Rsp"; + case SDP_SERVICE_SEARCH_REQ: + return "SS Req"; + case SDP_SERVICE_SEARCH_RSP: + return "SS Rsp"; + case SDP_SERVICE_ATTR_REQ: + return "SA Req"; + case SDP_SERVICE_ATTR_RSP: + return "SA Rsp"; + case SDP_SERVICE_SEARCH_ATTR_REQ: + return "SSA Req"; + case SDP_SERVICE_SEARCH_ATTR_RSP: + return "SSA Rsp"; + default: + return "Unknown"; + } +} + +#define FRAME_TABLE_SIZE 10 + +static struct frame frame_table[FRAME_TABLE_SIZE]; + +static int frame_add(struct frame *frm, int count) +{ + register struct frame *fr; + register unsigned char *data; + register int i, len = 0, pos = -1; + + for (i = 0; i < FRAME_TABLE_SIZE; i++) { + if (frame_table[i].handle == frm->handle && + frame_table[i].cid == frm->cid) { + pos = i; + len = frame_table[i].data_len; + break; + } + if (pos < 0 && !frame_table[i].handle) + pos = i; + } + + if (pos < 0 || count <= 0) + return -EIO; + + data = malloc(len + count); + if (!data) + return -ENOMEM; + + fr = &frame_table[pos]; + + if (len > 0) { + memcpy(data, fr->data, len); + memcpy(data + len, frm->ptr, count); + } else + memcpy(data, frm->ptr, count); + + if (fr->data) + free(fr->data); + + fr->data = data; + fr->data_len = len + count; + fr->len = fr->data_len; + fr->ptr = fr->data; + fr->dev_id = frm->dev_id; + fr->in = frm->in; + fr->ts = frm->ts; + fr->handle = frm->handle; + fr->cid = frm->cid; + fr->num = frm->num; + fr->channel = frm->channel; + fr->pppdump_fd = frm->pppdump_fd; + fr->audio_fd = frm->audio_fd; + + return pos; +} + +static struct frame *frame_get(struct frame *frm, int count) +{ + register int pos; + + pos = frame_add(frm, count); + if (pos < 0) + return frm; + + frame_table[pos].handle = 0; + + return &frame_table[pos]; +} + +void sdp_dump(int level, struct frame *frm) +{ + sdp_pdu_hdr *hdr = frm->ptr; + uint16_t tid = ntohs(hdr->tid); + uint16_t len = ntohs(hdr->len); + uint16_t total, count; + uint8_t cont; + + frm->ptr += SDP_PDU_HDR_SIZE; + frm->len -= SDP_PDU_HDR_SIZE; + + p_indent(level, frm); + printf("SDP %s: tid 0x%x len 0x%x\n", pid2str(hdr->pid), tid, len); + + switch (hdr->pid) { + case SDP_ERROR_RSP: + p_indent(level + 1, frm); + printf("code 0x%x info ", get_u16(frm)); + if (frm->len > 0) + hex_dump(0, frm, frm->len); + else + printf("none\n"); + break; + + case SDP_SERVICE_SEARCH_REQ: + /* Parse ServiceSearchPattern */ + print_srv_srch_pat(level + 1, frm); + + /* Parse MaximumServiceRecordCount */ + p_indent(level + 1, frm); + printf("max %d\n", get_u16(frm)); + + /* Parse ContinuationState */ + print_cont_state(level + 1, frm->ptr); + break; + + case SDP_SERVICE_SEARCH_RSP: + /* Parse TotalServiceRecordCount */ + total = get_u16(frm); + + /* Parse CurrentServiceRecordCount */ + count = get_u16(frm); + p_indent(level + 1, frm); + if (count < total) + printf("count %d of %d\n", count, total); + else + printf("count %d\n", count); + + /* Parse service record handle(s) */ + if (count > 0) { + int i; + p_indent(level + 1, frm); + printf("handle%s", count > 1 ? "s" : ""); + for (i = 0; i < count; i++) + printf(" 0x%x", get_u32(frm)); + printf("\n"); + } + + /* Parse ContinuationState */ + print_cont_state(level + 1, frm->ptr); + break; + + case SDP_SERVICE_ATTR_REQ: + /* Parse ServiceRecordHandle */ + p_indent(level + 1, frm); + printf("handle 0x%x\n", get_u32(frm)); + + /* Parse MaximumAttributeByteCount */ + p_indent(level + 1, frm); + printf("max %d\n", get_u16(frm)); + + /* Parse ServiceSearchPattern */ + print_attr_id_list(level + 1, frm); + + /* Parse ContinuationState */ + print_cont_state(level + 1, frm->ptr); + break; + + case SDP_SERVICE_ATTR_RSP: + /* Parse AttributeByteCount */ + count = get_u16(frm); + p_indent(level + 1, frm); + printf("count %d\n", count); + + /* Parse ContinuationState */ + cont = *(unsigned char *)(frm->ptr + count); + + if (cont == 0) { + /* Parse AttributeList */ + print_attr_list(level + 1, frame_get(frm, count)); + } else + frame_add(frm, count); + + print_cont_state(level + 1, frm->ptr + count); + break; + + case SDP_SERVICE_SEARCH_ATTR_REQ: + /* Parse ServiceSearchPattern */ + print_srv_srch_pat(level + 1, frm); + + /* Parse MaximumAttributeByteCount */ + p_indent(level + 1, frm); + printf("max %d\n", get_u16(frm)); + + /* Parse AttributeList */ + print_attr_id_list(level + 1, frm); + + /* Parse ContinuationState */ + print_cont_state(level + 1, frm->ptr); + break; + + case SDP_SERVICE_SEARCH_ATTR_RSP: + /* Parse AttributeByteCount */ + count = get_u16(frm); + p_indent(level + 1, frm); + printf("count %d\n", count); + + /* Parse ContinuationState */ + cont = *(unsigned char *)(frm->ptr + count); + + if (cont == 0) { + /* Parse AttributeLists */ + print_attr_lists(level + 1, frame_get(frm, count)); + } else + frame_add(frm, count); + + print_cont_state(level + 1, frm->ptr + count); + break; + + default: + raw_dump(level + 1, frm); + break; + } +} diff --git a/parser/sdp.h b/parser/sdp.h new file mode 100644 index 0000000..914bdb6 --- /dev/null +++ b/parser/sdp.h @@ -0,0 +1,162 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Ricky Yuen + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __SDP_H +#define __SDP_H + +/* Bluetooth assigned UUIDs for protocols */ +#define SDP_UUID_SDP 0x0001 +#define SDP_UUID_UDP 0x0002 +#define SDP_UUID_RFCOMM 0x0003 +#define SDP_UUID_TCP 0x0004 +#define SDP_UUID_TCS_BIN 0x0005 +#define SDP_UUID_TCS_AT 0x0006 +#define SDP_UUID_OBEX 0x0008 +#define SDP_UUID_IP 0x0009 +#define SDP_UUID_FTP 0x000A +#define SDP_UUID_HTTP 0x000C +#define SDP_UUID_WSP 0x000E +#define SDP_UUID_BNEP 0x000F /* PAN */ +#define SDP_UUID_HIDP 0x0011 /* HID */ +#define SDP_UUID_HARDCOPY_CONTROL_CHANNEL 0x0012 /* HCRP */ +#define SDP_UUID_HARDCOPY_DATA_CHANNEL 0x0014 /* HCRP */ +#define SDP_UUID_HARDCOPY_NOTIFICATION 0x0016 /* HCRP */ +#define SDP_UUID_AVCTP 0x0017 /* AVCTP */ +#define SDP_UUID_AVDTP 0x0019 /* AVDTP */ +#define SDP_UUID_CMTP 0x001B /* CIP */ +#define SDP_UUID_UDI_C_PLANE 0x001D /* UDI */ +#define SDP_UUID_L2CAP 0x0100 + +/* Bluetooth assigned UUIDs for Service Classes */ +#define SDP_UUID_SERVICE_DISCOVERY_SERVER 0x1000 +#define SDP_UUID_BROWSE_GROUP_DESCRIPTOR 0x1001 +#define SDP_UUID_PUBLIC_BROWSE_GROUP 0x1002 +#define SDP_UUID_SERIAL_PORT 0x1101 +#define SDP_UUID_LAN_ACCESS_PPP 0x1102 +#define SDP_UUID_DIALUP_NETWORKING 0x1103 +#define SDP_UUID_IR_MC_SYNC 0x1104 +#define SDP_UUID_OBEX_OBJECT_PUSH 0x1105 +#define SDP_UUID_OBEX_FILE_TRANSFER 0x1106 +#define SDP_UUID_IR_MC_SYNC_COMMAND 0x1107 +#define SDP_UUID_HEADSET 0x1108 +#define SDP_UUID_CORDLESS_TELEPHONY 0x1109 +#define SDP_UUID_AUDIO_SOURCE 0x110a /* A2DP */ +#define SDP_UUID_AUDIO_SINK 0x110b /* A2DP */ +#define SDP_UUID_AV_REMOTE_TARGET 0x110c /* AVRCP */ +#define SDP_UUID_ADVANCED_AUDIO 0x110d /* A2DP */ +#define SDP_UUID_AV_REMOTE 0x110e /* AVRCP */ +#define SDP_UUID_VIDEO_CONFERENCING 0x110f /* VCP */ +#define SDP_UUID_INTERCOM 0x1110 +#define SDP_UUID_FAX 0x1111 +#define SDP_UUID_HEADSET_AUDIO_GATEWAY 0x1112 +#define SDP_UUID_WAP 0x1113 +#define SDP_UUID_WAP_CLIENT 0x1114 +#define SDP_UUID_PANU 0x1115 /* PAN */ +#define SDP_UUID_NAP 0x1116 /* PAN */ +#define SDP_UUID_GN 0x1117 /* PAN */ +#define SDP_UUID_DIRECT_PRINTING 0x1118 /* BPP */ +#define SDP_UUID_REFERENCE_PRINTING 0x1119 /* BPP */ +#define SDP_UUID_IMAGING 0x111a /* BIP */ +#define SDP_UUID_IMAGING_RESPONDER 0x111b /* BIP */ +#define SDP_UUID_IMAGING_AUTOMATIC_ARCHIVE 0x111c /* BIP */ +#define SDP_UUID_IMAGING_REFERENCED_OBJECTS 0x111d /* BIP */ +#define SDP_UUID_HANDSFREE 0x111e +#define SDP_UUID_HANDSFREE_AUDIO_GATEWAY 0x111f +#define SDP_UUID_DIRECT_PRINTING_REF_OBJS 0x1120 /* BPP */ +#define SDP_UUID_DIRECT_PRINTING_REFERENCE_OBJECTS 0x1120 /* BPP */ +#define SDP_UUID_REFLECTED_UI 0x1121 /* BPP */ +#define SDP_UUID_BASIC_PRINTING 0x1122 /* BPP */ +#define SDP_UUID_PRINTING_STATUS 0x1123 /* BPP */ +#define SDP_UUID_HUMAN_INTERFACE_DEVICE 0x1124 /* HID */ +#define SDP_UUID_HARDCOPY_CABLE_REPLACE 0x1125 /* HCRP */ +#define SDP_UUID_HCR_PRINT 0x1126 /* HCRP */ +#define SDP_UUID_HCR_SCAN 0x1127 /* HCRP */ +#define SDP_UUID_COMMON_ISDN_ACCESS 0x1128 /* CIP */ +#define SDP_UUID_VIDEO_CONFERENCING_GW 0x1129 /* VCP */ +#define SDP_UUID_UDI_MT 0x112a /* UDI */ +#define SDP_UUID_UDI_TA 0x112b /* UDI */ +#define SDP_UUID_AUDIO_VIDEO 0x112c /* VCP */ +#define SDP_UUID_SIM_ACCESS 0x112d /* SAP */ +#define SDP_UUID_PHONEBOOK_ACCESS_PCE 0x112e /* PBAP */ +#define SDP_UUID_PHONEBOOK_ACCESS_PSE 0x112f /* PBAP */ +#define SDP_UUID_PHONEBOOK_ACCESS 0x1130 /* PBAP */ +#define SDP_UUID_PNP_INFORMATION 0x1200 +#define SDP_UUID_GENERIC_NETWORKING 0x1201 +#define SDP_UUID_GENERIC_FILE_TRANSFER 0x1202 +#define SDP_UUID_GENERIC_AUDIO 0x1203 +#define SDP_UUID_GENERIC_TELEPHONY 0x1204 +#define SDP_UUID_UPNP_SERVICE 0x1205 /* ESDP */ +#define SDP_UUID_UPNP_IP_SERVICE 0x1206 /* ESDP */ +#define SDP_UUID_ESDP_UPNP_IP_PAN 0x1300 /* ESDP */ +#define SDP_UUID_ESDP_UPNP_IP_LAP 0x1301 /* ESDP */ +#define SDP_UUID_ESDP_UPNP_L2CAP 0x1302 /* ESDP */ +#define SDP_UUID_VIDEO_SOURCE 0x1303 /* VDP */ +#define SDP_UUID_VIDEO_SINK 0x1304 /* VDP */ +#define SDP_UUID_VIDEO_DISTRIBUTION 0x1305 /* VDP */ +#define SDP_UUID_APPLE_AGENT 0x2112 + +/* Bluetooth assigned numbers for Attribute IDs */ +#define SDP_ATTR_ID_SERVICE_RECORD_HANDLE 0x0000 +#define SDP_ATTR_ID_SERVICE_CLASS_ID_LIST 0x0001 +#define SDP_ATTR_ID_SERVICE_RECORD_STATE 0x0002 +#define SDP_ATTR_ID_SERVICE_SERVICE_ID 0x0003 +#define SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST 0x0004 +#define SDP_ATTR_ID_BROWSE_GROUP_LIST 0x0005 +#define SDP_ATTR_ID_LANGUAGE_BASE_ATTRIBUTE_ID_LIST 0x0006 +#define SDP_ATTR_ID_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define SDP_ATTR_ID_SERVICE_AVAILABILITY 0x0008 +#define SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST 0x0009 +#define SDP_ATTR_ID_DOCUMENTATION_URL 0x000A +#define SDP_ATTR_ID_CLIENT_EXECUTABLE_URL 0x000B +#define SDP_ATTR_ID_ICON_10 0x000C +#define SDP_ATTR_ID_ICON_URL 0x000D +#define SDP_ATTR_ID_SERVICE_NAME 0x0100 +#define SDP_ATTR_ID_SERVICE_DESCRIPTION 0x0101 +#define SDP_ATTR_ID_PROVIDER_NAME 0x0102 +#define SDP_ATTR_ID_VERSION_NUMBER_LIST 0x0200 +#define SDP_ATTR_ID_GROUP_ID 0x0200 +#define SDP_ATTR_ID_SERVICE_DATABASE_STATE 0x0201 +#define SDP_ATTR_ID_SERVICE_VERSION 0x0300 + +#define SDP_ATTR_ID_EXTERNAL_NETWORK 0x0301 /* Cordless Telephony */ +#define SDP_ATTR_ID_SUPPORTED_DATA_STORES_LIST 0x0301 /* Synchronization */ +#define SDP_ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 /* GAP */ +#define SDP_ATTR_ID_SUPPORTED_FORMATS_LIST 0x0303 /* OBEX Object Push */ +#define SDP_ATTR_ID_FAX_CLASS_1_SUPPORT 0x0302 /* Fax */ +#define SDP_ATTR_ID_FAX_CLASS_2_0_SUPPORT 0x0303 +#define SDP_ATTR_ID_FAX_CLASS_2_SUPPORT 0x0304 +#define SDP_ATTR_ID_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define SDP_ATTR_ID_SECURITY_DESCRIPTION 0x030a /* PAN */ +#define SDP_ATTR_ID_NET_ACCESS_TYPE 0x030b /* PAN */ +#define SDP_ATTR_ID_MAX_NET_ACCESS_RATE 0x030c /* PAN */ +#define SDP_ATTR_ID_IPV4_SUBNET 0x030d /* PAN */ +#define SDP_ATTR_ID_IPV6_SUBNET 0x030e /* PAN */ + +#define SDP_ATTR_ID_SUPPORTED_CAPABILITIES 0x0310 /* Imaging */ +#define SDP_ATTR_ID_SUPPORTED_FEATURES 0x0311 /* Imaging and Hansfree */ +#define SDP_ATTR_ID_SUPPORTED_FUNCTIONS 0x0312 /* Imaging */ +#define SDP_ATTR_ID_TOTAL_IMAGING_DATA_CAPACITY 0x0313 /* Imaging */ +#define SDP_ATTR_ID_SUPPORTED_REPOSITORIES 0x0314 /* PBAP */ + +#endif /* __SDP_H */ diff --git a/parser/tcpip.c b/parser/tcpip.c new file mode 100644 index 0000000..7e29b3f --- /dev/null +++ b/parser/tcpip.c @@ -0,0 +1,149 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAS_INET6 +#include +#endif + +#include +#include +#include + +#include "parser.h" + +void arp_dump(int level, struct frame *frm) +{ +#if 0 + int i; + char buf[20]; + struct sockaddr_in sai; + struct ether_arp *arp = (struct ether_arp *) frm->ptr; + + printf("Src "); + for (i = 0; i < 5; i++) + printf("%02x:", arp->arp_sha[i]); + printf("%02x", arp->arp_sha[5]); + sai.sin_family = AF_INET; + memcpy(&sai.sin_addr, &arp->arp_spa, sizeof(sai.sin_addr)); + getnameinfo((struct sockaddr *) &sai, sizeof(sai), buf, sizeof(buf), + NULL, 0, NI_NUMERICHOST); + printf("(%s) ", buf); + printf("Tgt "); + for (i = 0; i < 5; i++) + printf("%02x:", arp->arp_tha[i]); + printf("%02x", arp->arp_tha[5]); + memcpy(&sai.sin_addr, &arp->arp_tpa, sizeof(sai.sin_addr)); + getnameinfo((struct sockaddr *) &sai, sizeof(sai), buf, sizeof(buf), + NULL, 0, NI_NUMERICHOST); + printf("(%s)\n", buf); + frm->ptr += sizeof(struct ether_arp); + frm->len -= sizeof(struct ether_arp); +#endif + raw_dump(level, frm); // not needed. + +} + +void ip_dump(int level, struct frame *frm) +{ +#if 0 + char src[50], dst[50]; + struct ip *ip = (struct ip *) (frm->ptr); + uint8_t proto; + int len; + + if (ip->ip_v == 4) { + struct sockaddr_in sai; + proto = ip->ip_p; + len = ip->ip_hl << 2; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + memcpy(&sai.sin_addr, &ip->ip_src, sizeof(struct in_addr)); + getnameinfo((struct sockaddr *) &sai, sizeof(sai), + src, sizeof(src), NULL, 0, NI_NUMERICHOST); + memcpy(&sai.sin_addr, &ip->ip_dst, sizeof(struct in_addr)); + getnameinfo((struct sockaddr *) &sai, sizeof(sai), + dst, sizeof(dst), NULL, 0, NI_NUMERICHOST); + } else if (ip->ip_v == 6) { + struct sockaddr_in6 sai6; + struct ip6_hdr *ip6 = (struct ip6_hdr *) ip; + proto = ip6->ip6_nxt; + len = sizeof(struct ip6_hdr); + memset(&sai6, 0, sizeof(sai6)); + sai6.sin6_family = AF_INET6; + memcpy(&sai6.sin6_addr, &ip6->ip6_src, sizeof(struct in6_addr)); + getnameinfo((struct sockaddr *) &sai6, sizeof(sai6), + src, sizeof(src), NULL, 0, NI_NUMERICHOST); + memcpy(&sai6.sin6_addr, &ip6->ip6_dst, sizeof(struct in6_addr)); + getnameinfo((struct sockaddr *) &sai6, sizeof(sai6), + dst, sizeof(dst), NULL, 0, NI_NUMERICHOST); + } else { + raw_dump(level, frm); + return; + } + + printf("src %s ", src); + printf("dst %s\n", dst); + + frm->ptr += len; + frm->len -= len; + p_indent(++level, frm); + + switch (proto) { + case IPPROTO_TCP: + printf("TCP:\n"); + break; + + case IPPROTO_UDP: + printf("UDP:\n"); + break; + + case IPPROTO_ICMP: + printf("ICMP:\n"); + break; + + case IPPROTO_ICMPV6: + printf("ICMPv6:\n"); + break; + + default: + printf("Unknown Protocol: 0x%02x\n", ip->ip_p); + break; + } +#endif + raw_dump(level, frm); +} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c66a1aa --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,16 @@ + +sbin_PROGRAMS = hcidump + +noinst_PROGRAMS = bpasniff csrsniff + +LDADD = @BLUEZ_LIBS@ $(top_builddir)/parser/libparser.a + +man_MANS = hcidump.8 + +AM_CFLAGS = @BLUEZ_CFLAGS@ + +INCLUDES = -I$(top_srcdir) + +EXTRA_DIST = $(man_MANS) magic.btsnoop + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/bpasniff.c b/src/bpasniff.c new file mode 100644 index 0000000..43f6d51 --- /dev/null +++ b/src/bpasniff.c @@ -0,0 +1,476 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "parser/parser.h" + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ + __io_canceled = 1; +} + +static int read_revision(int dd, char *revision, int size) +{ + struct hci_request rq; + unsigned char req[] = { 0x07 }; + unsigned char buf[46]; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x000e; + rq.cparam = req; + rq.clen = sizeof(req); + rq.rparam = &buf; + rq.rlen = sizeof(buf); + + if (hci_send_req(dd, &rq, 1000) < 0) + return -1; + + if (buf[0] > 0) { + errno = EIO; + return -1; + } + + if (revision) + strncpy(revision, (char *) (buf + 1), size); + + return 0; +} + +static int enable_sniffer(int dd, uint8_t enable) +{ + struct hci_request rq; + unsigned char req[] = { 0x00, enable }; + unsigned char buf[1]; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x000e; + rq.cparam = req; + rq.clen = sizeof(req); + rq.rparam = &buf; + rq.rlen = sizeof(buf); + + if (hci_send_req(dd, &rq, 1000) < 0) + return -1; + + if (buf[0] > 0) { + errno = EIO; + return -1; + } + + return 0; +} + +static int enable_sync(int dd, uint8_t enable, bdaddr_t *bdaddr) +{ + struct hci_request rq; + unsigned char req[] = { 0x01, enable, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfa, 0x00 }; + + memcpy(req + 2, bdaddr, 6); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x000e; + rq.cparam = req; + rq.clen = sizeof(req); + + hci_send_req(dd, &rq, 1000); + + return 0; +} + +static char *type2str(uint8_t type) +{ + switch (type) { + case 0x00: + return "NULL"; + case 0x01: + return "POLL"; + case 0x02: + return "FHS"; + case 0x03: + return "DM1"; + case 0x04: + return "DH1"; + case 0x05: + return "HV1"; + case 0x06: + return "HV2"; + case 0x07: + return "HV3"; + case 0x08: + return "DV"; + case 0x09: + return "AUX1"; + case 0x0a: + return "DM3"; + case 0x0b: + return "DH3"; + case 0x0c: + return "EV4"; + case 0x0d: + return "EV5"; + case 0x0e: + return "DM5"; + case 0x0f: + return "DH5"; + case 0xff: + return "ID"; + default: + return "UNK"; + } +} + +static void decode(unsigned char *buf, int count) +{ + struct frame frm; + uint8_t id, status, channel; + uint16_t num, len; + uint32_t time; + uint8_t type, addr, temp, hdr; + uint8_t flow, arqn, seqn, hec, llid, pflow; + uint16_t plen; + + if (count < 7) + return; + + id = buf[0]; + num = ntohs(bt_get_unaligned((uint16_t *) (buf + 1))); + len = btohs(bt_get_unaligned((uint16_t *) (buf + 3))); + + status = buf[5]; + time = ntohl(bt_get_unaligned((uint32_t *) (buf + 6))); + channel = buf[10]; + + if (len < 8) + return; + + type = (len < 7) ? 0xff : bt_get_unaligned((uint8_t *) (buf + 11)); + + if (type < 2) + return; + + p_indent(-1, NULL); + + memset(&frm, 0, sizeof(frm)); + frm.data = buf + 12; + frm.data_len = count - 12; + frm.ptr = frm.data; + frm.len = frm.data_len; + frm.in = 0; + frm.master = 0; + frm.handle = 0; + frm.flags = 0; + + p_indent(0, &frm); + + printf("BPA: id %d num %d status 0x%02x time %d channel %2d len %d\n", + id, num, status, time, channel, len - 6); + + if (type < 3) { + printf(" %s\n", type2str(type)); + raw_dump(1, &frm); + return; + } + + addr = bt_get_unaligned((uint8_t *) (buf + 12)); + temp = bt_get_unaligned((uint8_t *) (buf + 13)); + flow = (temp & 0x04) >> 2; + arqn = (temp & 0x02) >> 1; + seqn = (temp & 0x01); + hec = bt_get_unaligned((uint8_t *) (buf + 14)); + + hdr = bt_get_unaligned((uint8_t *) (buf + 20)); + plen = ((hdr & 0x10) >> 4) | ((hdr & 0x08) >> 2) | (hdr & 0x04) | ((hdr & 0x02) << 2) | ((hdr & 0x01) << 4); + pflow = ((hdr & 0x20) >> 5); + llid = ((hdr & 0x80) >> 7) | ((hdr & 0x40) >> 5); + hdr = bt_get_unaligned((uint8_t *) (buf + 21)); + plen = plen | ((hdr & 0x80) >> 2) | (hdr & 0x40) | ((hdr & 0x20) << 2) | ((hdr & 0x08) << 4); + + p_indent(0, &frm); + + printf("%s: addr 0x%02x flow %d arqn %d seqn %d hec 0x%02x llid %d pflow %d plen %d\n", + type2str(type), addr, flow, arqn, seqn, hec, llid, pflow, plen); + + if (type == 0x03 && llid == 3) { + memset(&frm, 0, sizeof(frm)); + frm.data = buf + 22; + frm.data_len = plen; + frm.ptr = frm.data; + frm.len = frm.data_len; + frm.in = 0; + frm.master = 1; + frm.handle = 0; + frm.flags = llid; + + lmp_dump(1, &frm); + return; + } + + raw_dump(1, &frm); +} + +static void process_frames(int dev) +{ + struct sigaction sa; + struct hci_filter flt; + unsigned char *buf; + int dd, size = 2048; + + buf = malloc(size); + if (!buf) { + fprintf(stderr, "Can't allocate buffer for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + return; + } + + dd = hci_open_dev(dev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + dev, strerror(errno), errno); + free(buf); + return; + } + + hci_filter_clear(&flt); + hci_filter_set_ptype(HCI_VENDOR_PKT, &flt); + hci_filter_set_ptype(HCI_EVENT_PKT, &flt); + hci_filter_set_event(EVT_VENDOR, &flt); + + if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + fprintf(stderr, "Can't set filter for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + free(buf); + return; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + while (!__io_canceled) { + int len; + + len = read(dd, buf, size); + if (len < 0) + break; + if (len < 2) + continue; + + if (buf[0] == 0x04 && buf[1] == 0xff) { + if (buf[3] == 0x02) { + switch (buf[4]) { + case 0x00: + printf("Waiting for synchronization...\n"); + break; + case 0x08: + printf("Synchronization lost\n"); + __io_canceled = 1; + break; + default: + printf("Unknown event 0x%02x\n", buf[4]); + break; + } + } + } + + if (buf[0] != 0xff) + continue; + + decode(buf + 1, len - 1); + } + + hci_close_dev(dd); + + free(buf); +} + +static void usage(void) +{ + printf("bpasniff - Utility for the BPA 100/105 sniffers\n\n"); + printf("Usage:\n" + "\tbpasniff [-i ] \n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'i' }, + { 0, 0, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + struct hci_dev_info di; + struct hci_version ver; + char rev[46]; + bdaddr_t bdaddr; + int dd, opt, dev = 0; + + bacpy(&bdaddr, BDADDR_ANY); + + while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { + switch (opt) { + case 'i': + dev = hci_devid(optarg); + if (dev < 0) { + perror("Invalid device"); + exit(1); + } + break; + + case 'h': + default: + usage(); + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + str2ba(argv[0], &bdaddr); + + dd = hci_open_dev(dev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + dev, strerror(errno), errno); + exit(1); + } + + if (hci_devinfo(dev, &di) < 0) { + fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (ver.manufacturer != 12) { + fprintf(stderr, "Can't find sniffer at hci%d: %s (%d)\n", + dev, strerror(ENOSYS), ENOSYS); + hci_close_dev(dd); + exit(1); + } + + if (read_revision(dd, rev, sizeof(rev)) < 0) { + fprintf(stderr, "Can't read revision info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + printf("%s\n", rev); + + if (enable_sniffer(dd, 0x01) < 0) { + fprintf(stderr, "Can't enable sniffer for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (enable_sync(dd, 0x01, &bdaddr) < 0) { + fprintf(stderr, "Can't enable sync for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + enable_sniffer(dd, 0x00); + hci_close_dev(dd); + exit(1); + } + + init_parser(DUMP_EXT | DUMP_VERBOSE, ~0L, 0, DEFAULT_COMPID, -1, -1); + + process_frames(dev); + + if (enable_sync(dd, 0x00, &bdaddr) < 0) { + fprintf(stderr, "Can't disable sync for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + enable_sniffer(dd, 0x00); + hci_close_dev(dd); + exit(1); + } + + if (enable_sniffer(dd, 0x00) < 0) { + fprintf(stderr, "Can't disable sniffer for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + hci_close_dev(dd); + + return 0; +} diff --git a/src/csrsniff.c b/src/csrsniff.c new file mode 100644 index 0000000..c32ca87 --- /dev/null +++ b/src/csrsniff.c @@ -0,0 +1,282 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ + __io_canceled = 1; +} + +static struct { + uint16_t id; + uint16_t ver; + char *date; +} firmware_map[] = { + { 195, 1, "2001-11-27" }, + { 220, 2, "2002-01-03" }, + { 269, 3, "2002-02-22" }, + { 270, 4, "2002-02-26" }, + { 284, 5, "2002-03-12" }, + { 292, 6, "2002-03-20" }, + { 305, 7, "2002-04-12" }, + { 306, 8, "2002-04-12" }, + { 343, 9, "2002-05-02" }, + { 346, 10, "2002-05-03" }, + { 355, 11, "2002-05-16" }, + { 256, 11, "2002-05-16" }, + { 390, 12, "2002-06-26" }, + { 450, 13, "2002-08-16" }, + { 451, 13, "2002-08-16" }, + { 533, 14, "2002-10-11" }, + { 580, 15, "2002-11-14" }, + { 623, 16, "2002-12-12" }, + { 678, 17, "2003-01-29" }, + { 847, 18, "2003-04-17" }, + { 876, 19, "2003-06-10" }, + { 997, 22, "2003-09-05" }, + { 1027, 23, "2003-10-03" }, + { 1029, 24, "2003-10-03" }, + { 1112, 25, "2003-12-03" }, + { 1113, 25, "2003-12-03" }, + { 1133, 26, "2003-12-18" }, + { 1134, 26, "2003-12-18" }, + { 1223, 27, "2004-03-08" }, + { 1224, 27, "2004-03-08" }, + { 1319, 31, "2004-04-22" }, + { 1320, 31, "2004-04-22" }, + { 1427, 34, "2004-06-16" }, + { 1508, 35, "2004-07-19" }, + { 1509, 35, "2004-07-19" }, + { 1587, 36, "2004-08-18" }, + { 1588, 36, "2004-08-18" }, + { 1641, 37, "2004-09-16" }, + { 1642, 37, "2004-09-16" }, + { 1699, 38, "2004-10-07" }, + { 1700, 38, "2004-10-07" }, + { 1752, 39, "2004-11-02" }, + { 1753, 39, "2004-11-02" }, + { 1759, 40, "2004-11-03" }, + { 1760, 40, "2004-11-03" }, + { 1761, 40, "2004-11-03" }, + { 2009, 41, "2005-04-06" }, + { 2010, 41, "2005-04-06" }, + { 2011, 41, "2005-04-06" }, + { 2016, 42, "2005-04-11" }, + { 2017, 42, "2005-04-11" }, + { 2018, 42, "2005-04-11" }, + { 2023, 43, "2005-04-14" }, + { 2024, 43, "2005-04-14" }, + { 2025, 43, "2005-04-14" }, + { 2032, 44, "2005-04-18" }, + { 2033, 44, "2005-04-18" }, + { 2034, 44, "2005-04-18" }, + { 2288, 45, "2005-07-08" }, + { 2289, 45, "2005-07-08" }, + { 2290, 45, "2005-07-08" }, + { 2388, 46, "2005-08-17" }, + { 2389, 46, "2005-08-17" }, + { 2390, 46, "2005-08-17" }, + { 2869, 47, "2006-02-15" }, + { 2870, 47, "2006-02-15" }, + { 2871, 47, "2006-02-15" }, + { 3214, 48, "2006-02-16" }, + { 3215, 48, "2006-02-16" }, + { 3216, 48, "2006-02-16" }, + { 0, } +}; + +static int id2ver(uint16_t id) +{ + int i; + + for (i = 0; firmware_map[i].id; i++) + if (firmware_map[i].id == id) + return firmware_map[i].ver; + + return -1; +} + +static void usage(void) +{ + printf("csrsniff - Utility for the CSR BlueCore sniffers\n\n"); + printf("Usage:\n" + "\tcsrsniff [-i ] [slave-bdaddr]\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'i' }, + { 0, 0, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + struct sigaction sa; + struct hci_dev_info di; + struct hci_version ver; + struct hci_filter flt; + bdaddr_t bdaddr, master, slave; + int need_raw; + int dd, opt, dev = 0; + + bacpy(&slave, BDADDR_ANY); + + while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { + switch (opt) { + case 'i': + dev = hci_devid(optarg); + if (dev < 0) { + perror("Invalid device"); + exit(1); + } + break; + + case 'h': + default: + usage(); + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + str2ba(argv[0], &master); + + if (argc > 1) + str2ba(argv[1], &slave); + + dd = hci_open_dev(dev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + dev, strerror(errno), errno); + exit(1); + } + + if (hci_devinfo(dev, &di) < 0) { + fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (ver.manufacturer != 10 || id2ver(ver.hci_rev) < 0) { + fprintf(stderr, "Can't find sniffer at hci%d: %s (%d)\n", + dev, strerror(ENOSYS), ENOSYS); + hci_close_dev(dd); + exit(1); + } + + if (!bacmp(&di.bdaddr, BDADDR_ANY)) { + if (hci_read_bd_addr(dd, &bdaddr, 1000) < 0) { + fprintf(stderr, "Can't read address for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + } else + bacpy(&bdaddr, &di.bdaddr); + + need_raw = !hci_test_bit(HCI_RAW, &di.flags); + + hci_filter_clear(&flt); + hci_filter_set_ptype(HCI_ACLDATA_PKT, &flt); + hci_filter_set_ptype(HCI_EVENT_PKT, &flt); + hci_filter_set_event(EVT_VENDOR, &flt); + + if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + fprintf(stderr, "Can't set filter for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + if (need_raw) { + if (ioctl(dd, HCISETRAW, 1) < 0) { + fprintf(stderr, "Can't set raw mode on hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + } + + printf("CSR sniffer - Bluetooth packet analyzer ver %s\n", VERSION); + + if (need_raw) { + if (ioctl(dd, HCISETRAW, 0) < 0) + fprintf(stderr, "Can't clear raw mode on hci%d: %s (%d)\n", + dev, strerror(errno), errno); + } + + hci_close_dev(dd); + + return 0; +} diff --git a/src/hcidump.8 b/src/hcidump.8 new file mode 100644 index 0000000..0bd4ec4 --- /dev/null +++ b/src/hcidump.8 @@ -0,0 +1,148 @@ +.TH HCIDUMP 8 "Nov 12 2002" BlueZ "Linux System Administration" +.SH NAME +hcidump \- Parse HCI data +.SH SYNOPSIS +.B hcidump [-h] +.br +.B hcidump [option [option...]] [filter] + +.SH DESCRIPTION +.LP +.B +hcidump +reads raw HCI data coming from and going to a Bluetooth device (which can be +specified with the option +.BR -i , +default is the first available one) and prints to screen commands, events and +data in a human-readable form. Optionally, the dump can be written to a file +rather than parsed, and the dump file can be parsed in a subsequent moment. +.SH OPTIONS +.TP +.BI -h +Prints usage info and exits +.TP +.BI -i " " +Data is read from +.IR hciX , +which must be the name of an installed Bluetooth device. If not specified, +and if +.B +-r +option is not set, data is read from the first available Bluetooth device. +.TP +.BI -l " " "\fR,\fP \-\^\-snap-len=" "" +Sets max length of processed packets to +.IR len . +.TP +.BI -p " " "\fR,\fP \-\^\-psm=" "" +Sets default Protocol Service Multiplexer to +.IR psm . +.TP +.BI -m " " "\fR,\fP \-\^\-manufacturer=" "" +Sets default company id for manufacturer to +.IR compid . +.TP +.BI -w " " "\fR,\fP \-\^\-save-dump=" "" +Parse output is not printed to screen, instead data read from device is saved in file +.IR file . +The saved dump file can be subsequently parsed with option +.BR -r . +.TP +.BI -r " " "\fR,\fP \-\^\-read-dump=" "" +Data is not read from a Bluetooth device, but from file +.IR file . +.I +file +is created with option +.BR -w . +.TP +.BI -s " " "\fR,\fP \-\^\-send-dump=" "" +Parse output is not printed to screen, instead data read from device is sent to host +.IR host . +.TP +.BI -n " " "\fR,\fP \-\^\-recv-dump=" "" +Data is not read from a Bluetooth device, but from host +.IR host . +.TP +.BI -d " " "\fR,\fP \-\^\-wait-dump=" "" +Data is read from a Bluetooth device, but then send to +.IR host +for processing. No data is read if no host is connected. +.TP +.BR -t ", " "\-\^\-timestamp" +Prepend a time stamp to every packet. +.TP +.BR -a ", " "\-\^\-ascii" +For every packet, not only is the packet type displayed, but also all data in ASCII. +.TP +.BR -x ", " "\-\^\-hex" +For every packet, not only is the packet type displayed, but also all data in hex. +.TP +.BR -X ", " "\-\^\-ext" +For every packet, not only is the packet type displayed, but also all data in hex and ASCII. +.TP +.BR -R ", " "\-\^\-raw" +For every packet, only the raw data is displayed. +.TP +.BR -C ", " "\-\^\-cmtp=" "" +Sets the PSM value for the CAPI Message Transport Protocol. +.TP +.BR -H ", " "\-\^\-hcrp=" "" +Sets the PSM value for the Hardcopy Control Channel. +.TP +.BR -O ", " "\-\^\-obex=" "" +Sets the RFCOMM channel value for the Object Exchange Protocol. +.TP +.BR -P ", " "\-\^\-ppp=" "" +Sets the RFCOMM channel value for the Point-to-Point Protocol. +.TP +.BR -D ", " "\-\^\-pppdump=" "" +Extract PPP traffic with pppdump format. +.TP +.BR -A ", " "\-\^\-audio=" "" +Extract SCO audio data. +.TP +.BR -B ", " "\-\^\-btsnoop" +Use the BTSnoop file format. +.TP +.BR -V ", " "\-\^\-verbose" +Enables a more verbose decoding of every packet. +.TP +.BR -Y ", " "\-\^\-novendor" +Don't display any vendor commands or events and don't show any pin code or link key in plain text. +.TP +.BR -N ", " "\-\^\-noappend" +No appending to existing files. Always create new files. +.TP +.BR -4 ", " "\-\^\-ipv4" +Use IPv4 when sending information over the network +.TP +.BR -6 ", " "\-\^\-ipv6" +Use IPv6 when sending information over the network +.SH FILTERS +.B +filter +is a space-separated list of packet categories: available categories are +.IR lmp , +.IR hci , +.IR sco , +.IR l2cap , +.IR rfcomm , +.IR sdp , +.IR bnep , +.IR cmtp , +.IR hidp , +.IR hcrp , +.IR avdtp , +.IR avctp , +.IR obex , +.IR capi +and +.IR ppp . +If filters are used, only packets belonging to the specified categories are +dumped. By default, all packets are dumped. +.SH AUTHORS +Written by Maxim Krasnyansky +and Marcel Holtmann +.PP +man page by Fabrizio Gennari diff --git a/src/hcidump.c b/src/hcidump.c new file mode 100644 index 0000000..9a2429d --- /dev/null +++ b/src/hcidump.c @@ -0,0 +1,1178 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2002 Maxim Krasnyansky + * Copyright (C) 2003-2007 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "parser/parser.h" +#include "parser/sdp.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static inline uint64_t ntoh64(uint64_t n) +{ + uint64_t h; + uint64_t tmp = ntohl(n & 0x00000000ffffffff); + h = ntohl(n >> 32); + h |= tmp << 32; + return h; +} +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ntoh64(x) (x) +#else +#error "Unknown byte order" +#endif +#define hton64(x) ntoh64(x) + +#define SNAP_LEN HCI_MAX_FRAME_SIZE +#define DEFAULT_PORT "10839"; + +/* Modes */ +enum { + PARSE, + READ, + WRITE, + RECEIVE, + SEND, + SERVER, + PPPDUMP, + AUDIO +}; + +/* Default options */ +static int snap_len = SNAP_LEN; +static int mode = PARSE; +static int permcheck = 1; +static int noappend = 0; +static char *dump_file = NULL; +static char *pppdump_file = NULL; +static char *audio_file = NULL; +static char *dump_addr; +static char *dump_port = DEFAULT_PORT; +static int af = AF_UNSPEC; + +struct hcidump_hdr { + uint16_t len; + uint8_t in; + uint8_t pad; + uint32_t ts_sec; + uint32_t ts_usec; +} __attribute__ ((packed)); +#define HCIDUMP_HDR_SIZE (sizeof(struct hcidump_hdr)) + +struct btsnoop_hdr { + uint8_t id[8]; /* Identification Pattern */ + uint32_t version; /* Version Number = 1 */ + uint32_t type; /* Datalink Type */ +} __attribute__ ((packed)); +#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr)) + +struct btsnoop_pkt { + uint32_t size; /* Original Length */ + uint32_t len; /* Included Length */ + uint32_t flags; /* Packet Flags */ + uint32_t drops; /* Cumulative Drops */ + uint64_t ts; /* Timestamp microseconds */ + uint8_t data[0]; /* Packet Data */ +} __attribute__ ((packed)); +#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt)) + +static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 }; + +static uint32_t btsnoop_version = 0; +static uint32_t btsnoop_type = 0; + +struct pktlog_hdr { + uint32_t len; + uint64_t ts; + uint8_t type; +} __attribute__ ((packed)); +#define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr)) + +static inline int read_n(int fd, char *buf, int len) +{ + int t = 0, w; + + while (len > 0) { + if ((w = read(fd, buf, len)) < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + if (!w) + return 0; + len -= w; buf += w; t += w; + } + return t; +} + +static inline int write_n(int fd, char *buf, int len) +{ + int t = 0, w; + + while (len > 0) { + if ((w = write(fd, buf, len)) < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + if (!w) + return 0; + len -= w; buf += w; t += w; + } + return t; +} + +static int process_frames(int dev, int sock, int fd, unsigned long flags) +{ + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iv; + struct hcidump_hdr *dh; + struct btsnoop_pkt *dp; + struct frame frm; + struct pollfd fds[2]; + int nfds = 0; + char *buf, *ctrl; + int len, hdr_size = HCIDUMP_HDR_SIZE; + + if (sock < 0) + return -1; + + if (mode == SERVER) + flags |= DUMP_BTSNOOP; + + if (snap_len < SNAP_LEN) + snap_len = SNAP_LEN; + + if (flags & DUMP_BTSNOOP) + hdr_size = BTSNOOP_PKT_SIZE; + + buf = malloc(snap_len + hdr_size); + if (!buf) { + perror("Can't allocate data buffer"); + return -1; + } + + dh = (void *) buf; + dp = (void *) buf; + frm.data = buf + hdr_size; + + ctrl = malloc(100); + if (!ctrl) { + free(buf); + perror("Can't allocate control buffer"); + return -1; + } + + if (dev == HCI_DEV_NONE) + printf("system: "); + else + printf("device: hci%d ", dev); + + printf("snap_len: %d filter: 0x%lx\n", snap_len, parser.filter); + + memset(&msg, 0, sizeof(msg)); + + if (mode == SERVER) { + struct btsnoop_hdr *hdr = (void *) buf; + + btsnoop_version = 1; + btsnoop_type = 1002; + + memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id)); + hdr->version = htonl(btsnoop_version); + hdr->type = htonl(btsnoop_type); + + printf("btsnoop version: %d datalink type: %d\n", + btsnoop_version, btsnoop_type); + + len = write(fd, buf, BTSNOOP_HDR_SIZE); + if (len < 0) { + perror("Can't create dump header"); + return -1; + } + + if (len != BTSNOOP_HDR_SIZE) { + fprintf(stderr, "Header size mismatch\n"); + return -1; + } + + fds[nfds].fd = fd; + fds[nfds].events = POLLIN; + fds[nfds].revents = 0; + nfds++; + } + + fds[nfds].fd = sock; + fds[nfds].events = POLLIN; + fds[nfds].revents = 0; + nfds++; + + while (1) { + int i, n = poll(fds, nfds, -1); + if (n <= 0) + continue; + + for (i = 0; i < nfds; i++) { + if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) { + if (fds[i].fd == sock) + printf("device: disconnected\n"); + else + printf("client: disconnect\n"); + return 0; + } + } + + if (mode == SERVER) { + len = recv(fd, buf, snap_len, MSG_DONTWAIT); + if (len == 0) { + printf("client: disconnect\n"); + return 0; + } + if (len < 0 && errno != EAGAIN && errno != EINTR) { + perror("Connection read failure"); + return -1; + } + } + + iv.iov_base = frm.data; + iv.iov_len = snap_len; + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = ctrl; + msg.msg_controllen = 100; + + len = recvmsg(sock, &msg, MSG_DONTWAIT); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + perror("Receive failed"); + return -1; + } + + /* Process control message */ + frm.data_len = len; + frm.dev_id = dev; + frm.in = 0; + frm.pppdump_fd = parser.pppdump_fd; + frm.audio_fd = parser.audio_fd; + + cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg) { + switch (cmsg->cmsg_type) { + case HCI_CMSG_DIR: + frm.in = *((int *) CMSG_DATA(cmsg)); + break; + case HCI_CMSG_TSTAMP: + frm.ts = *((struct timeval *) CMSG_DATA(cmsg)); + break; + } + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + frm.ptr = frm.data; + frm.len = frm.data_len; + + switch (mode) { + case WRITE: + case SEND: + case SERVER: + /* Save or send dump */ + if (flags & DUMP_BTSNOOP) { + uint64_t ts; + uint8_t pkt_type = ((uint8_t *) frm.data)[0]; + dp->size = htonl(frm.data_len); + dp->len = dp->size; + dp->flags = ntohl(frm.in & 0x01); + dp->drops = 0; + ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec; + dp->ts = hton64(ts + 0x00E03AB44A676000ll); + if (pkt_type == HCI_COMMAND_PKT || + pkt_type == HCI_EVENT_PKT) + dp->flags |= ntohl(0x02); + } else { + dh->len = htobs(frm.data_len); + dh->in = frm.in; + dh->ts_sec = htobl(frm.ts.tv_sec); + dh->ts_usec = htobl(frm.ts.tv_usec); + } + + if (write_n(fd, buf, frm.data_len + hdr_size) < 0) { + perror("Write error"); + return -1; + } + break; + + default: + /* Parse and print */ + parse(&frm); + break; + } + } + + return 0; +} + +static void read_dump(int fd) +{ + struct hcidump_hdr dh; + struct btsnoop_pkt dp; + struct pktlog_hdr ph; + struct frame frm; + uint8_t pkt_type; + int err; + + frm.data = malloc(HCI_MAX_FRAME_SIZE); + if (!frm.data) { + perror("Can't allocate data buffer"); + exit(1); + } + + while (1) { + if (parser.flags & DUMP_PKTLOG) + err = read_n(fd, (void *) &ph, PKTLOG_HDR_SIZE); + else if (parser.flags & DUMP_BTSNOOP) + err = read_n(fd, (void *) &dp, BTSNOOP_PKT_SIZE); + else + err = read_n(fd, (void *) &dh, HCIDUMP_HDR_SIZE); + + if (err < 0) + goto failed; + if (!err) + return; + + if (parser.flags & DUMP_PKTLOG) { + switch (ph.type) { + case 0x00: + ((uint8_t *) frm.data)[0] = HCI_COMMAND_PKT; + frm.in = 0; + break; + case 0x01: + ((uint8_t *) frm.data)[0] = HCI_EVENT_PKT; + frm.in = 1; + break; + case 0x02: + ((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT; + frm.in = 0; + break; + case 0x03: + ((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT; + frm.in = 1; + break; + default: + lseek(fd, ntohl(ph.len) - 9, SEEK_CUR); + continue; + } + + frm.data_len = ntohl(ph.len) - 8; + err = read_n(fd, frm.data + 1, frm.data_len - 1); + } else if (parser.flags & DUMP_BTSNOOP) { + switch (btsnoop_type) { + case 1001: + if (ntohl(dp.flags) & 0x02) { + if (ntohl(dp.flags) & 0x01) + pkt_type = HCI_EVENT_PKT; + else + pkt_type = HCI_COMMAND_PKT; + } else + pkt_type = HCI_ACLDATA_PKT; + + ((uint8_t *) frm.data)[0] = pkt_type; + + frm.data_len = ntohl(dp.len) + 1; + err = read_n(fd, frm.data + 1, frm.data_len - 1); + break; + + case 1002: + frm.data_len = ntohl(dp.len); + err = read_n(fd, frm.data, frm.data_len); + break; + } + } else { + frm.data_len = btohs(dh.len); + err = read_n(fd, frm.data, frm.data_len); + } + + if (err < 0) + goto failed; + if (!err) + return; + + frm.ptr = frm.data; + frm.len = frm.data_len; + + if (parser.flags & DUMP_PKTLOG) { + uint64_t ts; + ts = ntoh64(ph.ts); + frm.ts.tv_sec = ts >> 32; + frm.ts.tv_usec = ts & 0xffffffff; + } else if (parser.flags & DUMP_BTSNOOP) { + uint64_t ts; + frm.in = ntohl(dp.flags) & 0x01; + ts = ntoh64(dp.ts) - 0x00E03AB44A676000ll; + frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll; + frm.ts.tv_usec = ts % 1000000ll; + } else { + frm.in = dh.in; + frm.ts.tv_sec = btohl(dh.ts_sec); + frm.ts.tv_usec = btohl(dh.ts_usec); + } + + parse(&frm); + } + +failed: + perror("Read failed"); + exit(1); +} + +static int open_file(char *file, int mode, unsigned long flags) +{ + unsigned char buf[BTSNOOP_HDR_SIZE]; + struct btsnoop_hdr *hdr = (struct btsnoop_hdr *) buf; + int fd, len, open_flags; + + if (mode == WRITE || mode == PPPDUMP || mode == AUDIO) { + if (noappend || flags & DUMP_BTSNOOP) + open_flags = O_WRONLY | O_CREAT | O_TRUNC; + else + open_flags = O_WRONLY | O_CREAT | O_APPEND; + } else + open_flags = O_RDONLY; + + fd = open(file, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + perror("Can't open dump file"); + exit(1); + } + + if (mode == READ) { + len = read(fd, buf, BTSNOOP_HDR_SIZE); + if (len != BTSNOOP_HDR_SIZE) { + lseek(fd, 0, SEEK_SET); + return fd; + } + + if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) { + parser.flags |= DUMP_BTSNOOP; + + btsnoop_version = ntohl(hdr->version); + btsnoop_type = ntohl(hdr->type); + + printf("btsnoop version: %d datalink type: %d\n", + btsnoop_version, btsnoop_type); + + if (btsnoop_version != 1) { + fprintf(stderr, "Unsupported BTSnoop version\n"); + exit(1); + } + + if (btsnoop_type != 1001 && btsnoop_type != 1002) { + fprintf(stderr, "Unsupported BTSnoop datalink type\n"); + exit(1); + } + } else { + if (buf[0] == 0x00 && buf[1] == 0x00) { + parser.flags |= DUMP_PKTLOG; + printf("packet logger data format\n"); + } + + parser.flags &= ~DUMP_BTSNOOP; + lseek(fd, 0, SEEK_SET); + return fd; + } + } else { + if (flags & DUMP_BTSNOOP) { + btsnoop_version = 1; + btsnoop_type = 1002; + + memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id)); + hdr->version = htonl(btsnoop_version); + hdr->type = htonl(btsnoop_type); + + printf("btsnoop version: %d datalink type: %d\n", + btsnoop_version, btsnoop_type); + + len = write(fd, buf, BTSNOOP_HDR_SIZE); + if (len < 0) { + perror("Can't create dump header"); + exit(1); + } + + if (len != BTSNOOP_HDR_SIZE) { + fprintf(stderr, "Header size mismatch\n"); + exit(1); + } + } + } + + return fd; +} + +static int open_socket(int dev, unsigned long flags) +{ + struct sockaddr_hci addr; + struct hci_filter flt; + struct hci_dev_info di; + int sk, dd, opt; + + if (permcheck && dev != HCI_DEV_NONE) { + dd = hci_open_dev(dev); + if (dd < 0) { + perror("Can't open device"); + return -1; + } + + if (hci_devinfo(dev, &di) < 0) { + perror("Can't get device info"); + return -1; + } + + opt = hci_test_bit(HCI_RAW, &di.flags); + if (ioctl(dd, HCISETRAW, opt) < 0) { + if (errno == EACCES) { + perror("Can't access device"); + return -1; + } + } + + hci_close_dev(dd); + } + + /* Create HCI socket */ + sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (sk < 0) { + perror("Can't create raw socket"); + return -1; + } + + opt = 1; + if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) { + perror("Can't enable data direction info"); + return -1; + } + + opt = 1; + if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) { + perror("Can't enable time stamp"); + return -1; + } + + /* Setup filter */ + hci_filter_clear(&flt); + hci_filter_all_ptypes(&flt); + hci_filter_all_events(&flt); + if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + perror("Can't set filter"); + return -1; + } + + /* Bind socket to the HCI device */ + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = dev; + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + printf("Can't attach to device hci%d. %s(%d)\n", + dev, strerror(errno), errno); + return -1; + } + + return sk; +} + +static int open_connection(char *addr, char *port) +{ + struct sockaddr_storage ss; + struct addrinfo hints, *res0, *res; + int sk = -1, opt = 1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(addr, port, &hints, &res0)) + if(getaddrinfo(NULL, port, &hints, &res0)) { + perror("getaddrinfo"); + exit(1); + } + + for (res = res0; res; res = res->ai_next) { + sk = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sk < 0) { + if (res->ai_next) + continue; + + perror("Can't create socket"); + freeaddrinfo(res0); + exit(1); + } + + setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + memcpy(&ss, res->ai_addr, res->ai_addrlen); + + switch(ss.ss_family) { + case AF_INET: + ((struct sockaddr_in *) &ss)->sin_addr.s_addr = htonl(INADDR_ANY); + ((struct sockaddr_in *) &ss)->sin_port = 0; + break; +#ifdef HAS_INET6 + case AF_INET6: + memcpy(&((struct sockaddr_in6 *) &ss)->sin6_addr, + &in6addr_any, sizeof(in6addr_any)); + ((struct sockaddr_in6 *) &ss)->sin6_port = 0; + break; +#endif + } + if (bind(sk, (struct sockaddr *) &ss, sizeof(ss)) < 0) { + perror("Can't bind socket"); + close(sk); + freeaddrinfo(res0); + exit(1); + } + + if (connect(sk, res->ai_addr, res->ai_addrlen) < 0) { + perror("Can't connect socket"); + close(sk); + freeaddrinfo(res0); + exit(1); + } + } + + freeaddrinfo(res0); + + return sk; +} + +static int create_datagram(unsigned short port) +{ + struct sockaddr_in addr; + int sk, opt = 1; + + sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sk < 0) + return -1; + + if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + close(sk); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return -1; + } + + return sk; +} + +static unsigned char ping_data[] = { 'p', 'i', 'n', 'g' }; +static unsigned char pong_data[] = { 'p', 'o', 'n', 'g' }; + +static void handle_datagram(int sk) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + unsigned char buf[64]; + ssize_t len; + + len = recvfrom(sk, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &addr, &addr_len); + + if (len != sizeof(ping_data)) + return; + + if (memcmp(buf, ping_data, sizeof(ping_data)) != 0) + return; + + len = sendto(sk, pong_data, sizeof(pong_data), 0, + (struct sockaddr *) &addr, sizeof(addr)); +} + +static int wait_connection(char *addr, char *port) +{ + char hname[100], hport[10]; + struct addrinfo *ai, *runp; + struct addrinfo hints; + struct pollfd fds[3]; + int err, opt, datagram, nfds = 0; + + memset(&hints, 0, sizeof (hints)); + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + err = getaddrinfo(dump_addr, dump_port, &hints, &ai); + if (err < 0) { + printf("Can't get address info: %s\n", gai_strerror(err)); + return -1; + } + + runp = ai; + + datagram = create_datagram(atoi(dump_port)); + if (datagram < 0) { + printf("server: no discover protocol\n"); + } else { + fds[nfds].fd = datagram; + fds[nfds].events = POLLIN; + nfds++; + } + + while (runp != NULL && nfds < sizeof(fds) / sizeof(fds[0])) { + fds[nfds].fd = socket(runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (fds[nfds].fd < 0) { + perror("Can't create socket"); + return -1; + } + + fds[nfds].events = POLLIN; + + opt = 1; + setsockopt(fds[nfds].fd, SOL_SOCKET, SO_REUSEADDR, + &opt, sizeof(opt)); + + opt = 0; + setsockopt(fds[nfds].fd, SOL_SOCKET, SO_KEEPALIVE, + &opt, sizeof(opt)); + + if (bind(fds[nfds].fd, runp->ai_addr, runp->ai_addrlen) < 0) { + if (errno != EADDRINUSE) { + perror("Can't bind socket"); + return -1; + } + + close(fds[nfds].fd); + } else { + if (listen(fds[nfds].fd, SOMAXCONN) < 0) { + perror("Can't listen on socket"); + return -1; + } + + getnameinfo(runp->ai_addr, runp->ai_addrlen, + hname, sizeof(hname), + hport, sizeof(hport), + NI_NUMERICSERV); + + printf("server: %s:%s snap_len: %d filter: 0x%lx\n", + hname, hport, snap_len, parser.filter); + + nfds++; + } + + runp = runp->ai_next; + } + + freeaddrinfo(ai); + + while (1) { + int i, n = poll(fds, nfds, -1); + if (n <= 0) + continue; + + for (i = 0; i < nfds; i++) { + struct sockaddr_storage rem; + socklen_t remlen = sizeof(rem); + int sk; + + if (!(fds[i].revents & POLLIN)) + continue; + + if (fds[i].fd == datagram) { + handle_datagram(datagram); + continue; + } + + sk = accept(fds[i].fd, (struct sockaddr *) &rem, &remlen); + if (sk < 0) + continue; + + getnameinfo((struct sockaddr *) &rem, remlen, + hname, sizeof(hname), + hport, sizeof(hport), + NI_NUMERICSERV); + + printf("client: %s:%s snap_len: %d filter: 0x%lx\n", + hname, hport, snap_len, parser.filter); + + for (n = 0; n < nfds; n++) + close(fds[n].fd); + + return sk; + } + } + + return -1; +} + +static int run_server(int dev, char *addr, char *port, unsigned long flags) +{ + while (1) { + int dd, sk; + + sk = wait_connection(addr, port); + if (sk < 0) + continue; + + //fcntl(sk, F_SETFL, O_NONBLOCK); + + dd = open_socket(dev, flags); + if (dd < 0) { + close(sk); + continue; + } + + process_frames(dev, dd, sk, flags); + + close(dd); + close(sk); + } + + return 0; +} + +static struct { + char *name; + int flag; +} filters[] = { + { "lmp", FILT_LMP }, + { "hci", FILT_HCI }, + { "sco", FILT_SCO }, + { "l2cap", FILT_L2CAP }, + { "rfcomm", FILT_RFCOMM }, + { "sdp", FILT_SDP }, + { "bnep", FILT_BNEP }, + { "cmtp", FILT_CMTP }, + { "hidp", FILT_HIDP }, + { "hcrp", FILT_HCRP }, + { "avdtp", FILT_AVDTP }, + { "avctp", FILT_AVCTP }, + { "obex", FILT_OBEX }, + { "capi", FILT_CAPI }, + { "ppp", FILT_PPP }, + { "csr", FILT_CSR }, + { "dga", FILT_DGA }, + { 0 } +}; + +static unsigned long parse_filter(int argc, char **argv) +{ + unsigned long filter = 0; + int i,n; + + for (i = 0; i < argc; i++) { + for (n = 0; filters[n].name; n++) { + if (!strcasecmp(filters[n].name, argv[i])) { + filter |= filters[n].flag; + break; + } + } + } + + return filter; +} + +static void usage(void) +{ + printf( + "Usage: hcidump [OPTION...] [filter]\n" + " -i, --device=hci_dev HCI device\n" + " -l, --snap-len=len Snap len (in bytes)\n" + " -p, --psm=psm Default PSM\n" + " -m, --manufacturer=compid Default manufacturer\n" + " -w, --save-dump=file Save dump to a file\n" + " -r, --read-dump=file Read dump from a file\n" + " -s, --send-dump=host Send dump to a host\n" + " -n, --recv-dump=host Receive dump on a host\n" + " -d, --wait-dump=host Wait on a host and send\n" + " -t, --ts Display time stamps\n" + " -a, --ascii Dump data in ascii\n" + " -x, --hex Dump data in hex\n" + " -X, --ext Dump data in hex and ascii\n" + " -R, --raw Dump raw data\n" + " -C, --cmtp=psm PSM for CMTP\n" + " -H, --hcrp=psm PSM for HCRP\n" + " -O, --obex=channel Channel for OBEX\n" + " -P, --ppp=channel Channel for PPP\n" + " -D, --pppdump=file Extract PPP traffic\n" + " -A, --audio=file Extract SCO audio data\n" + " -B, --btsnoop Use BTSnoop file format\n" + " -V, --verbose Verbose decoding\n" + " -Y, --novendor No vendor commands or events\n" + " -N, --noappend No appending to existing files\n" + " -4, --ipv4 Use IPv4 as transport\n" + " -6 --ipv6 Use IPv6 as transport\n" + " -h, --help Give this help list\n" + " --usage Give a short usage message\n" + ); +} + +static struct option main_options[] = { + { "device", 1, 0, 'i' }, + { "snap-len", 1, 0, 'l' }, + { "psm", 1, 0, 'p' }, + { "manufacturer", 1, 0, 'm' }, + { "save-dump", 1, 0, 'w' }, + { "read-dump", 1, 0, 'r' }, + { "send-dump", 1, 0, 's' }, + { "recv-dump", 1, 0, 'n' }, + { "wait-dump", 1, 0, 'd' }, + { "timestamp", 0, 0, 't' }, + { "ascii", 0, 0, 'a' }, + { "hex", 0, 0, 'x' }, + { "ext", 0, 0, 'X' }, + { "raw", 0, 0, 'R' }, + { "cmtp", 1, 0, 'C' }, + { "hcrp", 1, 0, 'H' }, + { "obex", 1, 0, 'O' }, + { "ppp", 1, 0, 'P' }, + { "pppdump", 1, 0, 'D' }, + { "audio", 1, 0, 'A' }, + { "btsnoop", 0, 0, 'B' }, + { "verbose", 0, 0, 'V' }, + { "novendor", 0, 0, 'Y' }, + { "nopermcheck", 0, 0, 'Z' }, + { "noappend", 0, 0, 'N' }, + { "ipv4", 0, 0, '4' }, + { "ipv6", 0, 0, '6' }, + { "help", 0, 0, 'h' }, + { 0 } +}; + +int main(int argc, char *argv[]) +{ + unsigned long flags = 0; + unsigned long filter = 0; + int device = 0; + int defpsm = 0; + int defcompid = DEFAULT_COMPID; + int opt, pppdump_fd = -1, audio_fd = -1; + + printf("HCI sniffer - Bluetooth packet analyzer ver %s\n", VERSION); + + while ((opt=getopt_long(argc, argv, "i:l:p:m:w:r:s:n:d:taxXRC:H:O:P:D:A:BVYZN46h", main_options, NULL)) != -1) { + switch(opt) { + case 'i': + if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system")) + device = atoi(optarg + 3); + else + device = HCI_DEV_NONE; + break; + + case 'l': + snap_len = atoi(optarg); + break; + + case 'p': + defpsm = atoi(optarg); + break; + + case 'm': + defcompid = atoi(optarg); + break; + + case 'w': + mode = WRITE; + dump_file = strdup(optarg); + break; + + case 'r': + mode = READ; + dump_file = strdup(optarg); + break; + + case 's': + mode = SEND; + dump_addr = optarg; + break; + + case 'n': + mode = RECEIVE; + dump_addr = optarg; + break; + + case 'd': + mode = SERVER; + dump_addr = optarg; + break; + + case 't': + flags |= DUMP_TSTAMP; + break; + + case 'a': + flags |= DUMP_ASCII; + break; + + case 'x': + flags |= DUMP_HEX; + break; + + case 'X': + flags |= DUMP_EXT; + break; + + case 'R': + flags |= DUMP_RAW; + break; + + case 'C': + set_proto(0, atoi(optarg), 0, SDP_UUID_CMTP); + break; + + case 'H': + set_proto(0, atoi(optarg), 0, SDP_UUID_HARDCOPY_CONTROL_CHANNEL); + break; + + case 'O': + set_proto(0, 0, atoi(optarg), SDP_UUID_OBEX); + break; + + case 'P': + set_proto(0, 0, atoi(optarg), SDP_UUID_LAN_ACCESS_PPP); + break; + + case 'D': + pppdump_file = strdup(optarg); + break; + + case 'A': + audio_file = strdup(optarg); + break; + + case 'B': + flags |= DUMP_BTSNOOP; + break; + + case 'V': + flags |= DUMP_VERBOSE; + break; + + case 'Y': + flags |= DUMP_NOVENDOR; + break; + + case 'Z': + permcheck = 0; + break; + + case 'N': + noappend = 1; + break; + + case '4': + af = AF_INET; + break; + + case '6': + af = AF_INET6; + break; + + case 'h': + default: + usage(); + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc > 0) + filter = parse_filter(argc, argv); + + /* Default settings */ + if (!filter) + filter = ~0L; + + if (pppdump_file) + pppdump_fd = open_file(pppdump_file, PPPDUMP, flags); + + if (audio_file) + audio_fd = open_file(audio_file, AUDIO, flags); + + switch (mode) { + case PARSE: + init_parser(flags, filter, defpsm, defcompid, pppdump_fd, audio_fd); + process_frames(device, open_socket(device, flags), -1, flags); + break; + + case READ: + init_parser(flags, filter, defpsm, defcompid, pppdump_fd, audio_fd); + read_dump(open_file(dump_file, mode, flags)); + break; + + case WRITE: + process_frames(device, open_socket(device, flags), + open_file(dump_file, mode, flags), flags); + break; + + case RECEIVE: + init_parser(flags, filter, defpsm, defcompid, pppdump_fd, audio_fd); + read_dump(wait_connection(dump_addr, dump_port)); + break; + + case SEND: + process_frames(device, open_socket(device, flags), + open_connection(dump_addr, dump_port), flags); + break; + + case SERVER: + init_parser(flags, filter, defpsm, defcompid, pppdump_fd, audio_fd); + run_server(device, dump_addr, dump_port, flags); + break; + } + + return 0; +} diff --git a/src/magic.btsnoop b/src/magic.btsnoop new file mode 100644 index 0000000..1dca663 --- /dev/null +++ b/src/magic.btsnoop @@ -0,0 +1,12 @@ + +#------------------------------------------------------------------------------ +# BTSnoop: file(1) magic for BTSnoop files +# +# From +0 string btsnoop\0 BTSnoop +>8 belong x version %d, +>12 belong 1001 Unencapsulated HCI +>12 belong 1002 HCI UART (H4) +>12 belong 1003 HCI BCSP +>12 belong 1004 HCI Serial (H5) +>>12 belong x type %d -- cgit v1.2.3 -- cgit v1.2.3 From 0e2012c382c085cb4df31b47681bd2ddb3e4b1ef Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 9 Feb 2010 17:05:02 -0800 Subject: Print pkt_type when decoding Setup Synchronous Connection command. Change-Id: I23ebb72abd88a74fd951808cc63bd3b0832b2764 --- parser/hci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parser/hci.c b/parser/hci.c index c0ca27e..958cd38 100644 --- a/parser/hci.c +++ b/parser/hci.c @@ -917,8 +917,9 @@ static inline void setup_sync_conn_dump(int level, struct frame *frm) setup_sync_conn_cp *cp = frm->ptr; p_indent(level, frm); - printf("handle %d voice setting 0x%4.4x\n", btohs(cp->handle), - btohs(cp->voice_setting)); + printf("handle %d voice setting 0x%4.4x pkt_type 0x%4.4x\n", + btohs(cp->handle), btohs(cp->voice_setting), + btohs(cp->pkt_type)); } static inline void hold_mode_dump(int level, struct frame *frm) -- cgit v1.2.3 From 55f43e604797356468733a7a333bc835e9ab2332 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 11 Feb 2010 15:26:27 -0800 Subject: Print pkt_type on HCI Accept Synchrnous Connection Request Command. Change-Id: Ifc321b8f4e31407edf4a8e5ee4308cb70f4db328 --- parser/hci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/parser/hci.c b/parser/hci.c index 958cd38..ee4fdb0 100644 --- a/parser/hci.c +++ b/parser/hci.c @@ -922,6 +922,17 @@ static inline void setup_sync_conn_dump(int level, struct frame *frm) btohs(cp->pkt_type)); } +static inline void accept_sync_conn_req_dump(int level, struct frame *frm) +{ + accept_sync_conn_req_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s voice_setting 0x%4.4x pkt_type 0x%4.4x\n", + addr, btohs(cp->voice_setting), btohs(cp->pkt_type)); +} + static inline void hold_mode_dump(int level, struct frame *frm) { hold_mode_cp *cp = frm->ptr; @@ -1385,9 +1396,11 @@ static inline void command_dump(int level, struct frame *frm) return; case OCF_CREATE_CONN_CANCEL: case OCF_REMOTE_NAME_REQ_CANCEL: - case OCF_ACCEPT_SYNC_CONN_REQ: bdaddr_command_dump(level + 1, frm); return; + case OCF_ACCEPT_SYNC_CONN_REQ: + accept_sync_conn_req_dump(level + 1, frm); + return; case OCF_ADD_SCO: case OCF_SET_CONN_PTYPE: add_sco_dump(level + 1, frm); -- cgit v1.2.3 From 2dd13dc20956426dfa43c94d7d04805f536f942b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Mon, 8 Mar 2010 18:04:17 -0800 Subject: Add an empty CleanSpec.mk Change-Id: I9fbe738300b541ccd59590d46f95e76db6ff3dd2 --- CleanSpec.mk | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 CleanSpec.mk diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ -- cgit v1.2.3 From ce36a6c7c5f8043c038415d8e270d55d9834008d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 1 Jun 2010 19:19:39 -0700 Subject: Update include paths for Bluez. Change-Id: I34a875feb37bc9574d95584bd741eaffc6247b66 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 47bb8fa..276b043 100755 --- a/Android.mk +++ b/Android.mk @@ -5,7 +5,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_C_INCLUDES:= \ - $(call include-path-for,bluez)/include/ \ + $(call include-path-for,bluez)/lib/ \ LOCAL_CFLAGS:= \ -DVERSION=\"1.42\" -- cgit v1.2.3 From 9143fb58cd0d2c2a0abb9d91f1f3916f9251ec61 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 29 Mar 2011 11:29:00 -0700 Subject: Make Android specific chnages to hcidump. 1. Add Android.mk file. 2. Stub out ipv6 features and some header file changes. Change-Id: Icc7d33dabade9c3072dc2f2d75f50985c68f1b81 --- Android.mk | 45 +++++++++++++++++++++++++++++++++++++++++++++ parser/bnep.c | 2 +- parser/tcpip.c | 12 ++++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100755 Android.mk diff --git a/Android.mk b/Android.mk new file mode 100755 index 0000000..661ebba --- /dev/null +++ b/Android.mk @@ -0,0 +1,45 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES:= \ + $(call include-path-for,bluez)/lib/ \ + +LOCAL_CFLAGS:= \ + -DVERSION=\"2.0\" + +LOCAL_SRC_FILES:= \ + parser/att.c \ + parser/avctp.c \ + parser/avdtp.c \ + parser/bnep.c \ + parser/bpa.c \ + parser/capi.c \ + parser/cmtp.c \ + parser/csr.c \ + parser/ericsson.c \ + parser/hci.c \ + parser/hcrp.c \ + parser/hidp.c \ + parser/l2cap.c \ + parser/lmp.c \ + parser/obex.c \ + parser/parser.c \ + parser/ppp.c \ + parser/rfcomm.c \ + parser/sdp.c \ + parser/tcpip.c \ + src/hcidump.c + +LOCAL_SHARED_LIBRARIES := \ + libbluetooth \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE:=hcidump + +include $(BUILD_EXECUTABLE) +endif +endif diff --git a/parser/bnep.c b/parser/bnep.c index d9048a9..d368abe 100644 --- a/parser/bnep.c +++ b/parser/bnep.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include "parser.h" diff --git a/parser/tcpip.c b/parser/tcpip.c index 9062008..22b68f9 100644 --- a/parser/tcpip.c +++ b/parser/tcpip.c @@ -32,10 +32,14 @@ #include #include -#include +#include #include #include + +#ifdef HAS_INET6 #include +#endif + #include #include #include @@ -44,6 +48,7 @@ void arp_dump(int level, struct frame *frm) { +#if 0 int i; char buf[20]; struct sockaddr_in sai; @@ -68,11 +73,14 @@ void arp_dump(int level, struct frame *frm) printf("(%s)\n", buf); frm->ptr += sizeof(struct ether_arp); frm->len -= sizeof(struct ether_arp); +#endif raw_dump(level, frm); // not needed. + } void ip_dump(int level, struct frame *frm) { +#if 0 char src[50], dst[50]; struct ip *ip = (struct ip *) (frm->ptr); uint8_t proto; @@ -136,6 +144,6 @@ void ip_dump(int level, struct frame *frm) printf("Unknown Protocol: 0x%02x\n", ip->ip_p); break; } - +#endif raw_dump(level, frm); } -- cgit v1.2.3 From 09798a98432f86c3897e324d910dfdfe720c311d Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 11 Feb 2010 15:26:27 -0800 Subject: Print pkt_type on HCI Accept Synchrnous Connection Request Command. Change-Id: Ifc321b8f4e31407edf4a8e5ee4308cb70f4db328 --- parser/hci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/parser/hci.c b/parser/hci.c index e4a3633..f6ed1d3 100644 --- a/parser/hci.c +++ b/parser/hci.c @@ -1149,6 +1149,17 @@ static inline void create_logical_link_dump(int level, struct frame *frm) printf("\n"); } +static inline void accept_sync_conn_req_dump(int level, struct frame *frm) +{ + accept_sync_conn_req_cp *cp = frm->ptr; + char addr[18]; + + p_indent(level, frm); + p_ba2str(&cp->bdaddr, addr); + printf("bdaddr %s voice_setting 0x%4.4x pkt_type 0x%4.4x\n", + addr, btohs(cp->voice_setting), btohs(cp->pkt_type)); +} + static inline void hold_mode_dump(int level, struct frame *frm) { hold_mode_cp *cp = frm->ptr; @@ -1703,9 +1714,11 @@ static inline void command_dump(int level, struct frame *frm) return; case OCF_CREATE_CONN_CANCEL: case OCF_REMOTE_NAME_REQ_CANCEL: - case OCF_ACCEPT_SYNC_CONN_REQ: bdaddr_command_dump(level + 1, frm); return; + case OCF_ACCEPT_SYNC_CONN_REQ: + accept_sync_conn_req_dump(level + 1, frm); + return; case OCF_ADD_SCO: case OCF_SET_CONN_PTYPE: add_sco_dump(level + 1, frm); -- cgit v1.2.3 From 00555977ebb6e963e9dffd79590143fa04761386 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Mon, 8 Mar 2010 18:04:17 -0800 Subject: Add an empty CleanSpec.mk Change-Id: I9fbe738300b541ccd59590d46f95e76db6ff3dd2 --- CleanSpec.mk | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 CleanSpec.mk diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ -- cgit v1.2.3