OSDN Git Service

Initial Contribution
authorThe Android Open Source Project <initial-contribution@android.com>
Tue, 21 Oct 2008 14:00:00 +0000 (07:00 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Tue, 21 Oct 2008 14:00:00 +0000 (07:00 -0700)
44 files changed:
1  2  3 
Android.mk
libs/Android.mk
libs/NOTICE
libs/include/bluetooth/bluetooth.h
libs/include/bluetooth/bnep.h
libs/include/bluetooth/cmtp.h
libs/include/bluetooth/hci.h
libs/include/bluetooth/hci_lib.h
libs/include/bluetooth/hidp.h
libs/include/bluetooth/l2cap.h
libs/include/bluetooth/rfcomm.h
libs/include/bluetooth/sco.h
libs/include/bluetooth/sdp.h
libs/include/bluetooth/sdp_lib.h
libs/src/hci.c
libs/src/sdp.c
utils/Android.mk
utils/NOTICE
utils/audio/Android.mk
utils/common/Android.mk
utils/common/android_bluez.c
utils/dund/Android.mk
utils/eglib/Android.mk
utils/eglib/gmain.c
utils/gdbus/Android.mk
utils/hcid/Android.mk
utils/hcid/adapter.c
utils/hcid/dbus-hci.c
utils/hcid/dbus-sdp.c
utils/hcid/dbus-security.c
utils/hcid/main.c
utils/pand/Android.mk
utils/rfcomm/Android.mk
utils/sdpd/Android.mk
utils/sdpd/server.c
utils/sdpd/service.c
utils/test/Android.mk
utils/test/l2test.c
utils/test/passkey-agent.c
utils/test/rctest.c
utils/tools/Android.mk
utils/tools/hciattach.c
utils/tools/hciattach_tialt.c
utils/tools/sdptool.c

diff --cc Android.mk
index 0000000,0000000,0000000..02c1bec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,18 @@@@
+++#
+++# Copyright (C) 2008 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.
+++#
+++ifneq ($(TARGET_SIMULATOR),true)
+++  include $(all-subdir-makefiles)
+++endif
diff --cc libs/Android.mk
index 0000000,0000000,0000000..d8189da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,16 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_SRC_FILES:= \
+++     src/bluetooth.c \
+++     src/sdp.c \
+++     src/hci.c 
+++
+++LOCAL_C_INCLUDES+= \
+++      $(LOCAL_PATH)/include
+++
+++LOCAL_MODULE:=libbluetooth
+++
+++LOCAL_CFLAGS+=-O3
+++
+++include $(BUILD_SHARED_LIBRARY)
diff --cc libs/NOTICE
index 0000000,0000000,0000000..71f0363
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,280 @@@@
+++                 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
index d886cb8,0000000,0000000..d886cb8
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index 75eaac0,0000000,0000000..b90b2d5
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
@@@@ -1,150 -1,0 -1,0 +1,151 @@@@
-  #include <net/ethernet.h>
 ++/*
 ++ *
 ++ *  BlueZ - Bluetooth protocol stack for Linux
 ++ *
 ++ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 ++ *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
 ++ *
 ++ *
 ++ *  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 __BNEP_H
 ++#define __BNEP_H
 ++
 ++#ifdef __cplusplus
 ++extern "C" {
 ++#endif
 ++
 ++#include <bluetooth/bluetooth.h>
 ++
+++#define ETH_ALEN 6  /* Octets in one ethernet addr (ipv4) */
+++
 ++/* BNEP UUIDs */
 ++#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
 ++#define BNEP_UUID16    0x02
 ++#define BNEP_UUID32    0x04
 ++#define BNEP_UUID128   0x16
 ++
 ++#define BNEP_SVC_PANU  0x1115
 ++#define BNEP_SVC_NAP   0x1116
 ++#define BNEP_SVC_GN    0x1117
 ++
 ++/* BNEP packet types */
 ++#define BNEP_GENERAL               0x00
 ++#define BNEP_CONTROL               0x01
 ++#define BNEP_COMPRESSED            0x02
 ++#define BNEP_COMPRESSED_SRC_ONLY   0x03
 ++#define BNEP_COMPRESSED_DST_ONLY   0x04
 ++
 ++/* BNEP control types */
 ++#define BNEP_CMD_NOT_UNDERSTOOD    0x00
 ++#define BNEP_SETUP_CONN_REQ        0x01
 ++#define BNEP_SETUP_CONN_RSP        0x02
 ++#define BNEP_FILTER_NET_TYPE_SET   0x03
 ++#define BNEP_FILTER_NET_TYPE_RSP   0x04
 ++#define BNEP_FILTER_MULT_ADDR_SET  0x05
 ++#define BNEP_FILTER_MULT_ADDR_RSP  0x06
 ++
 ++/* BNEP response messages */
 ++#define BNEP_SUCCESS               0x00
 ++
 ++#define BNEP_CONN_INVALID_DST      0x01
 ++#define BNEP_CONN_INVALID_SRC      0x02
 ++#define BNEP_CONN_INVALID_SVC      0x03
 ++#define BNEP_CONN_NOT_ALLOWED      0x04
 ++
 ++#define BNEP_FILTER_UNSUPPORTED_REQ    0x01
 ++#define BNEP_FILTER_INVALID_RANGE      0x02
 ++#define BNEP_FILTER_INVALID_MCADDR     0x02
 ++#define BNEP_FILTER_LIMIT_REACHED      0x03
 ++#define BNEP_FILTER_DENIED_SECURITY    0x04
 ++
 ++/* L2CAP settings */
 ++#define BNEP_MTU         1691
 ++#define BNEP_FLUSH_TO    0xffff
 ++#define BNEP_CONNECT_TO  15
 ++#define BNEP_FILTER_TO   15
 ++
 ++#ifndef BNEP_PSM
 ++#define BNEP_PSM      0x0f
 ++#endif
 ++
 ++/* BNEP headers */
 ++#define BNEP_TYPE_MASK        0x7f
 ++#define BNEP_EXT_HEADER       0x80
 ++
 ++struct bnep_setup_conn_req {
 ++     uint8_t  type;
 ++     uint8_t  ctrl;
 ++     uint8_t  uuid_size;
 ++     uint8_t  service[0];
 ++} __attribute__((packed));
 ++
 ++struct bnep_set_filter_req {
 ++     uint8_t  type;
 ++     uint8_t  ctrl;
 ++     uint16_t len;
 ++     uint8_t  list[0];
 ++} __attribute__((packed));
 ++
 ++struct bnep_control_rsp {
 ++     uint8_t  type;
 ++     uint8_t  ctrl;
 ++     uint16_t resp;
 ++} __attribute__((packed));
 ++
 ++struct bnep_ext_hdr {
 ++     uint8_t  type;
 ++     uint8_t  len;
 ++     uint8_t  data[0];
 ++} __attribute__((packed));
 ++
 ++/* BNEP ioctl defines */
 ++#define BNEPCONNADD  _IOW('B', 200, int)
 ++#define BNEPCONNDEL  _IOW('B', 201, int)
 ++#define BNEPGETCONNLIST      _IOR('B', 210, int)
 ++#define BNEPGETCONNINFO      _IOR('B', 211, int)
 ++
 ++struct bnep_connadd_req {
 ++     int      sock;          /* Connected socket */
 ++     uint32_t flags;
 ++     uint16_t role;
 ++     char     device[16];    /* Name of the Ethernet device */
 ++};
 ++
 ++struct bnep_conndel_req {
 ++     uint32_t flags;
 ++     uint8_t  dst[ETH_ALEN];
 ++};
 ++
 ++struct bnep_conninfo {
 ++     uint32_t flags;
 ++     uint16_t role;
 ++     uint16_t state;
 ++     uint8_t  dst[ETH_ALEN];
 ++     char     device[16];
 ++};
 ++
 ++struct bnep_connlist_req {
 ++     uint32_t cnum;
 ++     struct bnep_conninfo *ci;
 ++};
 ++
 ++#ifdef __cplusplus
 ++}
 ++#endif
 ++
 ++#endif /* __BNEP_H */
index 16add1d,0000000,0000000..16add1d
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index 499f1bd,0000000,0000000..499f1bd
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index ccd155a,0000000,0000000..ccd155a
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index 74da689,0000000,0000000..74da689
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index d54e74d,0000000,0000000..d54e74d
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index 5c4751e,0000000,0000000..5c4751e
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index bce7098,0000000,0000000..bce7098
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
index dd5b0e2,0000000,0000000..96a79d1
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
@@@@ -1,503 -1,0 -1,0 +1,504 @@@@
-  #define SDP_UNIX_PATH "/var/run/sdp"
 ++/*
 ++ *
 ++ *  BlueZ - Bluetooth protocol stack for Linux
 ++ *
 ++ *  Copyright (C) 2001-2002  Nokia Corporation
 ++ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 ++ *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
 ++ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
 ++ *
 ++ *
 ++ *  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
 ++
 ++#ifdef __cplusplus
 ++extern "C" {
 ++#endif
 ++
 ++#include <stdint.h>
+++#include <cutils/sockets.h>
 ++
+++#define SDP_UNIX_PATH ANDROID_SOCKET_DIR"/bluetooth"
 ++#define SDP_RESPONSE_TIMEOUT 20
 ++#define SDP_REQ_BUFFER_SIZE  2048
 ++#define SDP_RSP_BUFFER_SIZE  65535
 ++#define SDP_PDU_CHUNK_SIZE   1024
 ++
 ++/*
 ++ * All definitions are based on Bluetooth Assigned Numbers
 ++ * of the Bluetooth Specification
 ++ */
 ++#define SDP_PSM              0x0001
 ++
 ++/*
 ++ * Protocol UUIDs
 ++ */
 ++#define SDP_UUID     0x0001
 ++#define UDP_UUID     0x0002
 ++#define RFCOMM_UUID  0x0003
 ++#define TCP_UUID     0x0004
 ++#define TCS_BIN_UUID 0x0005
 ++#define TCS_AT_UUID  0x0006
 ++#define OBEX_UUID    0x0008
 ++#define IP_UUID              0x0009
 ++#define FTP_UUID     0x000a
 ++#define HTTP_UUID    0x000c
 ++#define WSP_UUID     0x000e
 ++#define BNEP_UUID    0x000f
 ++#define UPNP_UUID    0x0010
 ++#define HIDP_UUID    0x0011
 ++#define HCRP_CTRL_UUID       0x0012
 ++#define HCRP_DATA_UUID       0x0014
 ++#define HCRP_NOTE_UUID       0x0016
 ++#define AVCTP_UUID   0x0017
 ++#define AVDTP_UUID   0x0019
 ++#define CMTP_UUID    0x001b
 ++#define UDI_UUID     0x001d
 ++#define MCAP_CTRL_UUID       0x001e
 ++#define MCAP_DATA_UUID       0x001f
 ++#define L2CAP_UUID   0x0100
 ++
 ++/*
 ++ * Service class identifiers of standard services and service groups
 ++ */
 ++#define SDP_SERVER_SVCLASS_ID                0x1000
 ++#define BROWSE_GRP_DESC_SVCLASS_ID   0x1001
 ++#define PUBLIC_BROWSE_GROUP          0x1002
 ++#define SERIAL_PORT_SVCLASS_ID               0x1101
 ++#define LAN_ACCESS_SVCLASS_ID                0x1102
 ++#define DIALUP_NET_SVCLASS_ID                0x1103
 ++#define IRMC_SYNC_SVCLASS_ID         0x1104
 ++#define OBEX_OBJPUSH_SVCLASS_ID              0x1105
 ++#define OBEX_FILETRANS_SVCLASS_ID    0x1106
 ++#define IRMC_SYNC_CMD_SVCLASS_ID     0x1107
 ++#define HEADSET_SVCLASS_ID           0x1108
 ++#define CORDLESS_TELEPHONY_SVCLASS_ID        0x1109
 ++#define AUDIO_SOURCE_SVCLASS_ID              0x110a
 ++#define AUDIO_SINK_SVCLASS_ID                0x110b
 ++#define AV_REMOTE_TARGET_SVCLASS_ID  0x110c
 ++#define ADVANCED_AUDIO_SVCLASS_ID    0x110d
 ++#define AV_REMOTE_SVCLASS_ID         0x110e
 ++#define VIDEO_CONF_SVCLASS_ID                0x110f
 ++#define INTERCOM_SVCLASS_ID          0x1110
 ++#define FAX_SVCLASS_ID                       0x1111
 ++#define HEADSET_AGW_SVCLASS_ID               0x1112
 ++#define WAP_SVCLASS_ID                       0x1113
 ++#define WAP_CLIENT_SVCLASS_ID                0x1114
 ++#define PANU_SVCLASS_ID                      0x1115
 ++#define NAP_SVCLASS_ID                       0x1116
 ++#define GN_SVCLASS_ID                        0x1117
 ++#define DIRECT_PRINTING_SVCLASS_ID   0x1118
 ++#define REFERENCE_PRINTING_SVCLASS_ID        0x1119
 ++#define IMAGING_SVCLASS_ID           0x111a
 ++#define IMAGING_RESPONDER_SVCLASS_ID 0x111b
 ++#define IMAGING_ARCHIVE_SVCLASS_ID   0x111c
 ++#define IMAGING_REFOBJS_SVCLASS_ID   0x111d
 ++#define HANDSFREE_SVCLASS_ID         0x111e
 ++#define HANDSFREE_AGW_SVCLASS_ID     0x111f
 ++#define DIRECT_PRT_REFOBJS_SVCLASS_ID        0x1120
 ++#define REFLECTED_UI_SVCLASS_ID              0x1121
 ++#define BASIC_PRINTING_SVCLASS_ID    0x1122
 ++#define PRINTING_STATUS_SVCLASS_ID   0x1123
 ++#define HID_SVCLASS_ID                       0x1124
 ++#define HCR_SVCLASS_ID                       0x1125
 ++#define HCR_PRINT_SVCLASS_ID         0x1126
 ++#define HCR_SCAN_SVCLASS_ID          0x1127
 ++#define CIP_SVCLASS_ID                       0x1128
 ++#define VIDEO_CONF_GW_SVCLASS_ID     0x1129
 ++#define UDI_MT_SVCLASS_ID            0x112a
 ++#define UDI_TA_SVCLASS_ID            0x112b
 ++#define AV_SVCLASS_ID                        0x112c
 ++#define SAP_SVCLASS_ID                       0x112d
 ++#define PBAP_PCE_SVCLASS_ID          0x112e
 ++#define PBAP_PSE_SVCLASS_ID          0x112f
 ++#define PBAP_SVCLASS_ID                      0x1130
 ++#define PNP_INFO_SVCLASS_ID          0x1200
 ++#define GENERIC_NETWORKING_SVCLASS_ID        0x1201
 ++#define GENERIC_FILETRANS_SVCLASS_ID 0x1202
 ++#define GENERIC_AUDIO_SVCLASS_ID     0x1203
 ++#define GENERIC_TELEPHONY_SVCLASS_ID 0x1204
 ++#define UPNP_SVCLASS_ID                      0x1205
 ++#define UPNP_IP_SVCLASS_ID           0x1206
 ++#define UPNP_PAN_SVCLASS_ID          0x1300
 ++#define UPNP_LAP_SVCLASS_ID          0x1301
 ++#define UPNP_L2CAP_SVCLASS_ID                0x1302
 ++#define VIDEO_SOURCE_SVCLASS_ID              0x1303
 ++#define VIDEO_SINK_SVCLASS_ID                0x1304
 ++#define VIDEO_DISTRIBUTION_SVCLASS_ID        0x1305
 ++#define MDP_SVCLASS_ID                       0x1400
 ++#define MDP_SOURCE_SVCLASS_ID                0x1401
 ++#define MDP_SINK_SVCLASS_ID          0x1402
 ++#define APPLE_AGENT_SVCLASS_ID               0x2112
 ++
 ++/*
 ++ * Standard profile descriptor identifiers; note these
 ++ * may be identical to some of the service classes defined above
 ++ */
 ++#define SDP_SERVER_PROFILE_ID                SDP_SERVER_SVCLASS_ID
 ++#define BROWSE_GRP_DESC_PROFILE_ID   BROWSE_GRP_DESC_SVCLASS_ID
 ++#define SERIAL_PORT_PROFILE_ID               SERIAL_PORT_SVCLASS_ID
 ++#define LAN_ACCESS_PROFILE_ID                LAN_ACCESS_SVCLASS_ID
 ++#define DIALUP_NET_PROFILE_ID                DIALUP_NET_SVCLASS_ID
 ++#define IRMC_SYNC_PROFILE_ID         IRMC_SYNC_SVCLASS_ID
 ++#define OBEX_OBJPUSH_PROFILE_ID              OBEX_OBJPUSH_SVCLASS_ID
 ++#define OBEX_FILETRANS_PROFILE_ID    OBEX_FILETRANS_SVCLASS_ID
 ++#define IRMC_SYNC_CMD_PROFILE_ID     IRMC_SYNC_CMD_SVCLASS_ID
 ++#define HEADSET_PROFILE_ID           HEADSET_SVCLASS_ID
 ++#define CORDLESS_TELEPHONY_PROFILE_ID        CORDLESS_TELEPHONY_SVCLASS_ID
 ++#define AUDIO_SOURCE_PROFILE_ID              AUDIO_SOURCE_SVCLASS_ID
 ++#define AUDIO_SINK_PROFILE_ID                AUDIO_SINK_SVCLASS_ID
 ++#define AV_REMOTE_TARGET_PROFILE_ID  AV_REMOTE_TARGET_SVCLASS_ID
 ++#define ADVANCED_AUDIO_PROFILE_ID    ADVANCED_AUDIO_SVCLASS_ID
 ++#define AV_REMOTE_PROFILE_ID         AV_REMOTE_SVCLASS_ID
 ++#define VIDEO_CONF_PROFILE_ID                VIDEO_CONF_SVCLASS_ID
 ++#define INTERCOM_PROFILE_ID          INTERCOM_SVCLASS_ID
 ++#define FAX_PROFILE_ID                       FAX_SVCLASS_ID
 ++#define HEADSET_AGW_PROFILE_ID               HEADSET_AGW_SVCLASS_ID
 ++#define WAP_PROFILE_ID                       WAP_SVCLASS_ID
 ++#define WAP_CLIENT_PROFILE_ID                WAP_CLIENT_SVCLASS_ID
 ++#define PANU_PROFILE_ID                      PANU_SVCLASS_ID
 ++#define NAP_PROFILE_ID                       NAP_SVCLASS_ID
 ++#define GN_PROFILE_ID                        GN_SVCLASS_ID
 ++#define DIRECT_PRINTING_PROFILE_ID   DIRECT_PRINTING_SVCLASS_ID
 ++#define REFERENCE_PRINTING_PROFILE_ID        REFERENCE_PRINTING_SVCLASS_ID
 ++#define IMAGING_PROFILE_ID           IMAGING_SVCLASS_ID
 ++#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID
 ++#define IMAGING_ARCHIVE_PROFILE_ID   IMAGING_ARCHIVE_SVCLASS_ID
 ++#define IMAGING_REFOBJS_PROFILE_ID   IMAGING_REFOBJS_SVCLASS_ID
 ++#define HANDSFREE_PROFILE_ID         HANDSFREE_SVCLASS_ID
 ++#define HANDSFREE_AGW_PROFILE_ID     HANDSFREE_AGW_SVCLASS_ID
 ++#define DIRECT_PRT_REFOBJS_PROFILE_ID        DIRECT_PRT_REFOBJS_SVCLASS_ID
 ++#define REFLECTED_UI_PROFILE_ID              REFLECTED_UI_SVCLASS_ID
 ++#define BASIC_PRINTING_PROFILE_ID    BASIC_PRINTING_SVCLASS_ID
 ++#define PRINTING_STATUS_PROFILE_ID   PRINTING_STATUS_SVCLASS_ID
 ++#define HID_PROFILE_ID                       HID_SVCLASS_ID
 ++#define HCR_PROFILE_ID                       HCR_SCAN_SVCLASS_ID
 ++#define HCR_PRINT_PROFILE_ID         HCR_PRINT_SVCLASS_ID
 ++#define HCR_SCAN_PROFILE_ID          HCR_SCAN_SVCLASS_ID
 ++#define CIP_PROFILE_ID                       CIP_SVCLASS_ID
 ++#define VIDEO_CONF_GW_PROFILE_ID     VIDEO_CONF_GW_SVCLASS_ID
 ++#define UDI_MT_PROFILE_ID            UDI_MT_SVCLASS_ID
 ++#define UDI_TA_PROFILE_ID            UDI_TA_SVCLASS_ID
 ++#define AV_PROFILE_ID                        AV_SVCLASS_ID
 ++#define SAP_PROFILE_ID                       SAP_SVCLASS_ID
 ++#define PBAP_PCE_PROFILE_ID          PBAP_PCE_SVCLASS_ID
 ++#define PBAP_PSE_PROFILE_ID          PBAP_PSE_SVCLASS_ID
 ++#define PBAP_PROFILE_ID                      PBAP_SVCLASS_ID
 ++#define PNP_INFO_PROFILE_ID          PNP_INFO_SVCLASS_ID
 ++#define GENERIC_NETWORKING_PROFILE_ID        GENERIC_NETWORKING_SVCLASS_ID
 ++#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
 ++#define GENERIC_AUDIO_PROFILE_ID     GENERIC_AUDIO_SVCLASS_ID
 ++#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID
 ++#define UPNP_PROFILE_ID                      UPNP_SVCLASS_ID
 ++#define UPNP_IP_PROFILE_ID           UPNP_IP_SVCLASS_ID
 ++#define UPNP_PAN_PROFILE_ID          UPNP_PAN_SVCLASS_ID
 ++#define UPNP_LAP_PROFILE_ID          UPNP_LAP_SVCLASS_ID
 ++#define UPNP_L2CAP_PROFILE_ID                UPNP_L2CAP_SVCLASS_ID
 ++#define VIDEO_SOURCE_PROFILE_ID              VIDEO_SOURCE_SVCLASS_ID
 ++#define VIDEO_SINK_PROFILE_ID                VIDEO_SINK_SVCLASS_ID
 ++#define VIDEO_DISTRIBUTION_PROFILE_ID        VIDEO_DISTRIBUTION_SVCLASS_ID
 ++#define MDP_PROFILE_ID                       MDP_SVCLASS_ID
 ++#define MDP_SOURCE_PROFILE_ID                MDP_SROUCE_SVCLASS_ID
 ++#define MDP_SINK_PROFILE_ID          MDP_SINK_SVCLASS_ID
 ++#define APPLE_AGENT_PROFILE_ID               APPLE_AGENT_SVCLASS_ID
 ++
 ++/*
 ++ * Attribute identifier codes
 ++ */
 ++#define SDP_SERVER_RECORD_HANDLE             0x0000
 ++
 ++/*
 ++ * Possible values for attribute-id are listed below.
 ++ * See SDP Spec, section "Service Attribute Definitions" for more details.
 ++ */
 ++#define SDP_ATTR_RECORD_HANDLE                       0x0000
 ++#define SDP_ATTR_SVCLASS_ID_LIST             0x0001
 ++#define SDP_ATTR_RECORD_STATE                        0x0002
 ++#define SDP_ATTR_SERVICE_ID                  0x0003
 ++#define SDP_ATTR_PROTO_DESC_LIST             0x0004
 ++#define SDP_ATTR_BROWSE_GRP_LIST             0x0005
 ++#define SDP_ATTR_LANG_BASE_ATTR_ID_LIST              0x0006
 ++#define SDP_ATTR_SVCINFO_TTL                 0x0007
 ++#define SDP_ATTR_SERVICE_AVAILABILITY                0x0008
 ++#define SDP_ATTR_PFILE_DESC_LIST             0x0009
 ++#define SDP_ATTR_DOC_URL                     0x000a
 ++#define SDP_ATTR_CLNT_EXEC_URL                       0x000b
 ++#define SDP_ATTR_ICON_URL                    0x000c
 ++#define SDP_ATTR_ADD_PROTO_DESC_LIST         0x000d
 ++
 ++#define SDP_ATTR_GROUP_ID                    0x0200
 ++#define SDP_ATTR_IP_SUBNET                   0x0200
 ++#define SDP_ATTR_VERSION_NUM_LIST            0x0200
 ++#define SDP_ATTR_SVCDB_STATE                 0x0201
 ++
 ++#define SDP_ATTR_SERVICE_VERSION             0x0300
 ++#define SDP_ATTR_EXTERNAL_NETWORK            0x0301
 ++#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST  0x0301
 ++#define SDP_ATTR_FAX_CLASS1_SUPPORT          0x0302
 ++#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302
 ++#define SDP_ATTR_FAX_CLASS20_SUPPORT         0x0303
 ++#define SDP_ATTR_SUPPORTED_FORMATS_LIST              0x0303
 ++#define SDP_ATTR_FAX_CLASS2_SUPPORT          0x0304
 ++#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT              0x0305
 ++#define SDP_ATTR_NETWORK_ADDRESS             0x0306
 ++#define SDP_ATTR_WAP_GATEWAY                 0x0307
 ++#define SDP_ATTR_HOMEPAGE_URL                        0x0308
 ++#define SDP_ATTR_WAP_STACK_TYPE                      0x0309
 ++#define SDP_ATTR_SECURITY_DESC                       0x030a
 ++#define SDP_ATTR_NET_ACCESS_TYPE             0x030b
 ++#define SDP_ATTR_MAX_NET_ACCESSRATE          0x030c
 ++#define SDP_ATTR_IP4_SUBNET                  0x030d
 ++#define SDP_ATTR_IP6_SUBNET                  0x030e
 ++#define SDP_ATTR_SUPPORTED_CAPABILITIES              0x0310
 ++#define SDP_ATTR_SUPPORTED_FEATURES          0x0311
 ++#define SDP_ATTR_SUPPORTED_FUNCTIONS         0x0312
 ++#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313
 ++#define SDP_ATTR_SUPPORTED_REPOSITORIES              0x0314
 ++
 ++#define SDP_ATTR_SPECIFICATION_ID            0x0200
 ++#define SDP_ATTR_VENDOR_ID                   0x0201
 ++#define SDP_ATTR_PRODUCT_ID                  0x0202
 ++#define SDP_ATTR_VERSION                     0x0203
 ++#define SDP_ATTR_PRIMARY_RECORD                      0x0204
 ++#define SDP_ATTR_VENDOR_ID_SOURCE            0x0205
 ++
 ++#define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER   0x0200
 ++#define SDP_ATTR_HID_PARSER_VERSION          0x0201
 ++#define SDP_ATTR_HID_DEVICE_SUBCLASS         0x0202
 ++#define SDP_ATTR_HID_COUNTRY_CODE            0x0203
 ++#define SDP_ATTR_HID_VIRTUAL_CABLE           0x0204
 ++#define SDP_ATTR_HID_RECONNECT_INITIATE              0x0205
 ++#define SDP_ATTR_HID_DESCRIPTOR_LIST         0x0206
 ++#define SDP_ATTR_HID_LANG_ID_BASE_LIST               0x0207
 ++#define SDP_ATTR_HID_SDP_DISABLE             0x0208
 ++#define SDP_ATTR_HID_BATTERY_POWER           0x0209
 ++#define SDP_ATTR_HID_REMOTE_WAKEUP           0x020a
 ++#define SDP_ATTR_HID_PROFILE_VERSION         0x020b
 ++#define SDP_ATTR_HID_SUPERVISION_TIMEOUT     0x020c
 ++#define SDP_ATTR_HID_NORMALLY_CONNECTABLE    0x020d
 ++#define SDP_ATTR_HID_BOOT_DEVICE             0x020e
 ++
 ++/*
 ++ * These identifiers are based on the SDP spec stating that 
 ++ * "base attribute id of the primary (universal) language must be 0x0100"
 ++ *
 ++ * Other languages should have their own offset; e.g.:
 ++ * #define XXXLangBase yyyy
 ++ * #define AttrServiceName_XXX       0x0000+XXXLangBase
 ++ */
 ++#define SDP_PRIMARY_LANG_BASE                0x0100
 ++
 ++#define SDP_ATTR_SVCNAME_PRIMARY     0x0000 + SDP_PRIMARY_LANG_BASE
 ++#define SDP_ATTR_SVCDESC_PRIMARY     0x0001 + SDP_PRIMARY_LANG_BASE
 ++#define SDP_ATTR_PROVNAME_PRIMARY    0x0002 + SDP_PRIMARY_LANG_BASE
 ++
 ++/*
 ++ * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec)
 ++ * These are the exact data type+size descriptor values
 ++ * that go into the PDU buffer.
 ++ *
 ++ * The datatype (leading 5bits) + size descriptor (last 3 bits)
 ++ * is 8 bits. The size descriptor is critical to extract the
 ++ * right number of bytes for the data value from the PDU.
 ++ *
 ++ * For most basic types, the datatype+size descriptor is
 ++ * straightforward. However for constructed types and strings,
 ++ * the size of the data is in the next "n" bytes following the
 ++ * 8 bits (datatype+size) descriptor. Exactly what the "n" is
 ++ * specified in the 3 bits of the data size descriptor.
 ++ *
 ++ * TextString and URLString can be of size 2^{8, 16, 32} bytes
 ++ * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32}
 ++ * The size are computed post-facto in the API and are not known apriori
 ++ */
 ++#define SDP_DATA_NIL                 0x00
 ++#define SDP_UINT8            0x08
 ++#define SDP_UINT16           0x09
 ++#define SDP_UINT32           0x0A
 ++#define SDP_UINT64           0x0B
 ++#define SDP_UINT128          0x0C
 ++#define SDP_INT8             0x10
 ++#define SDP_INT16            0x11
 ++#define SDP_INT32            0x12
 ++#define SDP_INT64            0x13
 ++#define SDP_INT128           0x14
 ++#define SDP_UUID_UNSPEC              0x18
 ++#define SDP_UUID16           0x19
 ++#define SDP_UUID32           0x1A
 ++#define SDP_UUID128          0x1C
 ++#define SDP_TEXT_STR_UNSPEC  0x20
 ++#define SDP_TEXT_STR8                0x25
 ++#define SDP_TEXT_STR16               0x26
 ++#define SDP_TEXT_STR32               0x27
 ++#define SDP_BOOL             0x28
 ++#define SDP_SEQ_UNSPEC               0x30
 ++#define SDP_SEQ8             0x35
 ++#define SDP_SEQ16            0x36
 ++#define SDP_SEQ32            0x37
 ++#define SDP_ALT_UNSPEC               0x38
 ++#define SDP_ALT8             0x3D
 ++#define SDP_ALT16            0x3E
 ++#define SDP_ALT32            0x3F
 ++#define SDP_URL_STR_UNSPEC   0x40
 ++#define SDP_URL_STR8         0x45
 ++#define SDP_URL_STR16                0x46
 ++#define SDP_URL_STR32                0x47
 ++
 ++/*
 ++ * The PDU identifiers of SDP packets between client and server
 ++ */
 ++#define SDP_ERROR_RSP                0x01
 ++#define SDP_SVC_SEARCH_REQ   0x02
 ++#define SDP_SVC_SEARCH_RSP   0x03
 ++#define SDP_SVC_ATTR_REQ     0x04
 ++#define SDP_SVC_ATTR_RSP     0x05
 ++#define SDP_SVC_SEARCH_ATTR_REQ      0x06
 ++#define SDP_SVC_SEARCH_ATTR_RSP      0x07
 ++
 ++/*
 ++ * Some additions to support service registration.
 ++ * These are outside the scope of the Bluetooth specification
 ++ */
 ++#define SDP_SVC_REGISTER_REQ 0x75
 ++#define SDP_SVC_REGISTER_RSP 0x76
 ++#define SDP_SVC_UPDATE_REQ   0x77
 ++#define SDP_SVC_UPDATE_RSP   0x78
 ++#define SDP_SVC_REMOVE_REQ   0x79
 ++#define SDP_SVC_REMOVE_RSP   0x80
 ++
 ++/*
 ++ * SDP Error codes
 ++ */
 ++#define SDP_INVALID_VERSION          0x0001
 ++#define SDP_INVALID_RECORD_HANDLE    0x0002
 ++#define SDP_INVALID_SYNTAX           0x0003
 ++#define SDP_INVALID_PDU_SIZE         0x0004
 ++#define SDP_INVALID_CSTATE           0x0005
 ++
 ++/*
 ++ * SDP PDU
 ++ */
 ++typedef struct {
 ++     uint8_t  pdu_id;
 ++     uint16_t tid;
 ++     uint16_t plen;
 ++} __attribute__ ((packed)) sdp_pdu_hdr_t;
 ++
 ++/*
 ++ * Common definitions for attributes in the SDP.
 ++ * Should the type of any of these change, you need only make a change here.
 ++ */
 ++typedef struct {
 ++     uint8_t data[16];
 ++} uint128_t;
 ++
 ++typedef struct {
 ++     uint8_t type;
 ++     union {
 ++             uint16_t  uuid16;
 ++             uint32_t  uuid32;
 ++             uint128_t uuid128;
 ++     } value;
 ++} uuid_t;
 ++
 ++#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || (x) ==SDP_UUID128)
 ++
 ++typedef struct _sdp_list sdp_list_t;
 ++struct _sdp_list {
 ++     sdp_list_t *next;
 ++     void *data;
 ++};
 ++
 ++/*
 ++ * User-visible strings can be in many languages
 ++ * in addition to the universal language.
 ++ *
 ++ * Language meta-data includes language code in ISO639
 ++ * followed by the encoding format. The third field in this
 ++ * structure is the attribute offset for the language.
 ++ * User-visible strings in the specified language can be
 ++ * obtained at this offset.
 ++ */
 ++typedef struct {
 ++     uint16_t code_ISO639;
 ++     uint16_t encoding;
 ++     uint16_t base_offset;
 ++} sdp_lang_attr_t;
 ++
 ++/*
 ++ * Profile descriptor is the Bluetooth profile metadata. If a
 ++ * service conforms to a well-known profile, then its profile
 ++ * identifier (UUID) is an attribute of the service. In addition,
 ++ * if the profile has a version number it is specified here.
 ++ */
 ++typedef struct {
 ++     uuid_t uuid;
 ++     uint16_t version;
 ++} sdp_profile_desc_t;
 ++
 ++typedef struct {
 ++     uint8_t major;
 ++     uint8_t minor;
 ++} sdp_version_t;
 ++
 ++typedef struct {
 ++     uint8_t *data;
 ++     uint32_t data_size;
 ++     uint32_t buf_size;
 ++} sdp_buf_t;
 ++
 ++typedef struct {
 ++     uint32_t handle;
 ++
 ++     /* Search pattern: a sequence of all UUIDs seen in this record */
 ++     sdp_list_t *pattern;
 ++     sdp_list_t *attrlist;
 ++
 ++     /* Main service class for Extended Inquiry Response */
 ++     uuid_t svclass;
 ++} sdp_record_t;
 ++
 ++typedef struct sdp_data_struct sdp_data_t;
 ++struct sdp_data_struct {
 ++     uint8_t dtd;
 ++     uint16_t attrId;
 ++     union {
 ++             int8_t    int8;
 ++             int16_t   int16;
 ++             int32_t   int32;
 ++             int64_t   int64;
 ++             uint128_t int128;
 ++             uint8_t   uint8;
 ++             uint16_t  uint16;
 ++             uint32_t  uint32;
 ++             uint64_t  uint64;
 ++             uint128_t uint128;
 ++             uuid_t    uuid;
 ++             char     *str;
 ++             sdp_data_t *dataseq;
 ++     } val;
 ++     sdp_data_t *next;
 ++     int unitSize;
 ++};
 ++
 ++#ifdef __cplusplus
 ++}
 ++#endif
 ++
 ++#endif /* __SDP_H */
index 143056f,0000000,0000000..143056f
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
diff --cc libs/src/hci.c
index e2c355c,0000000,0000000..c29dd5d
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
@@@@ -1,2485 -1,0 -1,0 +1,2489 @@@@
 ++/*
 ++ *
 ++ *  BlueZ - Bluetooth protocol stack for Linux
 ++ *
 ++ *  Copyright (C) 2000-2001  Qualcomm Incorporated
 ++ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 ++ *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
 ++ *
 ++ *
 ++ *  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 <config.h>
 ++#endif
 ++
 ++#include <stdio.h>
 ++#include <errno.h>
 ++#include <fcntl.h>
 ++#include <unistd.h>
 ++#include <stdlib.h>
 ++#include <string.h>
 ++
 ++#include <sys/param.h>
 ++#include <sys/uio.h>
 ++#include <sys/poll.h>
 ++#include <sys/types.h>
 ++#include <sys/ioctl.h>
 ++#include <sys/socket.h>
 ++
 ++#include <bluetooth/bluetooth.h>
 ++#include <bluetooth/hci.h>
 ++#include <bluetooth/hci_lib.h>
 ++
+++#ifndef MIN
+++#define MIN(a,b) ((a)<(b)?(a):(b))
+++#endif
+++
 ++typedef struct {
 ++     char *str;
 ++     unsigned int val;
 ++} hci_map;
 ++
 ++static char *hci_bit2str(hci_map *m, unsigned int val) 
 ++{
 ++     char *str = malloc(120);
 ++     char *ptr = str;
 ++
 ++     if (!str)
 ++             return NULL;
 ++
 ++     *ptr = 0;
 ++     while (m->str) {
 ++             if ((unsigned int) m->val & val)
 ++                     ptr += sprintf(ptr, "%s ", m->str);
 ++             m++;
 ++     }
 ++     return str;
 ++}
 ++
 ++static int hci_str2bit(hci_map *map, char *str, unsigned int *val)
 ++{
 ++     char *t, *ptr;
 ++     hci_map *m;
 ++     int set;
 ++
 ++     if (!str || !(str = ptr = strdup(str)))
 ++             return 0;
 ++
 ++     *val = set = 0;
 ++
 ++     while ((t = strsep(&ptr, ","))) {
 ++             for (m = map; m->str; m++) {
 ++                     if (!strcasecmp(m->str, t)) {
 ++                             *val |= (unsigned int) m->val;
 ++                             set = 1;
 ++                     }
 ++             }
 ++     }
 ++     free(str);
 ++
 ++     return set;
 ++}
 ++
 ++static char *hci_uint2str(hci_map *m, unsigned int val) 
 ++{
 ++     char *str = malloc(50);
 ++     char *ptr = str;
 ++
 ++     if (!str)
 ++             return NULL;
 ++
 ++     *ptr = 0;
 ++     while (m->str) {
 ++             if ((unsigned int) m->val == val) {
 ++                     ptr += sprintf(ptr, "%s", m->str);
 ++                     break;
 ++             }
 ++             m++;
 ++     }
 ++     return str;
 ++}
 ++
 ++static int hci_str2uint(hci_map *map, char *str, unsigned int *val)
 ++{
 ++     char *t, *ptr;
 ++     hci_map *m;
 ++     int set = 0;
 ++
 ++     if (!str)
 ++             return 0;
 ++
 ++     str = ptr = strdup(str);
 ++
 ++     while ((t = strsep(&ptr, ","))) {
 ++             for (m = map; m->str; m++) {
 ++                     if (!strcasecmp(m->str,t)) {
 ++                             *val = (unsigned int) m->val; set = 1;
 ++                             break;
 ++                     }
 ++             }
 ++     }
 ++     free(str);
 ++
 ++     return set;
 ++}
 ++
 ++char *hci_dtypetostr(int type)
 ++{
 ++     switch (type) {
 ++     case HCI_VIRTUAL:
 ++             return "VIRTUAL";
 ++     case HCI_USB:
 ++             return "USB";
 ++     case HCI_PCCARD:
 ++             return "PCCARD";
 ++     case HCI_UART:
 ++             return "UART";
 ++     case HCI_RS232:
 ++             return "RS232";
 ++     case HCI_PCI:
 ++             return "PCI";
 ++     case HCI_SDIO:
 ++             return "SDIO";
 ++     default:
 ++             return "UNKNOWN";
 ++     }
 ++}
 ++
 ++/* HCI dev flags mapping */
 ++static hci_map dev_flags_map[] = {
 ++     { "UP",      HCI_UP      },
 ++     { "INIT",    HCI_INIT    },
 ++     { "RUNNING", HCI_RUNNING },
 ++     { "RAW",     HCI_RAW     },
 ++     { "PSCAN",   HCI_PSCAN   },
 ++     { "ISCAN",   HCI_ISCAN   },
 ++     { "INQUIRY", HCI_INQUIRY },
 ++     { "AUTH",    HCI_AUTH    },
 ++     { "ENCRYPT", HCI_ENCRYPT },
 ++     { "SECMGR",  HCI_SECMGR  },
 ++     { NULL }
 ++};
 ++
 ++char *hci_dflagstostr(uint32_t flags)
 ++{
 ++     char *str = bt_malloc(50);
 ++     char *ptr = str;
 ++     hci_map *m = dev_flags_map;
 ++
 ++     if (!str)
 ++             return NULL;
 ++
 ++     *ptr = 0;
 ++
 ++     if (!hci_test_bit(HCI_UP, &flags))
 ++             ptr += sprintf(ptr, "DOWN ");
 ++
 ++     while (m->str) {
 ++             if (hci_test_bit(m->val, &flags))
 ++                     ptr += sprintf(ptr, "%s ", m->str);
 ++             m++;
 ++     }       
 ++     return str;
 ++}
 ++
 ++/* HCI packet type mapping */
 ++static hci_map pkt_type_map[] = {
 ++     { "DM1",   HCI_DM1  },
 ++     { "DM3",   HCI_DM3  },
 ++     { "DM5",   HCI_DM5  },
 ++     { "DH1",   HCI_DH1  },
 ++     { "DH3",   HCI_DH3  },
 ++     { "DH5",   HCI_DH5  },
 ++     { "HV1",   HCI_HV1  },
 ++     { "HV2",   HCI_HV2  },
 ++     { "HV3",   HCI_HV3  },
 ++     { "2-DH1", HCI_2DH1 },
 ++     { "2-DH3", HCI_2DH3 },
 ++     { "2-DH5", HCI_2DH5 },
 ++     { "3-DH1", HCI_3DH1 },
 ++     { "3-DH3", HCI_3DH3 },
 ++     { "3-DH5", HCI_3DH5 },
 ++     { NULL }
 ++};
 ++
 ++static hci_map sco_ptype_map[] = {
 ++     { "HV1",   0x0001   },
 ++     { "HV2",   0x0002   },
 ++     { "HV3",   0x0004   },
 ++     { "EV3",   HCI_EV3  },
 ++     { "EV4",   HCI_EV4  },
 ++     { "EV5",   HCI_EV5  },
 ++     { "2-EV3", HCI_2EV3 },
 ++     { "2-EV5", HCI_2EV5 },
 ++     { "3-EV3", HCI_3EV3 },
 ++     { "3-EV5", HCI_3EV5 },
 ++     { NULL }
 ++};
 ++
 ++char *hci_ptypetostr(unsigned int ptype)
 ++{
 ++     return hci_bit2str(pkt_type_map, ptype);
 ++}
 ++
 ++int hci_strtoptype(char *str, unsigned int *val)
 ++{
 ++     return hci_str2bit(pkt_type_map, str, val);
 ++}
 ++
 ++char *hci_scoptypetostr(unsigned int ptype)
 ++{
 ++     return hci_bit2str(sco_ptype_map, ptype);
 ++}
 ++
 ++int hci_strtoscoptype(char *str, unsigned int *val)
 ++{
 ++     return hci_str2bit(sco_ptype_map, str, val);
 ++}
 ++
 ++/* Link policy mapping */
 ++static hci_map link_policy_map[] = {
 ++     { "NONE",       0               },
 ++     { "RSWITCH",    HCI_LP_RSWITCH  },
 ++     { "HOLD",       HCI_LP_HOLD     },
 ++     { "SNIFF",      HCI_LP_SNIFF    },
 ++     { "PARK",       HCI_LP_PARK     },
 ++     { NULL }
 ++};
 ++
 ++char *hci_lptostr(unsigned int lp)
 ++{
 ++     return hci_bit2str(link_policy_map, lp);
 ++}
 ++
 ++int hci_strtolp(char *str, unsigned int *val)
 ++{
 ++     return hci_str2bit(link_policy_map, str, val);
 ++}
 ++
 ++/* Link mode mapping */
 ++static hci_map link_mode_map[] = {
 ++     { "NONE",       0               },
 ++     { "ACCEPT",     HCI_LM_ACCEPT   },
 ++     { "MASTER",     HCI_LM_MASTER   },
 ++     { "AUTH",       HCI_LM_AUTH     },
 ++     { "ENCRYPT",    HCI_LM_ENCRYPT  },
 ++     { "TRUSTED",    HCI_LM_TRUSTED  },
 ++     { "RELIABLE",   HCI_LM_RELIABLE },
 ++     { "SECURE",     HCI_LM_SECURE   },
 ++     { NULL }
 ++};
 ++
 ++char *hci_lmtostr(unsigned int lm)
 ++{
 ++     char *s, *str = bt_malloc(50);
 ++     if (!str)
 ++             return NULL;
 ++
 ++     *str = 0;
 ++     if (!(lm & HCI_LM_MASTER))
 ++             strcpy(str, "SLAVE ");
 ++
 ++     s = hci_bit2str(link_mode_map, lm);
 ++     if (!s) {
 ++             bt_free(str);
 ++             return NULL;
 ++     }
 ++
 ++     strcat(str, s);
 ++     free(s);
 ++     return str;
 ++}
 ++
 ++int hci_strtolm(char *str, unsigned int *val)
 ++{
 ++     return hci_str2bit(link_mode_map, str, val);
 ++}
 ++
 ++/* Command mapping */
 ++static hci_map commands_map[] = {
 ++     { "Inquiry",                                    0   },
 ++     { "Inquiry Cancel",                             1   },
 ++     { "Periodic Inquiry Mode",                      2   },
 ++     { "Exit Periodic Inquiry Mode",                 3   },
 ++     { "Create Connection",                          4   },
 ++     { "Disconnect",                                 5   },
 ++     { "Add SCO Connection",                         6   },
 ++     { "Cancel Create Connection",                   7   },
 ++
 ++     { "Accept Connection Request",                  8   },
 ++     { "Reject Connection Request",                  9   },
 ++     { "Link Key Request Reply",                     10  },
 ++     { "Link Key Request Negative Reply",            11  },
 ++     { "PIN Code Request Reply",                     12  },
 ++     { "PIN Code Request Negative Reply",            13  },
 ++     { "Change Connection Packet Type",              14  },
 ++     { "Authentication Requested",                   15  },
 ++
 ++     { "Set Connection Encryption",                  16  },
 ++     { "Change Connection Link Key",                 17  },
 ++     { "Master Link Key",                            18  },
 ++     { "Remote Name Request",                        19  },
 ++     { "Cancel Remote Name Request",                 20  },
 ++     { "Read Remote Supported Features",             21  },
 ++     { "Read Remote Extended Features",              22  },
 ++     { "Read Remote Version Information",            23  },
 ++
 ++     { "Read Clock Offset",                          24  },
 ++     { "Read LMP Handle",                            25  },
 ++     { "Reserved",                                   26  },
 ++     { "Reserved",                                   27  },
 ++     { "Reserved",                                   28  },
 ++     { "Reserved",                                   29  },
 ++     { "Reserved",                                   30  },
 ++     { "Reserved",                                   31  },
 ++
 ++     { "Reserved",                                   32  },
 ++     { "Hold Mode",                                  33  },
 ++     { "Sniff Mode",                                 34  },
 ++     { "Exit Sniff Mode",                            35  },
 ++     { "Park State",                                 36  },
 ++     { "Exit Park State",                            37  },
 ++     { "QoS Setup",                                  38  },
 ++     { "Role Discovery",                             39  },
 ++
 ++     { "Switch Role",                                40  },
 ++     { "Read Link Policy Settings",                  41  },
 ++     { "Write Link Policy Settings",                 42  },
 ++     { "Read Default Link Policy Settings",          43  },
 ++     { "Write Default Link Policy Settings",         44  },
 ++     { "Flow Specification",                         45  },
 ++     { "Set Event Mask",                             46  },
 ++     { "Reset",                                      47  },
 ++
 ++     { "Set Event Filter",                           48  },
 ++     { "Flush",                                      49  },
 ++     { "Read PIN Type",                              50  },
 ++     { "Write PIN Type",                             51  },
 ++     { "Create New Unit Key",                        52  },
 ++     { "Read Stored Link Key",                       53  },
 ++     { "Write Stored Link Key",                      54  },
 ++     { "Delete Stored Link Key",                     55  },
 ++
 ++     { "Write Local Name",                           56  },
 ++     { "Read Local Name",                            57  },
 ++     { "Read Connection Accept Timeout",             58  },
 ++     { "Write Connection Accept Timeout",            59  },
 ++     { "Read Page Timeout",                          60  },
 ++     { "Write Page Timeout",                         61  },
 ++     { "Read Scan Enable",                           62  },
 ++     { "Write Scan Enable",                          63  },
 ++
 ++     { "Read Page Scan Activity",                    64  },
 ++     { "Write Page Scan Activity",                   65  },
 ++     { "Read Inquiry Scan Activity",                 66  },
 ++     { "Write Inquiry Scan Activity",                67  },
 ++     { "Read Authentication Enable",                 68  },
 ++     { "Write Authentication Enable",                69  },
 ++     { "Read Encryption Mode",                       70  },
 ++     { "Write Encryption Mode",                      71  },
 ++
 ++     { "Read Class Of Device",                       72  },
 ++     { "Write Class Of Device",                      73  },
 ++     { "Read Voice Setting",                         74  },
 ++     { "Write Voice Setting",                        75  },
 ++     { "Read Automatic Flush Timeout",               76  },
 ++     { "Write Automatic Flush Timeout",              77  },
 ++     { "Read Num Broadcast Retransmissions",         78  },
 ++     { "Write Num Broadcast Retransmissions",        79  },
 ++
 ++     { "Read Hold Mode Activity",                    80  },
 ++     { "Write Hold Mode Activity",                   81  },
 ++     { "Read Transmit Power Level",                  82  },
 ++     { "Read Synchronous Flow Control Enable",       83  },
 ++     { "Write Synchronous Flow Control Enable",      84  },
 ++     { "Set Host Controller To Host Flow Control",   85  },
 ++     { "Host Buffer Size",                           86  },
 ++     { "Host Number Of Completed Packets",           87  },
 ++
 ++     { "Read Link Supervision Timeout",              88  },
 ++     { "Write Link Supervision Timeout",             89  },
 ++     { "Read Number of Supported IAC",               90  },
 ++     { "Read Current IAC LAP",                       91  },
 ++     { "Write Current IAC LAP",                      92  },
 ++     { "Read Page Scan Period Mode",                 93  },
 ++     { "Write Page Scan Period Mode",                94  },
 ++     { "Read Page Scan Mode",                        95  },
 ++
 ++     { "Write Page Scan Mode",                       96  },
 ++     { "Set AFH Channel Classification",             97  },
 ++     { "Reserved",                                   98  },
 ++     { "Reserved",                                   99  },
 ++     { "Read Inquiry Scan Type",                     100 },
 ++     { "Write Inquiry Scan Type",                    101 },
 ++     { "Read Inquiry Mode",                          102 },
 ++     { "Write Inquiry Mode",                         103 },
 ++
 ++     { "Read Page Scan Type",                        104 },
 ++     { "Write Page Scan Type",                       105 },
 ++     { "Read AFH Channel Assessment Mode",           106 },
 ++     { "Write AFH Channel Assessment Mode",          107 },
 ++     { "Reserved",                                   108 },
 ++     { "Reserved",                                   109 },
 ++     { "Reserved",                                   110 },
 ++     { "Reserved",                                   111 },
 ++
 ++     { "Reserved",                                   112 },
 ++     { "Reserved",                                   113 },
 ++     { "Reserved",                                   114 },
 ++     { "Read Local Version Information",             115 },
 ++     { "Read Local Supported Commands",              116 },
 ++     { "Read Local Supported Features",              117 },
 ++     { "Read Local Extended Features",               118 },
 ++     { "Read Buffer Size",                           119 },
 ++
 ++     { "Read Country Code",                          120 },
 ++     { "Read BD ADDR",                               121 },
 ++     { "Read Failed Contact Counter",                122 },
 ++     { "Reset Failed Contact Counter",               123 },
 ++     { "Get Link Quality",                           124 },
 ++     { "Read RSSI",                                  125 },
 ++     { "Read AFH Channel Map",                       126 },
 ++     { "Read BD Clock",                              127 },
 ++
 ++     { "Read Loopback Mode",                         128 },
 ++     { "Write Loopback Mode",                        129 },
 ++     { "Enable Device Under Test Mode",              130 },
 ++     { "Setup Synchronous Connection",               131 },
 ++     { "Accept Synchronous Connection",              132 },
 ++     { "Reject Synchronous Connection",              133 },
 ++     { "Reserved",                                   134 },
 ++     { "Reserved",                                   135 },
 ++
 ++     { "Read Extended Inquiry Response",             136 },
 ++     { "Write Extended Inquiry Response",            137 },
 ++     { "Refresh Encryption Key",                     138 },
 ++     { "Reserved",                                   139 },
 ++     { "Sniff Subrating",                            140 },
 ++     { "Read Simple Pairing Mode",                   141 },
 ++     { "Write Simple Pairing Mode",                  142 },
 ++     { "Read Local OOB Data",                        143 },
 ++
 ++     { "Read Inquiry Transmit Power Level",          144 },
 ++     { "Write Inquiry Transmit Power Level",         145 },
 ++     { "Read Default Erroneous Data Reporting",      146 },
 ++     { "Write Default Erroneous Data Reporting",     147 },
 ++     { "Reserved",                                   148 },
 ++     { "Reserved",                                   149 },
 ++     { "Reserved",                                   150 },
 ++     { "IO Capability Request Reply",                151 },
 ++
 ++     { "User Confirmation Request Reply",            152 },
 ++     { "User Confirmation Request Negative Reply",   153 },
 ++     { "User Passkey Request Reply",                 154 },
 ++     { "User Passkey Request Negative Reply",        155 },
 ++     { "Remote OOB Data Request Reply",              156 },
 ++     { "Write Simple Pairing Debug Mode",            157 },
 ++     { "Enhanced Flush",                             158 },
 ++     { "Remote OOB Data Request Negative Reply",     159 },
 ++
 ++     { "Reserved",                                   160 },
 ++     { "Reserved",                                   161 },
 ++     { "Send Keypress Notification",                 162 },
 ++     { "IO Capabilities Response Negative Reply",    163 },
 ++     { "Reserved",                                   164 },
 ++     { "Reserved",                                   165 },
 ++     { "Reserved",                                   166 },
 ++     { "Reserved",                                   167 },
 ++
 ++     { NULL }
 ++};
 ++
 ++char *hci_cmdtostr(unsigned int cmd)
 ++{
 ++     return hci_uint2str(commands_map, cmd);
 ++}
 ++
 ++char *hci_commandstostr(uint8_t *commands, char *pref, int width)
 ++{
 ++     hci_map *m;
 ++     char *off, *ptr, *str;
 ++     int size = 10;
 ++
 ++     m = commands_map;
 ++
 ++     while (m->str) {
 ++             if (commands[m->val / 8] & (1 << (m->val % 8)))
 ++                     size += strlen(m->str) + (pref ? strlen(pref) : 0) + 3;
 ++             m++;
 ++     }
 ++
 ++     str = bt_malloc(size);
 ++     if (!str)
 ++             return NULL;
 ++
 ++     ptr = str; *ptr = '\0';
 ++
 ++     if (pref)
 ++             ptr += sprintf(ptr, "%s", pref);
 ++
 ++     off = ptr;
 ++
 ++     m = commands_map;
 ++
 ++     while (m->str) {
 ++             if (commands[m->val / 8] & (1 << (m->val % 8))) {
 ++                     if (strlen(off) + strlen(m->str) > width - 3) {
 ++                             ptr += sprintf(ptr, "\n%s", pref ? pref : "");
 ++                             off = ptr;
 ++                     }
 ++                     ptr += sprintf(ptr, "'%s' ", m->str);
 ++             }
 ++             m++;
 ++     }
 ++
 ++     return str;
 ++}
 ++
 ++/* Version mapping */
 ++static hci_map ver_map[] = {
 ++     { "1.0b",       0x00 },
 ++     { "1.1",        0x01 },
 ++     { "1.2",        0x02 },
 ++     { "2.0",        0x03 },
 ++     { "2.1",        0x04 },
 ++     { NULL }
 ++};
 ++
 ++char *hci_vertostr(unsigned int ver)
 ++{
 ++     return hci_uint2str(ver_map, ver);
 ++}
 ++
 ++int hci_strtover(char *str, unsigned int *ver)
 ++{
 ++     return hci_str2uint(ver_map, str, ver);
 ++}
 ++
 ++char *lmp_vertostr(unsigned int ver)
 ++{
 ++     return hci_uint2str(ver_map, ver);
 ++}
 ++
 ++int lmp_strtover(char *str, unsigned int *ver)
 ++{
 ++     return hci_str2uint(ver_map, str, ver);
 ++}
 ++
 ++/* LMP features mapping */
 ++static hci_map lmp_features_map[8][9] = {
 ++     {       /* Byte 0 */
 ++             { "<3-slot packets>",   LMP_3SLOT       },      /* Bit 0 */
 ++             { "<5-slot packets>",   LMP_5SLOT       },      /* Bit 1 */
 ++             { "<encryption>",       LMP_ENCRYPT     },      /* Bit 2 */
 ++             { "<slot offset>",      LMP_SOFFSET     },      /* Bit 3 */
 ++             { "<timing accuracy>",  LMP_TACCURACY   },      /* Bit 4 */
 ++             { "<role switch>",      LMP_RSWITCH     },      /* Bit 5 */
 ++             { "<hold mode>",        LMP_HOLD        },      /* Bit 6 */
 ++             { "<sniff mode>",       LMP_SNIFF       },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 1 */
 ++             { "<park state>",       LMP_PARK        },      /* Bit 0 */
 ++             { "<RSSI>",             LMP_RSSI        },      /* Bit 1 */
 ++             { "<channel quality>",  LMP_QUALITY     },      /* Bit 2 */
 ++             { "<SCO link>",         LMP_SCO         },      /* Bit 3 */
 ++             { "<HV2 packets>",      LMP_HV2         },      /* Bit 4 */
 ++             { "<HV3 packets>",      LMP_HV3         },      /* Bit 5 */
 ++             { "<u-law log>",        LMP_ULAW        },      /* Bit 6 */
 ++             { "<A-law log>",        LMP_ALAW        },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 2 */
 ++             { "<CVSD>",             LMP_CVSD        },      /* Bit 0 */
 ++             { "<paging scheme>",    LMP_PSCHEME     },      /* Bit 1 */
 ++             { "<power control>",    LMP_PCONTROL    },      /* Bit 2 */
 ++             { "<transparent SCO>",  LMP_TRSP_SCO    },      /* Bit 3 */
 ++             { "<broadcast encrypt>",LMP_BCAST_ENC   },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 3 */
 ++             { "<no. 24>",           0x01            },      /* Bit 0 */
 ++             { "<EDR ACL 2 Mbps>",   LMP_EDR_ACL_2M  },      /* Bit 1 */
 ++             { "<EDR ACL 3 Mbps>",   LMP_EDR_ACL_3M  },      /* Bit 2 */
 ++             { "<enhanced iscan>",   LMP_ENH_ISCAN   },      /* Bit 3 */
 ++             { "<interlaced iscan>", LMP_ILACE_ISCAN },      /* Bit 4 */
 ++             { "<interlaced pscan>", LMP_ILACE_PSCAN },      /* Bit 5 */
 ++             { "<inquiry with RSSI>",LMP_RSSI_INQ    },      /* Bit 6 */
 ++             { "<extended SCO>",     LMP_ESCO        },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 4 */
 ++             { "<EV4 packets>",      LMP_EV4         },      /* Bit 0 */
 ++             { "<EV5 packets>",      LMP_EV5         },      /* Bit 1 */
 ++             { "<no. 34>",           0x04            },      /* Bit 2 */
 ++             { "<AFH cap. slave>",   LMP_AFH_CAP_SLV },      /* Bit 3 */
 ++             { "<AFH class. slave>", LMP_AFH_CLS_SLV },      /* Bit 4 */
 ++             { "<no. 37>",           0x20            },      /* Bit 5 */
 ++             { "<no. 38>",           0x40            },      /* Bit 6 */
 ++             { "<3-slot EDR ACL>",   LMP_EDR_3SLOT   },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 5 */
 ++             { "<5-slot EDR ACL>",   LMP_EDR_5SLOT   },      /* Bit 0 */
 ++             { "<sniff subrating>",  LMP_SNIFF_SUBR  },      /* Bit 1 */
 ++             { "<pause encryption>", LMP_PAUSE_ENC   },      /* Bit 2 */
 ++             { "<AFH cap. master>",  LMP_AFH_CAP_MST },      /* Bit 3 */
 ++             { "<AFH class. master>",LMP_AFH_CLS_MST },      /* Bit 4 */
 ++             { "<EDR eSCO 2 Mbps>",  LMP_EDR_ESCO_2M },      /* Bit 5 */
 ++             { "<EDR eSCO 3 Mbps>",  LMP_EDR_ESCO_3M },      /* Bit 6 */
 ++             { "<3-slot EDR eSCO>",  LMP_EDR_3S_ESCO },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 6 */
 ++             { "<extended inquiry>", LMP_EXT_INQ     },      /* Bit 0 */
 ++             { "<no. 49>",           0x02            },      /* Bit 1 */
 ++             { "<no. 50>",           0x04            },      /* Bit 2 */
 ++             { "<simple pairing>",   LMP_SIMPLE_PAIR },      /* Bit 3 */
 ++             { "<encapsulated PDU>", LMP_ENCAPS_PDU  },      /* Bit 4 */
 ++             { "<err. data report>", LMP_ERR_DAT_REP },      /* Bit 5 */
 ++             { "<non-flush flag>",   LMP_NFLUSH_PKTS },      /* Bit 6 */
 ++             { "<no. 55>",           0x80            },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++     {       /* Byte 7 */
 ++             { "<LSTO>",             LMP_LSTO        },      /* Bit 1 */
 ++             { "<inquiry TX power>", LMP_INQ_TX_PWR  },      /* Bit 1 */
 ++             { "<no. 58>",           0x04            },      /* Bit 2 */
 ++             { "<no. 59>",           0x08            },      /* Bit 3 */
 ++             { "<no. 60>",           0x10            },      /* Bit 4 */
 ++             { "<no. 61>",           0x20            },      /* Bit 5 */
 ++             { "<no. 62>",           0x40            },      /* Bit 6 */
 ++             { "<extended features>",LMP_EXT_FEAT    },      /* Bit 7 */
 ++             { NULL }
 ++     },
 ++};
 ++
 ++char *lmp_featurestostr(uint8_t *features, char *pref, int width)
 ++{
 ++     char *off, *ptr, *str;
 ++     int i, size = 10;
 ++
 ++     for (i = 0; i < 8; i++) {
 ++             hci_map *m = lmp_features_map[i];
 ++
 ++             while (m->str) {
 ++                     if (m->val & features[i])
 ++                             size += strlen(m->str) + (pref ? strlen(pref) : 0) + 1;
 ++                     m++;
 ++             }
 ++     }
 ++
 ++     str = bt_malloc(size);
 ++     if (!str)
 ++             return NULL;
 ++
 ++     ptr = str; *ptr = '\0';
 ++
 ++     if (pref)
 ++             ptr += sprintf(ptr, "%s", pref);
 ++
 ++     off = ptr;
 ++
 ++     for (i = 0; i < 8; i++) {
 ++             hci_map *m = lmp_features_map[i];
 ++
 ++             while (m->str) {
 ++                     if (m->val & features[i]) {
 ++                             if (strlen(off) + strlen(m->str) > width - 1) {
 ++                                     ptr += sprintf(ptr, "\n%s", pref ? pref : "");
 ++                                     off = ptr;
 ++                             }
 ++                             ptr += sprintf(ptr, "%s ", m->str);
 ++                     }
 ++                     m++;
 ++             }
 ++     }
 ++
 ++     return str;
 ++}
 ++
 ++/* HCI functions that do not require open device */
 ++
 ++int hci_for_each_dev(int flag, int (*func)(int dd, int dev_id, long arg), long arg)
 ++{
 ++     struct hci_dev_list_req *dl;
 ++     struct hci_dev_req *dr;
 ++     int dev_id = -1;
 ++     int i, sk, err = 0;
 ++
 ++     sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
 ++     if (sk < 0)
 ++             return -1;
 ++
 ++     dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
 ++     if (!dl) {
 ++             err = errno;
 ++             goto done;
 ++     }
 ++
 ++     memset(dl, 0, HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
 ++
 ++     dl->dev_num = HCI_MAX_DEV;
 ++     dr = dl->dev_req;
 ++
 ++     if (ioctl(sk, HCIGETDEVLIST, (void *) dl) < 0) {
 ++             err = errno;
 ++             goto free;
 ++     }
 ++
 ++     for (i = 0; i < dl->dev_num; i++, dr++) {
 ++             if (hci_test_bit(flag, &dr->dev_opt))
 ++                     if (!func || func(sk, dr->dev_id, arg)) {
 ++                             dev_id = dr->dev_id;
 ++                             break;
 ++                     }
 ++     }
 ++
 ++     if (dev_id < 0)
 ++             err = ENODEV;
 ++
 ++free:
 ++     free(dl);
 ++
 ++done:
 ++     close(sk);
 ++     errno = err;
 ++
 ++     return dev_id;
 ++}
 ++
 ++static int __other_bdaddr(int dd, int dev_id, long arg)
 ++{
 ++     struct hci_dev_info di = { dev_id: dev_id };
 ++
 ++     if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
 ++             return 0;
 ++
 ++     if (hci_test_bit(HCI_RAW, &di.flags))
 ++             return 0;
 ++
 ++     return bacmp((bdaddr_t *) arg, &di.bdaddr);
 ++}
 ++
 ++static int __same_bdaddr(int dd, int dev_id, long arg)
 ++{
 ++     struct hci_dev_info di = { dev_id: dev_id };
 ++
 ++     if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
 ++             return 0;
 ++
 ++     return !bacmp((bdaddr_t *) arg, &di.bdaddr);
 ++}
 ++
 ++int hci_get_route(bdaddr_t *bdaddr)
 ++{
 ++     return hci_for_each_dev(HCI_UP, __other_bdaddr,
 ++                             (long) (bdaddr ? bdaddr : BDADDR_ANY));
 ++}
 ++
 ++int hci_devid(const char *str)
 ++{
 ++     bdaddr_t ba;
 ++     int id = -1;
 ++
 ++     if (!strncmp(str, "hci", 3) && strlen(str) >= 4) {
 ++             id = atoi(str + 3);
 ++             if (hci_devba(id, &ba) < 0)
 ++                     return -1;
 ++     } else {
 ++             errno = ENODEV;
 ++             str2ba(str, &ba);
 ++             id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) &ba);
 ++     }
 ++
 ++     return id;
 ++}
 ++
 ++int hci_devinfo(int dev_id, struct hci_dev_info *di)
 ++{
 ++     int dd, err, ret;
 ++
 ++     dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
 ++     if (dd < 0)
 ++             return dd;
 ++
 ++     memset(di, 0, sizeof(struct hci_dev_info));
 ++
 ++     di->dev_id = dev_id;
 ++     ret = ioctl(dd, HCIGETDEVINFO, (void *) di);
 ++
 ++     err = errno;
 ++     close(dd);
 ++     errno = err;
 ++
 ++     return ret;
 ++}
 ++
 ++int hci_devba(int dev_id, bdaddr_t *bdaddr)
 ++{
 ++     struct hci_dev_info di;
 ++
 ++     memset(&di, 0, sizeof(di));
 ++
 ++     if (hci_devinfo(dev_id, &di))
 ++             return -1;
 ++
 ++     if (!hci_test_bit(HCI_UP, &di.flags)) {
 ++             errno = ENETDOWN;
 ++             return -1;
 ++     }
 ++
 ++     bacpy(bdaddr, &di.bdaddr);
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags)
 ++{
 ++     struct hci_inquiry_req *ir;
 ++     uint8_t num_rsp = nrsp;
 ++     void *buf;
 ++     int dd, size, err, ret = -1;
 ++
 ++     if (nrsp <= 0) {
 ++             num_rsp = 0;
 ++             nrsp = 255;
 ++     }
 ++
 ++     if (dev_id < 0) {
 ++             dev_id = hci_get_route(NULL);
 ++             if (dev_id < 0) {
 ++                     errno = ENODEV;
 ++                     return -1;
 ++             }
 ++     }       
 ++
 ++     dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
 ++     if (dd < 0)
 ++             return dd;
 ++
 ++     buf = malloc(sizeof(*ir) + (sizeof(inquiry_info) * (nrsp)));
 ++     if (!buf)
 ++             goto done;
 ++
 ++     ir = buf;
 ++     ir->dev_id  = dev_id;
 ++     ir->num_rsp = num_rsp;
 ++     ir->length  = len;
 ++     ir->flags   = flags;
 ++
 ++     if (lap) {
 ++             memcpy(ir->lap, lap, 3);
 ++     } else {
 ++             ir->lap[0] = 0x33;
 ++             ir->lap[1] = 0x8b;
 ++             ir->lap[2] = 0x9e;
 ++     }
 ++
 ++     ret = ioctl(dd, HCIINQUIRY, (unsigned long) buf);
 ++     if (ret < 0)
 ++             goto free;
 ++
 ++     size = sizeof(inquiry_info) * ir->num_rsp;
 ++
 ++     if (!*ii)
 ++             *ii = malloc(size);
 ++
 ++     if (*ii) {
 ++             memcpy((void *) *ii, buf + sizeof(*ir), size);
 ++             ret = ir->num_rsp;
 ++     } else
 ++             ret = -1;
 ++
 ++free:
 ++     free(buf);
 ++
 ++done:
 ++     err = errno;
 ++     close(dd);
 ++     errno = err;
 ++
 ++     return ret;
 ++}
 ++
 ++/* Open HCI device. 
 ++ * Returns device descriptor (dd). */
 ++int hci_open_dev(int dev_id)
 ++{
 ++     struct sockaddr_hci a;
 ++     int dd, err;
 ++
 ++     /* Create HCI socket */
 ++     dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
 ++     if (dd < 0)
 ++             return dd;
 ++
 ++     /* Bind socket to the HCI device */
 ++     memset(&a, 0, sizeof(a));
 ++     a.hci_family = AF_BLUETOOTH;
 ++     a.hci_dev = dev_id;
 ++     if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
 ++             goto failed;
 ++
 ++     return dd;
 ++
 ++failed:
 ++     err = errno;
 ++     close(dd);
 ++     errno = err;
 ++
 ++     return -1;
 ++}
 ++
 ++int hci_close_dev(int dd)
 ++{
 ++     return close(dd);
 ++}
 ++
 ++/* HCI functions that require open device
 ++ * dd - Device descriptor returned by hci_open_dev. */
 ++
 ++int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
 ++{
 ++     uint8_t type = HCI_COMMAND_PKT;
 ++     hci_command_hdr hc;
 ++     struct iovec iv[3];
 ++     int ivn;
 ++
 ++     hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
 ++     hc.plen= plen;
 ++
 ++     iv[0].iov_base = &type;
 ++     iv[0].iov_len  = 1;
 ++     iv[1].iov_base = &hc;
 ++     iv[1].iov_len  = HCI_COMMAND_HDR_SIZE;
 ++     ivn = 2;
 ++
 ++     if (plen) {
 ++             iv[2].iov_base = param;
 ++             iv[2].iov_len  = plen;
 ++             ivn = 3;
 ++     }
 ++
 ++     while (writev(dd, iv, ivn) < 0) {
 ++             if (errno == EAGAIN || errno == EINTR)
 ++                     continue;
 ++             return -1;
 ++     }
 ++     return 0;
 ++}
 ++
 ++int hci_send_req(int dd, struct hci_request *r, int to)
 ++{
 ++     unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
 ++     uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
 ++     struct hci_filter nf, of;
 ++     socklen_t len;
 ++     hci_event_hdr *hdr;
 ++     int err, try;
 ++
 ++     len = sizeof(of);
 ++     if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &len) < 0)
 ++             return -1;
 ++
 ++     hci_filter_clear(&nf);
 ++     hci_filter_set_ptype(HCI_EVENT_PKT,  &nf);
 ++     hci_filter_set_event(EVT_CMD_STATUS, &nf);
 ++     hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
 ++     hci_filter_set_event(r->event, &nf);
 ++     hci_filter_set_opcode(opcode, &nf);
 ++     if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
 ++             return -1;
 ++
 ++     if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
 ++             goto failed;
 ++
 ++     try = 10;
 ++     while (try--) {
 ++             evt_cmd_complete *cc;
 ++             evt_cmd_status *cs;
 ++             evt_remote_name_req_complete *rn;
 ++             remote_name_req_cp *cp;
 ++
 ++             if (to) {
 ++                     struct pollfd p;
 ++                     int n;
 ++
 ++                     p.fd = dd; p.events = POLLIN;
 ++                     while ((n = poll(&p, 1, to)) < 0) {
 ++                             if (errno == EAGAIN || errno == EINTR)
 ++                                     continue;
 ++                             goto failed;
 ++                     }
 ++
 ++                     if (!n) {
 ++                             errno = ETIMEDOUT;
 ++                             goto failed;
 ++                     }
 ++
 ++                     to -= 10;
 ++                     if (to < 0) to = 0;
 ++
 ++             }
 ++
 ++             while ((len = read(dd, buf, sizeof(buf))) < 0) {
 ++                     if (errno == EAGAIN || errno == EINTR)
 ++                             continue;
 ++                     goto failed;
 ++             }
 ++
 ++             hdr = (void *) (buf + 1);
 ++             ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
 ++             len -= (1 + HCI_EVENT_HDR_SIZE);
 ++
 ++             switch (hdr->evt) {
 ++             case EVT_CMD_STATUS:
 ++                     cs = (void *) ptr;
 ++
 ++                     if (cs->opcode != opcode)
 ++                             continue;
 ++
 ++                     if (r->event != EVT_CMD_STATUS) {
 ++                             if (cs->status) {
 ++                                     errno = EIO;
 ++                                     goto failed;
 ++                             }
 ++                             break;
 ++                     }
 ++
 ++                     r->rlen = MIN(len, r->rlen);
 ++                     memcpy(r->rparam, ptr, r->rlen);
 ++                     goto done;
 ++
 ++             case EVT_CMD_COMPLETE:
 ++                     cc = (void *) ptr;
 ++
 ++                     if (cc->opcode != opcode)
 ++                             continue;
 ++
 ++                     ptr += EVT_CMD_COMPLETE_SIZE;
 ++                     len -= EVT_CMD_COMPLETE_SIZE;
 ++
 ++                     r->rlen = MIN(len, r->rlen);
 ++                     memcpy(r->rparam, ptr, r->rlen);
 ++                     goto done;
 ++
 ++             case EVT_REMOTE_NAME_REQ_COMPLETE:
 ++                     if (hdr->evt != r->event)
 ++                             break;
 ++
 ++                     rn = (void *) ptr;
 ++                     cp = r->cparam;
 ++
 ++                     if (bacmp(&rn->bdaddr, &cp->bdaddr))
 ++                             continue;
 ++
 ++                     r->rlen = MIN(len, r->rlen);
 ++                     memcpy(r->rparam, ptr, r->rlen);
 ++                     goto done;
 ++
 ++             default:
 ++                     if (hdr->evt != r->event)
 ++                             break;
 ++
 ++                     r->rlen = MIN(len, r->rlen);
 ++                     memcpy(r->rparam, ptr, r->rlen);
 ++                     goto done;
 ++             }
 ++     }
 ++     errno = ETIMEDOUT;
 ++
 ++failed:
 ++     err = errno;
 ++     setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
 ++     errno = err;
 ++     return -1;
 ++
 ++done:
 ++     setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
 ++     return 0;
 ++}
 ++
 ++int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to)
 ++{
 ++     evt_conn_complete rp;
 ++     create_conn_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++     cp.pkt_type       = ptype;
 ++     cp.pscan_rep_mode = 0x02;
 ++     cp.clock_offset   = clkoffset;
 ++     cp.role_switch    = rswitch;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_CREATE_CONN;
 ++     rq.event  = EVT_CONN_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = CREATE_CONN_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_CONN_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *handle = rp.handle;
 ++     return 0;
 ++}
 ++
 ++int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to)
 ++{
 ++     evt_disconn_complete rp;
 ++     disconnect_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++     cp.reason = reason;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_DISCONNECT;
 ++     rq.event  = EVT_DISCONN_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = DISCONNECT_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_DISCONN_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_name(int dd, int len, char *name, int to)
 ++{
 ++     read_local_name_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_LOCAL_NAME;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_NAME_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     rp.name[247] = '\0';
 ++     strncpy(name, (char *) rp.name, len);
 ++     return 0;
 ++}
 ++
 ++int hci_write_local_name(int dd, const char *name, int to)
 ++{
 ++     change_local_name_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     strncpy((char *) cp.name, name, sizeof(cp.name));
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_CHANGE_LOCAL_NAME;
 ++     rq.cparam = &cp;
 ++     rq.clen   = CHANGE_LOCAL_NAME_CP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to)
 ++{
 ++     evt_remote_name_req_complete rn;
 ++     remote_name_req_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++     cp.pscan_rep_mode = pscan_rep_mode;
 ++     cp.clock_offset   = clkoffset;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_REMOTE_NAME_REQ;
 ++     rq.cparam = &cp;
 ++     rq.clen   = REMOTE_NAME_REQ_CP_SIZE;
 ++     rq.event  = EVT_REMOTE_NAME_REQ_COMPLETE;
 ++     rq.rparam = &rn;
 ++     rq.rlen   = EVT_REMOTE_NAME_REQ_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rn.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     rn.name[247] = '\0';
 ++     strncpy(name, (char *) rn.name, len);
 ++     return 0;
 ++}
 ++
 ++int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)
 ++{
 ++     return hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000, len, name, to);
 ++}
 ++
 ++int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to)
 ++{
 ++     remote_name_req_cancel_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_REMOTE_NAME_REQ_CANCEL;
 ++     rq.cparam = &cp;
 ++     rq.clen   = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to)
 ++{
 ++     evt_read_remote_version_complete rp;
 ++     read_remote_version_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_READ_REMOTE_VERSION;
 ++     rq.event  = EVT_READ_REMOTE_VERSION_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_REMOTE_VERSION_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_READ_REMOTE_VERSION_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     ver->manufacturer = btohs(rp.manufacturer);
 ++     ver->lmp_ver      = rp.lmp_ver;
 ++     ver->lmp_subver   = btohs(rp.lmp_subver);
 ++     return 0;
 ++}
 ++
 ++int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)
 ++{
 ++     evt_read_remote_features_complete rp;
 ++     read_remote_features_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_READ_REMOTE_FEATURES;
 ++     rq.event  = EVT_READ_REMOTE_FEATURES_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_REMOTE_FEATURES_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     memcpy(features, rp.features, 8);
 ++     return 0;
 ++}
 ++
 ++int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to)
 ++{
 ++     evt_read_remote_ext_features_complete rp;
 ++     read_remote_ext_features_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle   = handle;
 ++     cp.page_num = page;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_READ_REMOTE_EXT_FEATURES;
 ++     rq.event  = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_REMOTE_EXT_FEATURES_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *max_page = rp.max_page_num;
 ++     memcpy(features, rp.features, 8);
 ++     return 0;
 ++}
 ++
 ++int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to)
 ++{
 ++     evt_read_clock_offset_complete rp;
 ++     read_clock_offset_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_READ_CLOCK_OFFSET;
 ++     rq.event  = EVT_READ_CLOCK_OFFSET_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_CLOCK_OFFSET_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *clkoffset = rp.clock_offset;
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_version(int dd, struct hci_version *ver, int to)
 ++{
 ++     read_local_version_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_INFO_PARAM;
 ++     rq.ocf    = OCF_READ_LOCAL_VERSION;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_VERSION_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     ver->manufacturer = btohs(rp.manufacturer);
 ++     ver->hci_ver      = rp.hci_ver;
 ++     ver->hci_rev      = btohs(rp.hci_rev);
 ++     ver->lmp_ver      = rp.lmp_ver;
 ++     ver->lmp_subver   = btohs(rp.lmp_subver);
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_commands(int dd, uint8_t *commands, int to)
 ++{
 ++     read_local_commands_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_INFO_PARAM;
 ++     rq.ocf    = OCF_READ_LOCAL_COMMANDS;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_COMMANDS_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     memcpy(commands, rp.commands, 64);
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_features(int dd, uint8_t *features, int to)
 ++{
 ++     read_local_features_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_INFO_PARAM;
 ++     rq.ocf    = OCF_READ_LOCAL_FEATURES;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_FEATURES_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     memcpy(features, rp.features, 8);
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to)
 ++{
 ++     read_local_ext_features_cp cp;
 ++     read_local_ext_features_rp rp;
 ++     struct hci_request rq;
 ++
 ++     cp.page_num = page;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_INFO_PARAM;
 ++     rq.ocf    = OCF_READ_LOCAL_EXT_FEATURES;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_LOCAL_EXT_FEATURES_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_EXT_FEATURES_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *max_page = rp.max_page_num;
 ++     memcpy(features, rp.features, 8);
 ++     return 0;
 ++}
 ++
 ++int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to)
 ++{
 ++     read_bd_addr_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_INFO_PARAM;
 ++     rq.ocf    = OCF_READ_BD_ADDR;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_BD_ADDR_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     bacpy(bdaddr, &rp.bdaddr);
 ++     return 0;
 ++}
 ++
 ++int hci_read_class_of_dev(int dd, uint8_t *cls, int to)
 ++{
 ++     read_class_of_dev_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_CLASS_OF_DEV;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_CLASS_OF_DEV_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     memcpy(cls, rp.dev_class, 3);
 ++     return 0;
 ++}
 ++
 ++int hci_write_class_of_dev(int dd, uint32_t cls, int to)
 ++{
 ++     write_class_of_dev_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     cp.dev_class[0] = cls & 0xff;
 ++     cp.dev_class[1] = (cls >> 8) & 0xff;
 ++     cp.dev_class[2] = (cls >> 16) & 0xff;
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_CLASS_OF_DEV;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_CLASS_OF_DEV_CP_SIZE;
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_read_voice_setting(int dd, uint16_t *vs, int to)
 ++{
 ++     read_voice_setting_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_VOICE_SETTING;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_VOICE_SETTING_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *vs = rp.voice_setting;
 ++     return 0;
 ++}
 ++
 ++int hci_write_voice_setting(int dd, uint16_t vs, int to)
 ++{
 ++     write_voice_setting_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     cp.voice_setting = vs;
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_VOICE_SETTING;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_VOICE_SETTING_CP_SIZE;
 ++
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to)
 ++{
 ++     read_current_iac_lap_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_CURRENT_IAC_LAP;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_CURRENT_IAC_LAP_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *num_iac = rp.num_current_iac;
 ++     memcpy(lap, rp.lap, rp.num_current_iac * 3);
 ++     return 0;
 ++}
 ++
 ++int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to)
 ++{
 ++     write_current_iac_lap_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.num_current_iac = num_iac;
 ++     memcpy(&cp.lap, lap, num_iac * 3);
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_CURRENT_IAC_LAP;
 ++     rq.cparam = &cp;
 ++     rq.clen   = num_iac * 3 + 1;
 ++
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
 ++{
 ++     read_stored_link_key_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++     cp.read_all = all;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_STORED_LINK_KEY;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_STORED_LINK_KEY_CP_SIZE;
 ++
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to)
 ++{
 ++     unsigned char cp[WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16];
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp[0] = 1;
 ++     bacpy((bdaddr_t *) (cp + 1), bdaddr);
 ++     memcpy(cp + 7, key, 16);
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_STORED_LINK_KEY;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16;
 ++
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
 ++{
 ++     delete_stored_link_key_cp cp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++     cp.delete_all = all;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_DELETE_STORED_LINK_KEY;
 ++     rq.cparam = &cp;
 ++     rq.clen   = DELETE_STORED_LINK_KEY_CP_SIZE;
 ++
 ++     return hci_send_req(dd, &rq, to);
 ++}
 ++
 ++int hci_authenticate_link(int dd, uint16_t handle, int to)
 ++{
 ++     auth_requested_cp cp;
 ++     evt_auth_complete rp;
 ++     struct hci_request rq;
 ++
 ++     cp.handle = handle;
 ++
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_AUTH_REQUESTED;
 ++     rq.event  = EVT_AUTH_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = AUTH_REQUESTED_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_AUTH_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to)
 ++{
 ++     set_conn_encrypt_cp cp;
 ++     evt_encrypt_change rp;
 ++     struct hci_request rq;
 ++
 ++     cp.handle  = handle;
 ++     cp.encrypt = encrypt;
 ++
 ++     rq.ogf     = OGF_LINK_CTL;
 ++     rq.ocf     = OCF_SET_CONN_ENCRYPT;
 ++     rq.event   = EVT_ENCRYPT_CHANGE;
 ++     rq.cparam  = &cp;
 ++     rq.clen    = SET_CONN_ENCRYPT_CP_SIZE;
 ++     rq.rparam  = &rp;
 ++     rq.rlen    = EVT_ENCRYPT_CHANGE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_change_link_key(int dd, uint16_t handle, int to)
 ++{
 ++     change_conn_link_key_cp cp;
 ++     evt_change_conn_link_key_complete rp;
 ++     struct hci_request rq;
 ++
 ++     cp.handle = handle;
 ++
 ++     rq.ogf    = OGF_LINK_CTL;
 ++     rq.ocf    = OCF_CHANGE_CONN_LINK_KEY;
 ++     rq.event  = EVT_CHANGE_CONN_LINK_KEY_COMPLETE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = CHANGE_CONN_LINK_KEY_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to)
 ++{
 ++     switch_role_cp cp;
 ++     evt_role_change rp;
 ++     struct hci_request rq;
 ++
 ++     bacpy(&cp.bdaddr, bdaddr);
 ++     cp.role   = role;
 ++     rq.ogf    = OGF_LINK_POLICY;
 ++     rq.ocf    = OCF_SWITCH_ROLE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = SWITCH_ROLE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_ROLE_CHANGE_SIZE;
 ++     rq.event  = EVT_ROLE_CHANGE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to)
 ++{
 ++     park_mode_cp cp;
 ++     evt_mode_change rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof (cp));
 ++     cp.handle       = handle;
 ++     cp.max_interval = max_interval;
 ++     cp.min_interval = min_interval;
 ++
 ++     memset(&rq, 0, sizeof (rq));
 ++     rq.ogf    = OGF_LINK_POLICY;
 ++     rq.ocf    = OCF_PARK_MODE;
 ++     rq.event  = EVT_MODE_CHANGE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = PARK_MODE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_MODE_CHANGE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_exit_park_mode(int dd, uint16_t handle, int to)
 ++{
 ++     exit_park_mode_cp cp;
 ++     evt_mode_change rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof (cp));
 ++     cp.handle = handle;
 ++
 ++     memset (&rq, 0, sizeof (rq));
 ++     rq.ogf    = OGF_LINK_POLICY;
 ++     rq.ocf    = OCF_EXIT_PARK_MODE;
 ++     rq.event  = EVT_MODE_CHANGE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = EXIT_PARK_MODE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = EVT_MODE_CHANGE_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to)
 ++{
 ++     read_inquiry_scan_type_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_INQUIRY_SCAN_TYPE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_INQUIRY_SCAN_TYPE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *type = rp.type;
 ++     return 0;
 ++}
 ++
 ++int hci_write_inquiry_scan_type(int dd, uint8_t type, int to)
 ++{
 ++     write_inquiry_scan_type_cp cp;
 ++     write_inquiry_scan_type_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.type = type;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_INQUIRY_SCAN_TYPE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_INQUIRY_SCAN_TYPE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_INQUIRY_SCAN_TYPE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)
 ++{
 ++     read_inquiry_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_INQUIRY_MODE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_INQUIRY_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *mode = rp.mode;
 ++     return 0;
 ++}
 ++
 ++int hci_write_inquiry_mode(int dd, uint8_t mode, int to)
 ++{
 ++     write_inquiry_mode_cp cp;
 ++     write_inquiry_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.mode = mode;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_INQUIRY_MODE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_INQUIRY_MODE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_INQUIRY_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_afh_mode(int dd, uint8_t *mode, int to)
 ++{
 ++     read_afh_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_AFH_MODE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_AFH_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *mode = rp.mode;
 ++     return 0;
 ++}
 ++
 ++int hci_write_afh_mode(int dd, uint8_t mode, int to)
 ++{
 ++     write_afh_mode_cp cp;
 ++     write_afh_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.mode = mode;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_AFH_MODE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_AFH_MODE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_AFH_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to)
 ++{
 ++     read_ext_inquiry_response_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_EXT_INQUIRY_RESPONSE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_EXT_INQUIRY_RESPONSE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *fec = rp.fec;
 ++     memcpy(data, rp.data, 240);
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to)
 ++{
 ++     write_ext_inquiry_response_cp cp;
 ++     write_ext_inquiry_response_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.fec = fec;
 ++     memcpy(cp.data, data, 240);
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_EXT_INQUIRY_RESPONSE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to)
 ++{
 ++     read_simple_pairing_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_SIMPLE_PAIRING_MODE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_SIMPLE_PAIRING_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *mode = rp.mode;
 ++     return 0;
 ++}
 ++
 ++int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to)
 ++{
 ++     write_simple_pairing_mode_cp cp;
 ++     write_simple_pairing_mode_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.mode = mode;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_SIMPLE_PAIRING_MODE;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_SIMPLE_PAIRING_MODE_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_SIMPLE_PAIRING_MODE_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to)
 ++{
 ++     read_local_oob_data_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_LOCAL_OOB_DATA;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LOCAL_OOB_DATA_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     memcpy(hash, rp.hash, 16);
 ++     memcpy(randomizer, rp.randomizer, 16);
 ++     return 0;
 ++}
 ++
 ++int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to)
 ++{
 ++     read_inquiry_transmit_power_level_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *level = rp.level;
 ++     return 0;
 ++}
 ++
 ++int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to)
 ++{
 ++     write_inquiry_transmit_power_level_cp cp;
 ++     write_inquiry_transmit_power_level_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.level = level;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to)
 ++{
 ++     read_transmit_power_level_cp cp;
 ++     read_transmit_power_level_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++     cp.type   = type;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_TRANSMIT_POWER_LEVEL;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_TRANSMIT_POWER_LEVEL_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_TRANSMIT_POWER_LEVEL_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *level = rp.level;
 ++     return 0;
 ++}
 ++
 ++int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to)
 ++{
 ++     read_link_policy_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_POLICY;
 ++     rq.ocf    = OCF_READ_LINK_POLICY;
 ++     rq.cparam = &handle;
 ++     rq.clen   = 2;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LINK_POLICY_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *policy = rp.policy;
 ++     return 0;
 ++}
 ++
 ++int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to)
 ++{
 ++     write_link_policy_cp cp;
 ++     write_link_policy_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle = handle;
 ++     cp.policy = policy;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_LINK_POLICY;
 ++     rq.ocf    = OCF_WRITE_LINK_POLICY;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_LINK_POLICY_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_LINK_POLICY_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to)
 ++{
 ++     read_link_supervision_timeout_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_READ_LINK_SUPERVISION_TIMEOUT;
 ++     rq.cparam = &handle;
 ++     rq.clen   = 2;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *timeout = rp.timeout;
 ++     return 0;
 ++}
 ++
 ++int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to)
 ++{
 ++     write_link_supervision_timeout_cp cp;
 ++     write_link_supervision_timeout_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle  = handle;
 ++     cp.timeout = timeout;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_WRITE_LINK_SUPERVISION_TIMEOUT;
 ++     rq.cparam = &cp;
 ++     rq.clen   = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_set_afh_classification(int dd, uint8_t *map, int to)
 ++{
 ++     set_afh_classification_cp cp;
 ++     set_afh_classification_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     memcpy(cp.map, map, 10);
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_HOST_CTL;
 ++     rq.ocf    = OCF_SET_AFH_CLASSIFICATION;
 ++     rq.cparam = &cp;
 ++     rq.clen   = SET_AFH_CLASSIFICATION_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = SET_AFH_CLASSIFICATION_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to)
 ++{
 ++     read_link_quality_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_STATUS_PARAM;
 ++     rq.ocf    = OCF_READ_LINK_QUALITY;
 ++     rq.cparam = &handle;
 ++     rq.clen   = 2;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_LINK_QUALITY_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *link_quality = rp.link_quality;
 ++     return 0;
 ++}
 ++
 ++int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
 ++{
 ++     read_rssi_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_STATUS_PARAM;
 ++     rq.ocf    = OCF_READ_RSSI;
 ++     rq.cparam = &handle;
 ++     rq.clen   = 2;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_RSSI_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *rssi = rp.rssi;
 ++     return 0;
 ++}
 ++
 ++int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to)
 ++{
 ++     read_afh_map_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_STATUS_PARAM;
 ++     rq.ocf    = OCF_READ_AFH_MAP;
 ++     rq.cparam = &handle;
 ++     rq.clen   = 2;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_AFH_MAP_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *mode = rp.mode;
 ++     memcpy(map, rp.map, 10);
 ++     return 0;
 ++}
 ++
 ++int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to)
 ++{
 ++     read_clock_cp cp;
 ++     read_clock_rp rp;
 ++     struct hci_request rq;
 ++
 ++     memset(&cp, 0, sizeof(cp));
 ++     cp.handle      = handle;
 ++     cp.which_clock = which;
 ++
 ++     memset(&rq, 0, sizeof(rq));
 ++     rq.ogf    = OGF_STATUS_PARAM;
 ++     rq.ocf    = OCF_READ_CLOCK;
 ++     rq.cparam = &cp;
 ++     rq.clen   = READ_CLOCK_CP_SIZE;
 ++     rq.rparam = &rp;
 ++     rq.rlen   = READ_CLOCK_RP_SIZE;
 ++
 ++     if (hci_send_req(dd, &rq, to) < 0)
 ++             return -1;
 ++
 ++     if (rp.status) {
 ++             errno = EIO;
 ++             return -1;
 ++     }
 ++
 ++     *clock    = rp.clock;
 ++     *accuracy = rp.accuracy;
 ++     return 0;
 ++}
 ++
 ++int hci_local_name(int dd, int len, char *name, int to)
 ++{
 ++     return hci_read_local_name(dd, len, name, to);
 ++}
 ++
 ++int hci_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)
 ++{
 ++     return hci_read_remote_name(dd, bdaddr, len, name, to);
 ++}
diff --cc libs/src/sdp.c
index f8953c4,0000000,0000000..53554c8
mode 100644,000000,000000..100644
--- /dev/null
--- /dev/null
@@@@ -1,4421 -1,0 -1,0 +1,4421 @@@@
-  #include <sys/un.h>
 ++/*
 ++ *
 ++ *  BlueZ - Bluetooth protocol stack for Linux
 ++ *
 ++ *  Copyright (C) 2001-2002  Nokia Corporation
 ++ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 ++ *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
 ++ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
 ++ *
 ++ *
 ++ *  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 <config.h>
 ++#endif
 ++
 ++#include <stdio.h>
 ++#include <errno.h>
 ++#include <fcntl.h>
 ++#include <unistd.h>
 ++#include <stdlib.h>
 ++#include <limits.h>
 ++#include <string.h>
 ++#include <syslog.h>
-       { HANDSFREE_AGW_SVCLASS_ID,             "Handfree Audio Gateway"        },
 ++#include <sys/socket.h>
+++#include <sys/un.h>
 ++#include <sys/time.h>
 ++#include <sys/types.h>
 ++
 ++#include <bluetooth/bluetooth.h>
 ++#include <bluetooth/hci.h>
 ++#include <bluetooth/hci_lib.h>
 ++#include <bluetooth/l2cap.h>
 ++#include <bluetooth/sdp.h>
 ++#include <bluetooth/sdp_lib.h>
 ++
 ++#include <netinet/in.h>
 ++
 ++#define SDPINF(fmt, arg...) syslog(LOG_INFO, fmt "\n", ## arg)
 ++#define SDPERR(fmt, arg...) syslog(LOG_ERR, "%s: " fmt "\n", __func__ , ## arg)
 ++
 ++#ifdef SDP_DEBUG
 ++#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
 ++#else
 ++#define SDPDBG(fmt...)
 ++#endif
 ++
 ++#if __BYTE_ORDER == __BIG_ENDIAN
 ++#define ntoh64(x) (x)
 ++static inline void ntoh128(uint128_t *src, uint128_t *dst)
 ++{
 ++     int i;
 ++     for (i = 0; i < 16; i++)
 ++             dst->data[i] = src->data[i];
 ++}
 ++#else
 ++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;
 ++}
 ++static inline void ntoh128(uint128_t *src, uint128_t *dst)
 ++{
 ++     int i;
 ++     for (i = 0; i < 16; i++)
 ++             dst->data[15 - i] = src->data[i];
 ++}
 ++#endif
 ++
 ++#define hton64(x)     ntoh64(x)
 ++#define hton128(x, y) ntoh128(x, y)
 ++
 ++#define BASE_UUID "00000000-0000-1000-8000-00805F9B34FB"
 ++
 ++static uint128_t bluetooth_base_uuid = {
 ++     .data = {       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
 ++                     0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
 ++};
 ++
 ++#define SDP_MAX_ATTR_LEN 65535
 ++
 ++/* Message structure. */
 ++struct tupla {
 ++     int index;
 ++     char *str;
 ++};
 ++
 ++static struct tupla Protocol[] = {
 ++     { SDP_UUID,             "SDP"           },
 ++     { UDP_UUID,             "UDP"           },
 ++     { RFCOMM_UUID,          "RFCOMM"        },
 ++     { TCP_UUID,             "TCP"           },
 ++     { TCS_BIN_UUID,         "TCS-BIN"       },
 ++     { TCS_AT_UUID,          "TCS-AT"        },
 ++     { OBEX_UUID,            "OBEX"          },
 ++     { IP_UUID,              "IP"            },
 ++     { FTP_UUID,             "FTP"           },
 ++     { HTTP_UUID,            "HTTP"          },
 ++     { WSP_UUID,             "WSP"           },
 ++     { BNEP_UUID,            "BNEP"          },
 ++     { UPNP_UUID,            "UPNP"          },
 ++     { HIDP_UUID,            "HIDP"          },
 ++     { HCRP_CTRL_UUID,       "HCRP-Ctrl"     },
 ++     { HCRP_DATA_UUID,       "HCRP-Data"     },
 ++     { HCRP_NOTE_UUID,       "HCRP-Notify"   },
 ++     { AVCTP_UUID,           "AVCTP"         },
 ++     { AVDTP_UUID,           "AVDTP"         },
 ++     { CMTP_UUID,            "CMTP"          },
 ++     { UDI_UUID,             "UDI"           },
 ++     { MCAP_CTRL_UUID,       "MCAP-Ctrl"     },
 ++     { MCAP_DATA_UUID,       "MCAP-Data"     },
 ++     { L2CAP_UUID,           "L2CAP"         },
 ++     { 0 }
 ++};
 ++
 ++static struct tupla ServiceClass[] = {
 ++     { SDP_SERVER_SVCLASS_ID,                "SDP Server"                    },
 ++     { BROWSE_GRP_DESC_SVCLASS_ID,           "Browse Group Descriptor"       },
 ++     { PUBLIC_BROWSE_GROUP,                  "Public Browse Group"           },
 ++     { SERIAL_PORT_SVCLASS_ID,               "Serial Port"                   },
 ++     { LAN_ACCESS_SVCLASS_ID,                "LAN Access Using PPP"          },
 ++     { DIALUP_NET_SVCLASS_ID,                "Dialup Networking"             },
 ++     { IRMC_SYNC_SVCLASS_ID,                 "IrMC Sync"                     },
 ++     { OBEX_OBJPUSH_SVCLASS_ID,              "OBEX Object Push"              },
 ++     { OBEX_FILETRANS_SVCLASS_ID,            "OBEX File Transfer"            },
 ++     { IRMC_SYNC_CMD_SVCLASS_ID,             "IrMC Sync Command"             },
 ++     { HEADSET_SVCLASS_ID,                   "Headset"                       },
 ++     { CORDLESS_TELEPHONY_SVCLASS_ID,        "Cordless Telephony"            },
 ++     { AUDIO_SOURCE_SVCLASS_ID,              "Audio Source"                  },
 ++     { AUDIO_SINK_SVCLASS_ID,                "Audio Sink"                    },
 ++     { AV_REMOTE_TARGET_SVCLASS_ID,          "AV Remote Target"              },
 ++     { ADVANCED_AUDIO_SVCLASS_ID,            "Advanced Audio"                },
 ++     { AV_REMOTE_SVCLASS_ID,                 "AV Remote"                     },
 ++     { VIDEO_CONF_SVCLASS_ID,                "Video Conferencing"            },
 ++     { INTERCOM_SVCLASS_ID,                  "Intercom"                      },
 ++     { FAX_SVCLASS_ID,                       "Fax"                           },
 ++     { HEADSET_AGW_SVCLASS_ID,               "Headset Audio Gateway"         },
 ++     { WAP_SVCLASS_ID,                       "WAP"                           },
 ++     { WAP_CLIENT_SVCLASS_ID,                "WAP Client"                    },
 ++     { PANU_SVCLASS_ID,                      "PAN User"                      },
 ++     { NAP_SVCLASS_ID,                       "Network Access Point"          },
 ++     { GN_SVCLASS_ID,                        "PAN Group Network"             },
 ++     { DIRECT_PRINTING_SVCLASS_ID,           "Direct Printing"               },
 ++     { REFERENCE_PRINTING_SVCLASS_ID,        "Reference Printing"            },
 ++     { IMAGING_SVCLASS_ID,                   "Imaging"                       },
 ++     { IMAGING_RESPONDER_SVCLASS_ID,         "Imaging Responder"             },
 ++     { IMAGING_ARCHIVE_SVCLASS_ID,           "Imaging Automatic Archive"     },
 ++     { IMAGING_REFOBJS_SVCLASS_ID,           "Imaging Referenced Objects"    },
 ++     { HANDSFREE_SVCLASS_ID,                 "Handsfree"                     },
+++     { HANDSFREE_AGW_SVCLASS_ID,             "Handsfree Audio Gateway"       },
 ++     { DIRECT_PRT_REFOBJS_SVCLASS_ID,        "Direct Printing Ref. Objects"  },
 ++     { REFLECTED_UI_SVCLASS_ID,              "Reflected UI"                  },
 ++     { BASIC_PRINTING_SVCLASS_ID,            "Basic Printing"                },
 ++     { PRINTING_STATUS_SVCLASS_ID,           "Printing Status"               },
 ++     { HID_SVCLASS_ID,                       "Human Interface Device"        },
 ++     { HCR_SVCLASS_ID,                       "Hardcopy Cable Replacement"    },
 ++     { HCR_PRINT_SVCLASS_ID,                 "HCR Print"                     },
 ++     { HCR_SCAN_SVCLASS_ID,                  "HCR Scan"                      },
 ++     { CIP_SVCLASS_ID,                       "Common ISDN Access"            },
 ++     { VIDEO_CONF_GW_SVCLASS_ID,             "Video Conferencing Gateway"    },
 ++     { UDI_MT_SVCLASS_ID,                    "UDI MT"                        },
 ++     { UDI_TA_SVCLASS_ID,                    "UDI TA"                        },
 ++     { AV_SVCLASS_ID,                        "Audio/Video"                   },
 ++     { SAP_SVCLASS_ID,                       "SIM Access"                    },
 ++     { PBAP_PCE_SVCLASS_ID,                  "Phonebook Access - PCE"        },
 ++     { PBAP_PSE_SVCLASS_ID,                  "Phonebook Access - PSE"        },
 ++     { PBAP_SVCLASS_ID,                      "Phonebook Access"              },
 ++     { PNP_INFO_SVCLASS_ID,                  "PnP Information"               },
 ++     { GENERIC_NETWORKING_SVCLASS_ID,        "Generic Networking"            },
 ++     { GENERIC_FILETRANS_SVCLASS_ID,         "Generic File Transfer"         },
 ++     { GENERIC_AUDIO_SVCLASS_ID,             "Generic Audio"                 },
 ++     { GENERIC_TELEPHONY_SVCLASS_ID,         "Generic Telephony"             },
 ++     { UPNP_SVCLASS_ID,                      "UPnP"                          },
 ++     { UPNP_IP_SVCLASS_ID,                   "UPnP IP"                       },
 ++     { UPNP_PAN_SVCLASS_ID,                  "UPnP PAN"                      },
 ++     { UPNP_LAP_SVCLASS_ID,                  "UPnP LAP"                      },
 ++     { UPNP_L2CAP_SVCLASS_ID,                "UPnP L2CAP"                    },
 ++     { VIDEO_SOURCE_SVCLASS_ID,              "Video Source"                  },
 ++     { VIDEO_SINK_SVCLASS_ID,                "Video Sink"                    },
 ++     { VIDEO_DISTRIBUTION_SVCLASS_ID,        "Video Distribution"            },
 ++     { MDP_SVCLASS_ID,                       "MDP"                           },
 ++     { MDP_SOURCE_SVCLASS_ID,                "MDP Source"                    },
 ++     { MDP_SINK_SVCLASS_ID,                  "MDP Sink"                      },
 ++     { APPLE_AGENT_SVCLASS_ID,               "Apple Agent"                   },
 ++     { 0 }
 ++};
 ++
 ++#define Profile ServiceClass
 ++
 ++static char *string_lookup(struct tupla *pt0, int index)
 ++{
 ++     struct tupla *pt;
 ++
 ++     for (pt = pt0; pt->index; pt++)
 ++             if (pt->index == index)
 ++                     return pt->str;
 ++
 ++     return "";
 ++}
 ++
 ++static char *string_lookup_uuid(struct tupla *pt0, const uuid_t* uuid)
 ++{
 ++     uuid_t tmp_uuid;
 ++
 ++     memcpy(&tmp_uuid, uuid, sizeof(tmp_uuid));
 ++
 ++     if (sdp_uuid128_to_uuid(&tmp_uuid)) {
 ++             switch (tmp_uuid.type) {
 ++             case SDP_UUID16:
 ++                     return string_lookup(pt0, tmp_uuid.value.uuid16);
 ++             case SDP_UUID32:
 ++                     return string_lookup(pt0, tmp_uuid.value.uuid32);
 ++             }
 ++     }
 ++
 ++     return "";
 ++}
 ++
 ++/*
 ++ * Prints into a string the Protocol UUID
 ++ * coping a maximum of n characters.
 ++ */
 ++static int uuid2str(struct tupla *message, const uuid_t *uuid, char *str, size_t n) 
 ++{
 ++     char *str2;
 ++
 ++     if (!uuid) {
 ++             snprintf(str, n, "NULL");
 ++             return -2;
 ++     }
 ++
 ++     switch (uuid->type) {
 ++     case SDP_UUID16:
 ++             str2 = string_lookup(message, uuid->value.uuid16);
 ++             snprintf(str, n, str2);
 ++             break;
 ++     case SDP_UUID32:
 ++             str2 = string_lookup(message, uuid->value.uuid32);
 ++             snprintf(str, n, str2);
 ++             break;
 ++     case SDP_UUID128:
 ++             str2 = string_lookup_uuid(message, uuid);
 ++             snprintf(str, n, str2);
 ++             break;
 ++     default:
 ++             snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
 ++             return -1;
 ++     }
 ++
 ++     return 0;
 ++}
 ++
 ++int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n)
 ++{
 ++     return uuid2str(Protocol, uuid, str, n);
 ++}
 ++
 ++int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n)
 ++{
 ++     return uuid2str(ServiceClass, uuid, str, n);
 ++}
 ++
 ++int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n)
 ++{
 ++     return uuid2str(Profile, uuid, str, n);
 ++}
 ++
 ++/*
 ++ * convert the UUID to string, copying a maximum of n characters.
 ++ */
 ++int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n)
 ++{
 ++     if (!uuid) {
 ++             snprintf(str, n, "NULL");
 ++             return -2;
 ++     }
 ++     switch (uuid->type) {
 ++     case SDP_UUID16:
 ++             snprintf(str, n, "%.4x", uuid->value.uuid16);
 ++             break;
 ++     case SDP_UUID32:
 ++             snprintf(str, n, "%.8x", uuid->value.uuid32);
 ++             break;
 ++     case SDP_UUID128:{
 ++             unsigned int   data0;
 ++             unsigned short data1;
 ++             unsigned short data2;
 ++             unsigned short data3;
 ++             unsigned int   data4;
 ++             unsigned short data5;
 ++
 ++             memcpy(&data0, &uuid->value.uuid128.data[0], 4);
 ++             memcpy(&data1, &uuid->value.uuid128.data[4], 2);
 ++             memcpy(&data2, &uuid->value.uuid128.data[6], 2);
 ++             memcpy(&data3, &uuid->value.uuid128.data[8], 2);
 ++             memcpy(&data4, &uuid->value.uuid128.data[10], 4);
 ++             memcpy(&data5, &uuid->value.uuid128.data[14], 2);
 ++
 ++             snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", 
 ++                             ntohl(data0), ntohs(data1), 
 ++                             ntohs(data2), ntohs(data3), 
 ++                             ntohl(data4), ntohs(data5));
 ++             }
 ++             break;
 ++     default:
 ++             snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
 ++             return -1;      // Enum type of UUID not set
 ++     }
 ++     return 0;
 ++}
 ++
 ++#ifdef SDP_DEBUG
 ++/*
 ++ * Function prints the UUID in hex as per defined syntax -
 ++ *
 ++ * 4bytes-2bytes-2bytes-2bytes-6bytes
 ++ *
 ++ * There is some ugly code, including hardcoding, but
 ++ * that is just the way it is converting 16 and 32 bit
 ++ * UUIDs to 128 bit as defined in the SDP doc
 ++ */
 ++void sdp_uuid_print(const uuid_t *uuid)
 ++{
 ++     if (uuid == NULL) {
 ++             SDPERR("Null passed to print UUID\n");
 ++             return;
 ++     }
 ++     if (uuid->type == SDP_UUID16) {
 ++             SDPDBG("  uint16_t : 0x%.4x\n", uuid->value.uuid16);
 ++     } else if (uuid->type == SDP_UUID32) {
 ++             SDPDBG("  uint32_t : 0x%.8x\n", uuid->value.uuid32);
 ++     } else if (uuid->type == SDP_UUID128) {
 ++             unsigned int data0;
 ++             unsigned short data1;
 ++             unsigned short data2;
 ++             unsigned short data3;
 ++             unsigned int data4;
 ++             unsigned short data5;
 ++
 ++             memcpy(&data0, &uuid->value.uuid128.data[0], 4);
 ++             memcpy(&data1, &uuid->value.uuid128.data[4], 2);
 ++             memcpy(&data2, &uuid->value.uuid128.data[6], 2);
 ++             memcpy(&data3, &uuid->value.uuid128.data[8], 2);
 ++             memcpy(&data4, &uuid->value.uuid128.data[10], 4);
 ++             memcpy(&data5, &uuid->value.uuid128.data[14], 2);
 ++
 ++             SDPDBG("  uint128_t : 0x%.8x-", ntohl(data0));
 ++             SDPDBG("%.4x-", ntohs(data1));
 ++             SDPDBG("%.4x-", ntohs(data2));
 ++             SDPDBG("%.4x-", ntohs(data3));
 ++             SDPDBG("%.8x", ntohl(data4));
 ++             SDPDBG("%.4x\n", ntohs(data5));
 ++     } else
 ++             SDPERR("Enum type of UUID not set\n");
 ++}
 ++#endif
 ++
 ++sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length)
 ++{
 ++     sdp_data_t *seq;
 ++     sdp_data_t *d = malloc(sizeof(sdp_data_t));
 ++
 ++     if (!d)
 ++             return NULL;
 ++
 ++     memset(d, 0, sizeof(sdp_data_t));
 ++     d->dtd = dtd;
 ++     d->unitSize = sizeof(uint8_t);
 ++
 ++     switch (dtd) {
 ++     case SDP_DATA_NIL:
 ++             break;
 ++     case SDP_UINT8:
 ++             d->val.uint8 = *(uint8_t *) value;
 ++             d->unitSize += sizeof(uint8_t);
 ++             break;
 ++     case SDP_INT8:
 ++     case SDP_BOOL:
 ++             d->val.int8 = *(int8_t *) value;
 ++             d->unitSize += sizeof(int8_t);
 ++             break;
 ++     case SDP_UINT16:
 ++             d->val.uint16 = bt_get_unaligned((uint16_t *) value);
 ++             d->unitSize += sizeof(uint16_t);
 ++             break;
 ++     case SDP_INT16:
 ++             d->val.int16 = bt_get_unaligned((int16_t *) value);
 ++             d->unitSize += sizeof(int16_t);
 ++             break;
 ++     case SDP_UINT32:
 ++             d->val.uint32 = bt_get_unaligned((uint32_t *) value);
 ++             d->unitSize += sizeof(uint32_t);
 ++             break;
 ++     case SDP_INT32:
 ++             d->val.int32 = bt_get_unaligned((int32_t *) value);
 ++             d->unitSize += sizeof(int32_t);
 ++             break;
 ++     case SDP_INT64:
 ++             d->val.int64 = bt_get_unaligned((int64_t *) value);
 ++             d->unitSize += sizeof(int64_t);
 ++             break;
 ++     case SDP_UINT64:
 ++             d->val.uint64 = bt_get_unaligned((uint64_t *) value);
 ++             d->unitSize += sizeof(uint64_t);
 ++             break;
 ++     case SDP_UINT128:
 ++             memcpy(&d->val.uint128.data, value, sizeof(uint128_t));
 ++             d->unitSize += sizeof(uint128_t);
 ++             break;
 ++     case SDP_INT128:
 ++             memcpy(&d->val.int128.data, value, sizeof(uint128_t));
 ++             d->unitSize += sizeof(uint128_t);
 ++             break;
 ++     case SDP_UUID16:
 ++             sdp_uuid16_create(&d->val.uuid, bt_get_unaligned((uint16_t *) value));
 ++             d->unitSize += sizeof(uint16_t);
 ++             break;
 ++     case SDP_UUID32:
 ++             sdp_uuid32_create(&d->val.uuid, bt_get_unaligned((uint32_t *) value));
 ++             d->unitSize += sizeof(uint32_t);
 ++             break;
 ++     case SDP_UUID128:
 ++             sdp_uuid128_create(&d->val.uuid, value);
 ++             d->unitSize += sizeof(uint128_t);
 ++             break;
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++             if (!value) {
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++
 ++             d->unitSize += length;
 ++             if (length <= USHRT_MAX) {
 ++                     d->val.str = malloc(length);
 ++                     if (!d->val.str) {
 ++                             free(d);
 ++                             return NULL;
 ++                     }
 ++
 ++                     memcpy(d->val.str, value, length);
 ++             } else {
 ++                     SDPERR("Strings of size > USHRT_MAX not supported\n");
 ++                     free(d);
 ++                     d = NULL;
 ++             }
 ++             break;
 ++     case SDP_URL_STR32:
 ++     case SDP_TEXT_STR32:
 ++             SDPERR("Strings of size > USHRT_MAX not supported\n");
 ++             break;
 ++     case SDP_ALT8:
 ++     case SDP_ALT16:
 ++     case SDP_ALT32:
 ++     case SDP_SEQ8:
 ++     case SDP_SEQ16:
 ++     case SDP_SEQ32:
 ++             if (dtd == SDP_ALT8 || dtd == SDP_SEQ8)
 ++                     d->unitSize += sizeof(uint8_t);
 ++             else if (dtd == SDP_ALT16 || dtd == SDP_SEQ16)
 ++                     d->unitSize += sizeof(uint16_t);
 ++             else if (dtd == SDP_ALT32 || dtd == SDP_SEQ32)
 ++                     d->unitSize += sizeof(uint32_t);
 ++             seq = (sdp_data_t *)value;
 ++             d->val.dataseq = seq;
 ++             for (; seq; seq = seq->next)
 ++                     d->unitSize += seq->unitSize;
 ++             break;
 ++     default:
 ++             free(d);
 ++             d = NULL;
 ++     }
 ++
 ++     return d;
 ++}
 ++
 ++sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value)
 ++{
 ++     uint32_t length;
 ++
 ++     switch (dtd) {
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++             if (!value)
 ++                     return NULL;
 ++
 ++             length = strlen((char *) value);
 ++             break;
 ++     default:
 ++             length = 0;
 ++             break;
 ++     }
 ++
 ++     return sdp_data_alloc_with_length(dtd, value, length);
 ++}
 ++
 ++sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *d)
 ++{
 ++     if (seq) {
 ++             sdp_data_t *p;
 ++             for (p = seq; p->next; p = p->next);
 ++             p->next = d;
 ++     } else
 ++             seq = d;
 ++     d->next = NULL;
 ++     return seq;
 ++}
 ++
 ++sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len)
 ++{
 ++     sdp_data_t *curr = NULL, *seq = NULL;
 ++     int i;
 ++
 ++     for (i = 0; i < len; i++) {
 ++             sdp_data_t *data;
 ++             int8_t dtd = *(uint8_t *) dtds[i];
 ++
 ++             if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
 ++                     data = (sdp_data_t *) values[i];
 ++             else
 ++                     data = sdp_data_alloc_with_length(dtd, values[i], length[i]);
 ++
 ++             if (!data)
 ++                     return NULL;
 ++
 ++             if (curr)
 ++                     curr->next = data;
 ++             else
 ++                     seq = data;
 ++
 ++             curr = data;
 ++     }
 ++
 ++     return sdp_data_alloc_with_length(SDP_SEQ8, seq, length[i]);
 ++}
 ++
 ++sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len)
 ++{
 ++     sdp_data_t *curr = NULL, *seq = NULL;
 ++     int i;
 ++
 ++     for (i = 0; i < len; i++) {
 ++             sdp_data_t *data;
 ++             uint8_t dtd = *(uint8_t *) dtds[i];
 ++
 ++             if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
 ++                     data = (sdp_data_t *) values[i];
 ++             else
 ++                     data = sdp_data_alloc(dtd, values[i]);
 ++
 ++             if (!data)
 ++                     return NULL;
 ++
 ++             if (curr)
 ++                     curr->next = data;
 ++             else
 ++                     seq = data;
 ++
 ++             curr = data;
 ++     }
 ++
 ++     return sdp_data_alloc(SDP_SEQ8, seq);
 ++}
 ++
 ++static void extract_svclass_uuid(sdp_data_t *data, uuid_t *uuid)
 ++{
 ++     sdp_data_t *d;
 ++
 ++     if (!data || data->dtd < SDP_SEQ8 || data->dtd > SDP_SEQ32)
 ++             return;
 ++
 ++     d = data->val.dataseq;
 ++     if (!d)
 ++             return;
 ++
 ++     if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128)
 ++             return;
 ++
 ++     *uuid = d->val.uuid;
 ++}
 ++
 ++int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
 ++{
 ++     sdp_data_t *p = sdp_data_get(rec, attr);
 ++
 ++     if (p)
 ++             return -1;
 ++
 ++     d->attrId = attr;
 ++     rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
 ++
 ++     if (attr == SDP_ATTR_SVCLASS_ID_LIST)
 ++             extract_svclass_uuid(d, &rec->svclass);
 ++
 ++     return 0;
 ++}
 ++
 ++void sdp_attr_remove(sdp_record_t *rec, uint16_t attr)
 ++{
 ++     sdp_data_t *d = sdp_data_get(rec, attr);
 ++
 ++     if (d)
 ++             rec->attrlist = sdp_list_remove(rec->attrlist, d);
 ++
 ++     if (attr == SDP_ATTR_SVCLASS_ID_LIST)
 ++             memset(&rec->svclass, 0, sizeof(rec->svclass));
 ++}
 ++
 ++void sdp_set_seq_len(uint8_t *ptr, uint32_t length)
 ++{
 ++     uint8_t dtd = *(uint8_t *) ptr++;
 ++
 ++     switch (dtd) {
 ++     case SDP_SEQ8:
 ++     case SDP_ALT8:
 ++     case SDP_TEXT_STR8:
 ++     case SDP_URL_STR8:
 ++             *(uint8_t *)ptr = (uint8_t) length;
 ++             break;
 ++     case SDP_SEQ16:
 ++     case SDP_ALT16:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_URL_STR16:
 ++             bt_put_unaligned(htons(length), (uint16_t *) ptr);
 ++             break;
 ++     case SDP_SEQ32:
 ++     case SDP_ALT32:
 ++     case SDP_TEXT_STR32:
 ++     case SDP_URL_STR32:
 ++             bt_put_unaligned(htonl(length), (uint32_t *) ptr);
 ++             break;
 ++     }
 ++}
 ++
 ++int sdp_set_data_type(sdp_buf_t *buf, uint8_t dtd)
 ++{
 ++     int orig = buf->data_size;
 ++     uint8_t *p = buf->data + buf->data_size;
 ++
 ++     *p++ = dtd;
 ++     buf->data_size += sizeof(uint8_t);
 ++
 ++     switch (dtd) {
 ++     case SDP_SEQ8:
 ++     case SDP_TEXT_STR8:
 ++     case SDP_URL_STR8:
 ++     case SDP_ALT8:
 ++             buf->data_size += sizeof(uint8_t);
 ++             break;
 ++     case SDP_SEQ16:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_URL_STR16:
 ++     case SDP_ALT16:
 ++             buf->data_size += sizeof(uint16_t);
 ++             break;
 ++     case SDP_SEQ32:
 ++     case SDP_TEXT_STR32:
 ++     case SDP_URL_STR32:
 ++     case SDP_ALT32:
 ++             buf->data_size += sizeof(uint32_t);
 ++             break;
 ++     }
 ++
 ++     return buf->data_size - orig;
 ++}
 ++
 ++void sdp_set_attrid(sdp_buf_t *buf, uint16_t attr)
 ++{
 ++     uint8_t *p = buf->data;
 ++
 ++     // data type for attr
 ++     *p++ = SDP_UINT16;
 ++     buf->data_size = sizeof(uint8_t);
 ++     bt_put_unaligned(htons(attr), (uint16_t *) p);
 ++     p += sizeof(uint16_t);
 ++     buf->data_size += sizeof(uint16_t);
 ++}
 ++
 ++static int get_data_size(sdp_buf_t *buf, sdp_data_t *sdpdata)
 ++{
 ++     sdp_data_t *d;
 ++     int n = 0;
 ++
 ++     for (d = sdpdata->val.dataseq; d; d = d->next)
 ++             n += sdp_gen_pdu(buf, d);
 ++
 ++     return n;
 ++}
 ++
 ++int sdp_gen_pdu(sdp_buf_t *buf, sdp_data_t *d)
 ++{
 ++     uint32_t pdu_size = 0, data_size = 0;
 ++     unsigned char *src = NULL, is_seq = 0, is_alt = 0;
 ++     uint8_t dtd = d->dtd;
 ++     uint16_t u16;
 ++     uint32_t u32;
 ++     uint64_t u64;
 ++     uint128_t u128;
 ++     uint8_t *seqp = buf->data + buf->data_size;
 ++
 ++     pdu_size = sdp_set_data_type(buf, dtd);
 ++
 ++     switch (dtd) {
 ++     case SDP_DATA_NIL:
 ++             break;
 ++     case SDP_UINT8:
 ++             src = &d->val.uint8;
 ++             data_size = sizeof(uint8_t);
 ++             break;
 ++     case SDP_UINT16:
 ++             u16 = htons(d->val.uint16);
 ++             src = (unsigned char *) &u16;
 ++             data_size = sizeof(uint16_t);
 ++             break;
 ++     case SDP_UINT32:
 ++             u32 = htonl(d->val.uint32);
 ++             src = (unsigned char *) &u32;
 ++             data_size = sizeof(uint32_t);
 ++             break;
 ++     case SDP_UINT64:
 ++             u64 = hton64(d->val.uint64);
 ++             src = (unsigned char *) &u64;
 ++             data_size = sizeof(uint64_t);
 ++             break;
 ++     case SDP_UINT128:
 ++             hton128(&d->val.uint128, &u128);
 ++             src = (unsigned char *) &u128;
 ++             data_size = sizeof(uint128_t);
 ++             break;
 ++     case SDP_INT8:
 ++     case SDP_BOOL:
 ++             src = (unsigned char *) &d->val.int8;
 ++             data_size = sizeof(int8_t);
 ++             break;
 ++     case SDP_INT16:
 ++             u16 = htons(d->val.int16);
 ++             src = (unsigned char *) &u16;
 ++             data_size = sizeof(int16_t);
 ++             break;
 ++     case SDP_INT32:
 ++             u32 = htonl(d->val.int32);
 ++             src = (unsigned char *) &u32;
 ++             data_size = sizeof(int32_t);
 ++             break;
 ++     case SDP_INT64:
 ++             u64 = hton64(d->val.int64);
 ++             src = (unsigned char *) &u64;
 ++             data_size = sizeof(int64_t);
 ++             break;
 ++     case SDP_INT128:
 ++             hton128(&d->val.int128, &u128);
 ++             src = (unsigned char *) &u128;
 ++             data_size = sizeof(uint128_t);
 ++             break;
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_TEXT_STR32:
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_URL_STR32:
 ++             src = (unsigned char *) d->val.str;
 ++             data_size = d->unitSize - sizeof(uint8_t);
 ++             sdp_set_seq_len(seqp, data_size);
 ++             break;
 ++     case SDP_SEQ8:
 ++     case SDP_SEQ16:
 ++     case SDP_SEQ32:
 ++             is_seq = 1;
 ++             data_size = get_data_size(buf, d);
 ++             sdp_set_seq_len(seqp, data_size);
 ++             break;
 ++     case SDP_ALT8:
 ++     case SDP_ALT16:
 ++     case SDP_ALT32:
 ++             is_alt = 1;
 ++             data_size = get_data_size(buf, d);
 ++             sdp_set_seq_len(seqp, data_size);
 ++             break;
 ++     case SDP_UUID16:
 ++             u16 = htons(d->val.uuid.value.uuid16);
 ++             src = (unsigned char *) &u16;
 ++             data_size = sizeof(uint16_t);
 ++             break;
 ++     case SDP_UUID32:
 ++             u32 = htonl(d->val.uuid.value.uuid32);
 ++             src = (unsigned char *) &u32;
 ++             data_size = sizeof(uint32_t);
 ++             break;
 ++     case SDP_UUID128:
 ++             src = (unsigned char *) &d->val.uuid.value.uuid128;
 ++             data_size = sizeof(uint128_t);
 ++             break;
 ++     default:
 ++             break;
 ++     }
 ++
 ++     if (!is_seq && !is_alt) {
 ++             if (src && buf) {
 ++                     memcpy(buf->data + buf->data_size, src, data_size);
 ++                     buf->data_size += data_size;
 ++             } else if (dtd != SDP_DATA_NIL)
 ++                     SDPDBG("Gen PDU : Cant copy from NULL source or dest\n");
 ++     }
 ++
 ++     pdu_size += data_size;
 ++
 ++     return pdu_size;
 ++}
 ++
 ++static void sdp_attr_pdu(void *value, void *udata)
 ++{
 ++     sdp_append_to_pdu((sdp_buf_t *)udata, (sdp_data_t *)value);
 ++}
 ++
 ++int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *buf)
 ++{
 ++     buf->data = malloc(SDP_PDU_CHUNK_SIZE);
 ++     if (!buf->data)
 ++             return -ENOMEM;
 ++
 ++     buf->buf_size = SDP_PDU_CHUNK_SIZE;
 ++     buf->data_size = 0;
 ++     memset(buf->data, 0, buf->buf_size);
 ++     sdp_list_foreach(rec->attrlist, sdp_attr_pdu, buf);
 ++
 ++     return 0;
 ++}
 ++
 ++void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
 ++{
 ++     sdp_data_t *p = sdp_data_get(rec, attr);
 ++
 ++     if (p) {
 ++             rec->attrlist = sdp_list_remove(rec->attrlist, p);
 ++             sdp_data_free(p);
 ++     }
 ++
 ++     d->attrId = attr;
 ++     rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
 ++
 ++     if (attr == SDP_ATTR_SVCLASS_ID_LIST)
 ++             extract_svclass_uuid(d, &rec->svclass);
 ++}
 ++
 ++int sdp_attrid_comp_func(const void *key1, const void *key2)
 ++{
 ++     const sdp_data_t *d1 = (const sdp_data_t *)key1;
 ++     const sdp_data_t *d2 = (const sdp_data_t *)key2;
 ++
 ++     if (d1 && d2)
 ++             return d1->attrId - d2->attrId;
 ++     return 0;
 ++}
 ++
 ++static void data_seq_free(sdp_data_t *seq)
 ++{
 ++     sdp_data_t *d = seq->val.dataseq;
 ++
 ++     while (d) {
 ++             sdp_data_t *next = d->next;
 ++             sdp_data_free(d);
 ++             d = next;
 ++     }
 ++}
 ++
 ++void sdp_data_free(sdp_data_t *d)
 ++{
 ++     switch (d->dtd) {
 ++     case SDP_SEQ8:
 ++     case SDP_SEQ16:
 ++     case SDP_SEQ32:
 ++             data_seq_free(d);
 ++             break;
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_URL_STR32:
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_TEXT_STR32:
 ++             free(d->val.str);
 ++             break;
 ++     }
 ++     free(d);
 ++}
 ++
 ++int sdp_uuid_extract_safe(const uint8_t *p, int bufsize, uuid_t *uuid, int *scanned)
 ++{
 ++     uint8_t type;
 ++
 ++     if (bufsize < sizeof(uint8_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             return -1;
 ++     }
 ++
 ++     type = *(const uint8_t *) p;
 ++
 ++     if (!SDP_IS_UUID(type)) {
 ++             SDPERR("Unknown data type : %d expecting a svc UUID\n", type);
 ++             return -1;
 ++     }
 ++     p += sizeof(uint8_t);
 ++     *scanned += sizeof(uint8_t);
 ++     bufsize -= sizeof(uint8_t);
 ++     if (type == SDP_UUID16) {
 ++             if (bufsize < sizeof(uint16_t)) {
 ++                     SDPERR("Not enough room for 16-bit UUID");
 ++                     return -1;
 ++             }
 ++             sdp_uuid16_create(uuid, ntohs(bt_get_unaligned((uint16_t *) p)));
 ++             *scanned += sizeof(uint16_t);
 ++             p += sizeof(uint16_t);
 ++     } else if (type == SDP_UUID32) {
 ++             if (bufsize < sizeof(uint32_t)) {
 ++                     SDPERR("Not enough room for 32-bit UUID");
 ++                     return -1;
 ++             }
 ++             sdp_uuid32_create(uuid, ntohl(bt_get_unaligned((uint32_t *) p)));
 ++             *scanned += sizeof(uint32_t);
 ++             p += sizeof(uint32_t);
 ++     } else {
 ++             if (bufsize < sizeof(uint128_t)) {
 ++                     SDPERR("Not enough room for 128-bit UUID");
 ++                     return -1;
 ++             }
 ++             sdp_uuid128_create(uuid, p);
 ++             *scanned += sizeof(uint128_t);
 ++             p += sizeof(uint128_t);
 ++     }
 ++     return 0;
 ++}
 ++
 ++int sdp_uuid_extract(const uint8_t *p, uuid_t *uuid, int *scanned)
 ++{
 ++     /* Assume p points to a buffer of size at least SDP_MAX_ATTR_LEN,
 ++        because we don't have any better information */
 ++     return sdp_uuid_extract_safe(p, SDP_MAX_ATTR_LEN, uuid, scanned);
 ++}
 ++
 ++static sdp_data_t *extract_int(const void *p, int bufsize, int *len)
 ++{
 ++     sdp_data_t *d;
 ++
 ++     if (bufsize < sizeof(uint8_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             return NULL;
 ++     }
 ++
 ++     d = malloc(sizeof(sdp_data_t));
 ++
 ++     SDPDBG("Extracting integer\n");
 ++     memset(d, 0, sizeof(sdp_data_t));
 ++     d->dtd = *(uint8_t *) p;
 ++     p += sizeof(uint8_t);
 ++     *len += sizeof(uint8_t);
 ++     bufsize -= sizeof(uint8_t);
 ++
 ++     switch (d->dtd) {
 ++     case SDP_DATA_NIL:
 ++             break;
 ++     case SDP_BOOL:
 ++     case SDP_INT8:
 ++     case SDP_UINT8:
 ++             if (bufsize < sizeof(uint8_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             *len += sizeof(uint8_t);
 ++             d->val.uint8 = *(uint8_t *) p;
 ++             break;
 ++     case SDP_INT16:
 ++     case SDP_UINT16:
 ++             if (bufsize < sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             *len += sizeof(uint16_t);
 ++             d->val.uint16 = ntohs(bt_get_unaligned((uint16_t *) p));
 ++             break;
 ++     case SDP_INT32:
 ++     case SDP_UINT32:
 ++             if (bufsize < sizeof(uint32_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             *len += sizeof(uint32_t);
 ++             d->val.uint32 = ntohl(bt_get_unaligned((uint32_t *) p));
 ++             break;
 ++     case SDP_INT64:
 ++     case SDP_UINT64:
 ++             if (bufsize < sizeof(uint64_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             *len += sizeof(uint64_t);
 ++             d->val.uint64 = ntoh64(bt_get_unaligned((uint64_t *) p));
 ++             break;
 ++     case SDP_INT128:
 ++     case SDP_UINT128:
 ++             if (bufsize < sizeof(uint128_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             *len += sizeof(uint128_t);
 ++             ntoh128((uint128_t *) p, &d->val.uint128);
 ++             break;
 ++     default:
 ++             free(d);
 ++             d = NULL;
 ++     }
 ++     return d;
 ++}
 ++
 ++static sdp_data_t *extract_uuid(const uint8_t *p, int bufsize, int *len, sdp_record_t *rec)
 ++{
 ++     sdp_data_t *d = malloc(sizeof(sdp_data_t));
 ++
 ++     SDPDBG("Extracting UUID");
 ++     memset(d, 0, sizeof(sdp_data_t));
 ++     if (sdp_uuid_extract_safe(p, bufsize, &d->val.uuid, len) < 0) {
 ++             free(d);
 ++             return NULL;
 ++     }
 ++     d->dtd = *(uint8_t *) p;
 ++     if (rec)
 ++             sdp_pattern_add_uuid(rec, &d->val.uuid);
 ++     return d;
 ++}
 ++
 ++/*
 ++ * Extract strings from the PDU (could be service description and similar info) 
 ++ */
 ++static sdp_data_t *extract_str(const void *p, int bufsize, int *len)
 ++{
 ++     char *s;
 ++     int n;
 ++     sdp_data_t *d;
 ++
 ++     if (bufsize < sizeof(uint8_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             return NULL;
 ++     }
 ++
 ++     d = malloc(sizeof(sdp_data_t));
 ++
 ++     memset(d, 0, sizeof(sdp_data_t));
 ++     d->dtd = *(uint8_t *) p;
 ++     p += sizeof(uint8_t);
 ++     *len += sizeof(uint8_t);
 ++     bufsize -= sizeof(uint8_t);
 ++
 ++     switch (d->dtd) {
 ++     case SDP_TEXT_STR8:
 ++     case SDP_URL_STR8:
 ++             if (bufsize < sizeof(uint8_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             n = *(uint8_t *) p;
 ++             p += sizeof(uint8_t);
 ++             *len += sizeof(uint8_t);
 ++             bufsize -= sizeof(uint8_t);
 ++             break;
 ++     case SDP_TEXT_STR16:
 ++     case SDP_URL_STR16:
 ++             if (bufsize < sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     free(d);
 ++                     return NULL;
 ++             }
 ++             n = ntohs(bt_get_unaligned((uint16_t *) p));
 ++             p += sizeof(uint16_t);
 ++             *len += sizeof(uint16_t) + n;
 ++             bufsize -= sizeof(uint16_t);
 ++             break;
 ++     default:
 ++             SDPERR("Sizeof text string > UINT16_MAX\n");
 ++             free(d);
 ++             return 0;
 ++     }
 ++
 ++     if (bufsize < n) {
 ++             SDPERR("String too long to fit in packet");
 ++             free(d);
 ++             return NULL;
 ++     }
 ++
 ++     s = malloc(n + 1);
 ++     if (!s) {
 ++             SDPERR("Not enough memory for incoming string");
 ++             free(d);
 ++             return NULL;
 ++     }
 ++     memset(s, 0, n + 1);
 ++     memcpy(s, p, n);
 ++
 ++     *len += n;
 ++
 ++     SDPDBG("Len : %d\n", n);
 ++     SDPDBG("Str : %s\n", s);
 ++
 ++     d->val.str = s;
 ++     d->unitSize = n + sizeof(uint8_t);
 ++     return d;
 ++}
 ++
 ++/*
 ++ * Extract the sequence type and its length, and return offset into buf
 ++ * or 0 on failure.
 ++ */
 ++int sdp_extract_seqtype_safe(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size)
 ++{
 ++     uint8_t dtd;
 ++     int scanned = sizeof(uint8_t);
 ++
 ++     if (bufsize < sizeof(uint8_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             return 0;
 ++     }
 ++
 ++     dtd = *(uint8_t *) buf;
 ++     buf += sizeof(uint8_t);
 ++     bufsize -= sizeof(uint8_t);
 ++     *dtdp = dtd;
 ++     switch (dtd) {
 ++     case SDP_SEQ8:
 ++     case SDP_ALT8:
 ++             if (bufsize < sizeof(uint8_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     return 0;
 ++             }
 ++             *size = *(uint8_t *) buf;
 ++             scanned += sizeof(uint8_t);
 ++             break;
 ++     case SDP_SEQ16:
 ++     case SDP_ALT16:
 ++             if (bufsize < sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     return 0;
 ++             }
 ++             *size = ntohs(bt_get_unaligned((uint16_t *) buf));
 ++             scanned += sizeof(uint16_t);
 ++             break;
 ++     case SDP_SEQ32:
 ++     case SDP_ALT32:
 ++             if (bufsize < sizeof(uint32_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     return 0;
 ++             }
 ++             *size = ntohl(bt_get_unaligned((uint32_t *) buf));
 ++             scanned += sizeof(uint32_t);
 ++             break;
 ++     default:
 ++             SDPERR("Unknown sequence type, aborting\n");
 ++             return 0;
 ++     }
 ++     return scanned;
 ++}
 ++
 ++int sdp_extract_seqtype(const uint8_t *buf, uint8_t *dtdp, int *size)
 ++{
 ++     /* Assume buf points to a buffer of size at least SDP_MAX_ATTR_LEN,
 ++        because we don't have any better information */
 ++     return sdp_extract_seqtype_safe(buf, SDP_MAX_ATTR_LEN, dtdp, size);
 ++}
 ++
 ++static sdp_data_t *extract_seq(const void *p, int bufsize, int *len, sdp_record_t *rec)
 ++{
 ++     int seqlen, n = 0;
 ++     sdp_data_t *curr, *prev;
 ++     sdp_data_t *d = malloc(sizeof(sdp_data_t));
 ++
 ++     SDPDBG("Extracting SEQ");
 ++     memset(d, 0, sizeof(sdp_data_t));
 ++     *len = sdp_extract_seqtype_safe(p, bufsize, &d->dtd, &seqlen);
 ++     SDPDBG("Sequence Type : 0x%x length : 0x%x\n", d->dtd, seqlen);
 ++
 ++     if (*len == 0)
 ++             return d;
 ++
 ++     if (*len > bufsize) {
 ++             SDPERR("Packet not big enough to hold sequence.");
 ++             free(d);
 ++             return NULL;
 ++     }
 ++
 ++     p += *len;
 ++     bufsize -= *len;
 ++     curr = prev = NULL;
 ++     while (n < seqlen) {
 ++             int attrlen = 0;
 ++             curr = sdp_extract_attr_safe(p, bufsize, &attrlen, rec);
 ++             if (curr == NULL)
 ++                     break;
 ++
 ++             if (prev)
 ++                     prev->next = curr;
 ++             else
 ++                     d->val.dataseq = curr;
 ++             prev = curr;
 ++             p += attrlen;
 ++             n += attrlen;
 ++             bufsize -= attrlen;
 ++
 ++             SDPDBG("Extracted: %d SequenceLength: %d", n, seqlen);
 ++     }
 ++
 ++     *len += n;
 ++     return d;
 ++}
 ++
 ++sdp_data_t *sdp_extract_attr_safe(const uint8_t *p, int bufsize, int *size, sdp_record_t *rec)
 ++{
 ++     sdp_data_t *elem;
 ++     int n = 0;
 ++     uint8_t dtd;
 ++
 ++     if (bufsize < sizeof(uint8_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             return NULL;
 ++     }
 ++
 ++     dtd = *(const uint8_t *)p;
 ++
 ++     SDPDBG("extract_attr: dtd=0x%x", dtd);
 ++     switch (dtd) {
 ++     case SDP_DATA_NIL:
 ++     case SDP_BOOL:
 ++     case SDP_UINT8:
 ++     case SDP_UINT16:
 ++     case SDP_UINT32:
 ++     case SDP_UINT64:
 ++     case SDP_UINT128:
 ++     case SDP_INT8:
 ++     case SDP_INT16:
 ++     case SDP_INT32:
 ++     case SDP_INT64:
 ++     case SDP_INT128:
 ++             elem = extract_int(p, bufsize, &n);
 ++             break;
 ++     case SDP_UUID16:
 ++     case SDP_UUID32:
 ++     case SDP_UUID128:
 ++             elem = extract_uuid(p, bufsize, &n, rec);
 ++             break;
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_TEXT_STR32:
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_URL_STR32:
 ++             elem = extract_str(p, bufsize, &n);
 ++             break;
 ++     case SDP_SEQ8:
 ++     case SDP_SEQ16:
 ++     case SDP_SEQ32:
 ++     case SDP_ALT8:
 ++     case SDP_ALT16:
 ++     case SDP_ALT32:
 ++             elem = extract_seq(p, bufsize, &n, rec);
 ++             break;
 ++     default:
 ++             SDPERR("Unknown data descriptor : 0x%x terminating\n", dtd);
 ++             return NULL;
 ++     }
 ++     *size += n;
 ++     return elem;
 ++}
 ++
 ++sdp_data_t *sdp_extract_attr(const uint8_t *p, int *size, sdp_record_t *rec)
 ++{
 ++     /* Assume p points to a buffer of size at least SDP_MAX_ATTR_LEN,
 ++        because we don't have any better information */
 ++     return sdp_extract_attr_safe(p, SDP_MAX_ATTR_LEN, size, rec);
 ++}
 ++
 ++#ifdef SDP_DEBUG
 ++static void attr_print_func(void *value, void *userData)
 ++{
 ++     sdp_data_t *d = (sdp_data_t *)value;
 ++
 ++     SDPDBG("=====================================\n");
 ++     SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x\n",  d->attrId);
 ++     SDPDBG("ATTRIBUTE VALUE PTR : 0x%x\n", (uint32_t)value);
 ++     if (d)
 ++             sdp_data_print(d);
 ++     else
 ++             SDPDBG("NULL value\n");
 ++     SDPDBG("=====================================\n");
 ++}
 ++
 ++void sdp_print_service_attr(sdp_list_t *svcAttrList)
 ++{
 ++     SDPDBG("Printing service attr list %p\n", svcAttrList);
 ++     sdp_list_foreach(svcAttrList, attr_print_func, NULL);
 ++     SDPDBG("Printed service attr list %p\n", svcAttrList);
 ++}
 ++#endif
 ++
 ++sdp_record_t *sdp_extract_pdu_safe(const uint8_t *buf, int bufsize, int *scanned)
 ++{
 ++     int extracted = 0, seqlen = 0;
 ++     uint8_t dtd;
 ++     uint16_t attr;
 ++     sdp_record_t *rec = sdp_record_alloc();
 ++     const uint8_t *p = buf;
 ++
 ++     *scanned = sdp_extract_seqtype_safe(buf, bufsize, &dtd, &seqlen);
 ++     p += *scanned;
 ++     bufsize -= *scanned;
 ++     rec->attrlist = NULL;
 ++
 ++     while (extracted < seqlen && bufsize > 0) {
 ++             int n = sizeof(uint8_t), attrlen = 0;
 ++             sdp_data_t *data = NULL;
 ++
 ++             SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
 ++                                                     seqlen, extracted);
 ++
 ++             if (bufsize < n + sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     break;
 ++             }
 ++
 ++             dtd = *(uint8_t *) p;
 ++             attr = ntohs(bt_get_unaligned((uint16_t *) (p + n)));
 ++             n += sizeof(uint16_t);
 ++
 ++             SDPDBG("DTD of attrId : %d Attr id : 0x%x \n", dtd, attr);
 ++
 ++             data = sdp_extract_attr_safe(p + n, bufsize - n, &attrlen, rec);
 ++
 ++             SDPDBG("Attr id : 0x%x attrValueLength : %d\n", attr, attrlen);
 ++
 ++             n += attrlen;
 ++             if (data == NULL) {
 ++                     SDPDBG("Terminating extraction of attributes");
 ++                     break;
 ++             }
 ++
 ++             if (attr == SDP_ATTR_RECORD_HANDLE)
 ++                     rec->handle = data->val.uint32;
 ++
 ++             if (attr == SDP_ATTR_SVCLASS_ID_LIST)
 ++                     extract_svclass_uuid(data, &rec->svclass);
 ++
 ++             extracted += n;
 ++             p += n;
 ++             bufsize -= n;
 ++             sdp_attr_replace(rec, attr, data);
 ++
 ++             SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
 ++                                                     seqlen, extracted);
 ++     }
 ++#ifdef SDP_DEBUG
 ++     SDPDBG("Successful extracting of Svc Rec attributes\n");
 ++     sdp_print_service_attr(rec->attrlist);
 ++#endif
 ++     *scanned += seqlen;
 ++     return rec;
 ++}
 ++
 ++sdp_record_t *sdp_extract_pdu(const uint8_t *buf, int *scanned)
 ++{
 ++     /* Assume buf points to a buffer of size at least SDP_MAX_ATTR_LEN,
 ++        because we don't have any better information */
 ++     return sdp_extract_pdu_safe(buf, SDP_MAX_ATTR_LEN, scanned);
 ++}
 ++
 ++#ifdef SDP_DEBUG
 ++static void print_dataseq(sdp_data_t *p)
 ++{
 ++     sdp_data_t *d;
 ++
 ++     for (d = p; d; d = d->next)
 ++             sdp_data_print(d);
 ++}
 ++#endif
 ++
 ++void sdp_record_print(const sdp_record_t *rec)
 ++{
 ++     sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
 ++     if (d)
 ++             printf("Service Name: %.*s\n", d->unitSize, d->val.str);
 ++     d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
 ++     if (d)
 ++             printf("Service Description: %.*s\n", d->unitSize, d->val.str);
 ++     d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
 ++     if (d)
 ++             printf("Service Provider: %.*s\n", d->unitSize, d->val.str);
 ++}
 ++
 ++#ifdef SDP_DEBUG
 ++void sdp_data_print(sdp_data_t *d)
 ++{
 ++     switch (d->dtd) {
 ++     case SDP_DATA_NIL:
 ++             SDPDBG("NIL\n");
 ++             break;
 ++     case SDP_BOOL:
 ++     case SDP_UINT8:
 ++     case SDP_UINT16:
 ++     case SDP_UINT32:
 ++     case SDP_UINT64:
 ++     case SDP_UINT128:
 ++     case SDP_INT8:
 ++     case SDP_INT16:
 ++     case SDP_INT32:
 ++     case SDP_INT64:
 ++     case SDP_INT128:
 ++             SDPDBG("Integer : 0x%x\n", d->val.uint32);
 ++             break;
 ++     case SDP_UUID16:
 ++     case SDP_UUID32:
 ++     case SDP_UUID128:
 ++             SDPDBG("UUID\n");
 ++             sdp_uuid_print(&d->val.uuid);
 ++             break;
 ++     case SDP_TEXT_STR8:
 ++     case SDP_TEXT_STR16:
 ++     case SDP_TEXT_STR32:
 ++             SDPDBG("Text : %s\n", d->val.str);
 ++             break;
 ++     case SDP_URL_STR8:
 ++     case SDP_URL_STR16:
 ++     case SDP_URL_STR32:
 ++             SDPDBG("URL : %s\n", d->val.str);
 ++             break;
 ++     case SDP_SEQ8:
 ++     case SDP_SEQ16:
 ++     case SDP_SEQ32:
 ++             print_dataseq(d->val.dataseq);
 ++             break;
 ++     case SDP_ALT8:
 ++     case SDP_ALT16:
 ++     case SDP_ALT32:
 ++             SDPDBG("Data Sequence Alternates\n");
 ++             print_dataseq(d->val.dataseq);
 ++             break;
 ++     }
 ++}
 ++#endif
 ++
 ++sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId)
 ++{
 ++     if (rec->attrlist) {
 ++             sdp_data_t sdpTemplate;
 ++             sdp_list_t *p;
 ++
 ++             sdpTemplate.attrId = attrId;
 ++             p = sdp_list_find(rec->attrlist, &sdpTemplate, sdp_attrid_comp_func);
 ++             if (p)
 ++                     return (sdp_data_t *)p->data;
 ++     }
 ++     return NULL;
 ++}
 ++
 ++int sdp_send_req(sdp_session_t *session, uint8_t *buf, uint32_t size)
 ++{
 ++     uint32_t sent = 0;
 ++
 ++     while (sent < size) {
 ++             int n = send(session->sock, buf + sent, size - sent, 0);
 ++             if (n < 0)
 ++                     return -1;
 ++             sent += n;
 ++     }
 ++     return 0;
 ++}
 ++
 ++int sdp_read_rsp(sdp_session_t *session, uint8_t *buf, uint32_t size)
 ++{
 ++     fd_set readFds;
 ++     struct timeval timeout = { SDP_RESPONSE_TIMEOUT, 0 };
 ++
 ++     FD_ZERO(&readFds);
 ++     FD_SET(session->sock, &readFds);
 ++     SDPDBG("Waiting for response\n");
 ++     if (select(session->sock + 1, &readFds, NULL, NULL, &timeout) == 0) {
 ++             SDPERR("Client timed out\n");
 ++             errno = ETIMEDOUT;
 ++             return -1;
 ++     }
 ++     return recv(session->sock, buf, size, 0);
 ++}
 ++
 ++/*
 ++ * generic send request, wait for response method.
 ++ */
 ++int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *reqbuf, uint8_t *rspbuf, uint32_t reqsize, uint32_t *rspsize)
 ++{
 ++     int n;
 ++     sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)reqbuf;
 ++     sdp_pdu_hdr_t *rsphdr = (sdp_pdu_hdr_t *)rspbuf;
 ++
 ++     SDPDBG("");
 ++     if (0 > sdp_send_req(session, reqbuf, reqsize)) {
 ++             SDPERR("Error sending data:%s", strerror(errno));
 ++             return -1;
 ++     }
 ++     n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
 ++     if (0 > n)
 ++             return -1;
 ++     SDPDBG("Read : %d\n", n);
 ++     if (n == 0 || reqhdr->tid != rsphdr->tid) {
 ++             errno = EPROTO;
 ++             return -1;
 ++     }
 ++     *rspsize = n;
 ++     return 0;
 ++}
 ++
 ++/*
 ++ * singly-linked lists (after openobex implementation)
 ++ */
 ++sdp_list_t *sdp_list_append(sdp_list_t *p, void *d)
 ++{
 ++     sdp_list_t *q, *n = malloc(sizeof(sdp_list_t));
 ++
 ++     if (!n)
 ++             return 0;
 ++
 ++     n->data = d;
 ++     n->next = 0;
 ++
 ++     if (!p)
 ++             return n;
 ++
 ++     for (q = p; q->next; q = q->next);
 ++     q->next = n;
 ++
 ++     return p;
 ++}
 ++
 ++sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d)
 ++{
 ++     sdp_list_t *p, *q;
 ++
 ++     for (q = 0, p = list; p; q = p, p = p->next)
 ++             if (p->data == d) {
 ++                     if (q)
 ++                             q->next = p->next;
 ++                     else
 ++                             list = p->next;
 ++                     free(p);
 ++                     break;
 ++             }
 ++
 ++     return list;
 ++}
 ++
 ++sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *d, sdp_comp_func_t f)
 ++{
 ++     sdp_list_t *q, *p, *n;
 ++
 ++     n = malloc(sizeof(sdp_list_t));
 ++     if (!n)
 ++             return 0;
 ++     n->data = d;
 ++     for (q = 0, p = list; p; q = p, p = p->next)
 ++             if (f(p->data, d) >= 0)
 ++                     break; 
 ++     // insert between q and p; if !q insert at head
 ++     if (q)
 ++             q->next = n;
 ++     else
 ++             list = n;
 ++     n->next = p;
 ++     return list;
 ++}
 ++
 ++/*
 ++ * Every element of the list points to things which need 
 ++ * to be free()'d. This method frees the list's contents
 ++ */
 ++void sdp_list_free(sdp_list_t *list, sdp_free_func_t f)
 ++{
 ++     sdp_list_t *next;
 ++     while (list) {
 ++             next = list->next;
 ++             if (f)
 ++                     f(list->data);
 ++             free(list);
 ++             list = next;
 ++     }
 ++}
 ++
 ++static inline int __find_port(sdp_data_t *seq, int proto)
 ++{
 ++     if (!seq || !seq->next)
 ++             return 0;
 ++
 ++     if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) {
 ++             seq = seq->next;
 ++             switch (seq->dtd) {
 ++             case SDP_UINT8:
 ++                     return seq->val.uint8;
 ++             case SDP_UINT16:
 ++                     return seq->val.uint16;
 ++             }
 ++     }
 ++     return 0;
 ++}
 ++
 ++int sdp_get_proto_port(const sdp_list_t *list, int proto)
 ++{
 ++     if (proto != L2CAP_UUID && proto != RFCOMM_UUID) {
 ++             errno = EINVAL;
 ++             return -1;
 ++     }
 ++
 ++     for (; list; list = list->next) {
 ++             sdp_list_t *p;
 ++             for (p = list->data; p; p = p->next) {
 ++                     sdp_data_t *seq = (sdp_data_t *) p->data;
 ++                     int port = __find_port(seq, proto);
 ++                     if (port)
 ++                             return port;
 ++             }
 ++     }
 ++     return 0;
 ++}
 ++
 ++sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto)
 ++{
 ++     for (; list; list = list->next) {
 ++             sdp_list_t *p;
 ++             for (p = list->data; p; p = p->next) {
 ++                     sdp_data_t *seq = (sdp_data_t *) p->data;
 ++                     if (SDP_IS_UUID(seq->dtd) && 
 ++                                     sdp_uuid_to_proto(&seq->val.uuid) == proto)
 ++                             return seq->next;
 ++             }
 ++     }
 ++     return NULL;
 ++}
 ++
 ++int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
 ++{
 ++     sdp_data_t *pdlist, *curr;
 ++     sdp_list_t *ap = 0;
 ++
 ++     pdlist = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
 ++     if (pdlist == NULL) {
 ++             errno = ENODATA;
 ++             return -1;
 ++     }
 ++     SDPDBG("AP type : 0%x\n", pdlist->dtd);
 ++
 ++     for (; pdlist; pdlist = pdlist->next) {
 ++             sdp_list_t *pds = 0;
 ++             for (curr = pdlist->val.dataseq; curr; curr = curr->next)
 ++                     pds = sdp_list_append(pds, curr->val.dataseq);
 ++             ap = sdp_list_append(ap, pds);
 ++     }
 ++     *pap = ap;
 ++     return 0;
 ++}
 ++
 ++int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
 ++{
 ++     sdp_data_t *pdlist, *curr;
 ++     sdp_list_t *ap = 0;
 ++
 ++     pdlist = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
 ++     if (pdlist == NULL) {
 ++             errno = ENODATA;
 ++             return -1;
 ++     }
 ++     SDPDBG("AP type : 0%x\n", pdlist->dtd);
 ++
 ++     pdlist = pdlist->val.dataseq;
 ++
 ++     for (; pdlist; pdlist = pdlist->next) {
 ++             sdp_list_t *pds = 0;
 ++             for (curr = pdlist->val.dataseq; curr; curr = curr->next)
 ++                     pds = sdp_list_append(pds, curr->val.dataseq);
 ++             ap = sdp_list_append(ap, pds);
 ++     }
 ++     *pap = ap;
 ++     return 0;
 ++}
 ++
 ++int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp)
 ++{
 ++     sdp_data_t *sdpdata = sdp_data_get(rec, attr);
 ++
 ++     *seqp = NULL;
 ++     if (sdpdata && sdpdata->dtd >= SDP_SEQ8 && sdpdata->dtd <= SDP_SEQ32) {
 ++             sdp_data_t *d;
 ++             for (d = sdpdata->val.dataseq; d; d = d->next) {
 ++                     uuid_t *u;
 ++                     if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128)
 ++                             goto fail;
 ++
 ++                     u = malloc(sizeof(uuid_t));
 ++                     memset(u, 0, sizeof(uuid_t));
 ++                     *u = d->val.uuid;
 ++                     *seqp = sdp_list_append(*seqp, u);
 ++             }
 ++             return 0;
 ++     }
 ++fail:
 ++     sdp_list_free(*seqp, free);
 ++     errno = EINVAL;
 ++     return -1;
 ++}
 ++
 ++int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t aid, sdp_list_t *seq)
 ++{
 ++     int status = 0, i, len;
 ++     void **dtds, **values;
 ++     uint8_t uuid16 = SDP_UUID16;
 ++     uint8_t uuid32 = SDP_UUID32;
 ++     uint8_t uuid128 = SDP_UUID128;
 ++     sdp_list_t *p;
 ++
 ++     len = sdp_list_len(seq);
 ++     if (!seq || len == 0)
 ++             return -1;
 ++     dtds = (void **)malloc(len * sizeof(void *));
 ++     values = (void **)malloc(len * sizeof(void *));
 ++     for (p = seq, i = 0; i < len; i++, p = p->next) {
 ++             uuid_t *uuid = (uuid_t *)p->data;
 ++             if (uuid)
 ++                     switch (uuid->type) {
 ++                     case SDP_UUID16:
 ++                             dtds[i] = &uuid16;
 ++                             values[i] = &uuid->value.uuid16;
 ++                             break;
 ++                     case SDP_UUID32:
 ++                             dtds[i] = &uuid32;
 ++                             values[i] = &uuid->value.uuid32;
 ++                             break;
 ++                     case SDP_UUID128:
 ++                             dtds[i] = &uuid128;
 ++                             values[i] = &uuid->value.uuid128;
 ++                             break;
 ++                     default:
 ++                             status = -1;
 ++                             break;
 ++                     }
 ++             else {
 ++                     status = -1;
 ++                     break;
 ++             }
 ++     }
 ++     if (status == 0) {
 ++             sdp_data_t *data = sdp_seq_alloc(dtds, values, len);
 ++             sdp_attr_replace(rec, aid, data);
 ++             sdp_pattern_add_uuidseq(rec, seq);
 ++     }
 ++     free(dtds);
 ++     free(values);
 ++     return status;
 ++}
 ++
 ++int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq)
 ++{
 ++     sdp_lang_attr_t *lang;
 ++     sdp_data_t *sdpdata, *curr_data;
 ++
 ++     *langSeq = NULL;
 ++     sdpdata = sdp_data_get(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST);
 ++     if (sdpdata == NULL) {
 ++             errno = ENODATA;
 ++             return -1;
 ++     }
 ++     curr_data = sdpdata->val.dataseq;
 ++     while (curr_data) {
 ++             sdp_data_t *pCode = curr_data;
 ++             sdp_data_t *pEncoding = pCode->next;
 ++             sdp_data_t *pOffset = pEncoding->next;
 ++             if (pCode && pEncoding && pOffset) {
 ++                     lang = malloc(sizeof(sdp_lang_attr_t));
 ++                     lang->code_ISO639 = pCode->val.uint16;
 ++                     lang->encoding = pEncoding->val.uint16;
 ++                     lang->base_offset = pOffset->val.uint16;
 ++                     SDPDBG("code_ISO639 :  0x%02x\n", lang->code_ISO639);
 ++                     SDPDBG("encoding :     0x%02x\n", lang->encoding);
 ++                     SDPDBG("base_offfset : 0x%02x\n", lang->base_offset);
 ++                     *langSeq = sdp_list_append(*langSeq, lang);
 ++             }
 ++             curr_data = pOffset->next;
 ++     }
 ++     return 0;
 ++}
 ++
 ++int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq)
 ++{
 ++     sdp_profile_desc_t *profDesc;
 ++     sdp_data_t *sdpdata, *seq;
 ++
 ++     *profDescSeq = NULL;
 ++     sdpdata = sdp_data_get(rec, SDP_ATTR_PFILE_DESC_LIST);
 ++     if (!sdpdata || !sdpdata->val.dataseq) {
 ++             errno = ENODATA;
 ++             return -1;
 ++     }
 ++     for (seq = sdpdata->val.dataseq; seq && seq->val.dataseq; seq = seq->next) {
 ++             uuid_t *uuid = NULL;
 ++             uint16_t version = 0x100;
 ++
 ++             if (SDP_IS_UUID(seq->dtd)) {
 ++                     uuid = &seq->val.uuid;
 ++             } else {
 ++                     sdp_data_t *puuid = seq->val.dataseq;
 ++                     sdp_data_t *pVnum = seq->val.dataseq->next;
 ++                     if (puuid && pVnum) {
 ++                             uuid = &puuid->val.uuid;
 ++                             version = pVnum->val.uint16;
 ++                     }
 ++             }
 ++
 ++             if (uuid != NULL) {
 ++                     profDesc = malloc(sizeof(sdp_profile_desc_t));
 ++                     profDesc->uuid = *uuid;
 ++                     profDesc->version = version;
 ++#ifdef SDP_DEBUG
 ++                     sdp_uuid_print(&profDesc->uuid);
 ++                     SDPDBG("Vnum : 0x%04x\n", profDesc->version);
 ++#endif
 ++                     *profDescSeq = sdp_list_append(*profDescSeq, profDesc);
 ++             }
 ++     }
 ++     return 0;
 ++}
 ++
 ++int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16)
 ++{
 ++     sdp_data_t *d, *curr;
 ++
 ++     *u16 = NULL;
 ++     d = sdp_data_get(rec, SDP_ATTR_VERSION_NUM_LIST);
 ++     if (d == NULL) {
 ++             errno = ENODATA;
 ++             return -1;
 ++     }
 ++     for (curr = d->val.dataseq; curr; curr = curr->next)
 ++             *u16 = sdp_list_append(*u16, &curr->val.uint16);
 ++     return 0;
 ++}
 ++
 ++/* flexible extraction of basic attributes - Jean II */
 ++/* How do we expect caller to extract predefined data sequences? */
 ++int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value)
 ++{
 ++     sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
 ++
 ++     if (sdpdata)
 ++             /* Verify that it is what the caller expects */
 ++             if (sdpdata->dtd == SDP_BOOL || sdpdata->dtd == SDP_UINT8 ||
 ++             sdpdata->dtd == SDP_UINT16 || sdpdata->dtd == SDP_UINT32 ||
 ++             sdpdata->dtd == SDP_INT8 || sdpdata->dtd == SDP_INT16 ||
 ++             sdpdata->dtd == SDP_INT32) {
 ++                     *value = sdpdata->val.uint32;
 ++                     return 0;
 ++             }
 ++     errno = EINVAL;
 ++     return -1;
 ++}
 ++
 ++int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value, int valuelen)
 ++{
 ++     sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
 ++     if (sdpdata)
 ++             /* Verify that it is what the caller expects */
 ++             if (sdpdata->dtd == SDP_TEXT_STR8 || sdpdata->dtd == SDP_TEXT_STR16 || sdpdata->dtd == SDP_TEXT_STR32)
 ++                     if (strlen(sdpdata->val.str) < valuelen) {
 ++                             strcpy(value, sdpdata->val.str);
 ++                             return 0;
 ++                     }
 ++     errno = EINVAL;
 ++     return -1;
 ++}
 ++
 ++#define get_basic_attr(attrID, pAttrValue, fieldName)                \
 ++     sdp_data_t *data = sdp_data_get(rec, attrID);           \
 ++     if (data) {                                             \
 ++             *pAttrValue = data->val.fieldName;              \
 ++             return 0;                                       \
 ++     }                                                       \
 ++     errno = EINVAL;                                         \
 ++     return -1;
 ++
 ++int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid)
 ++{
 ++     get_basic_attr(SDP_ATTR_SERVICE_ID, uuid, uuid);
 ++}
 ++
 ++int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid)
 ++{
 ++     get_basic_attr(SDP_ATTR_GROUP_ID, uuid, uuid);
 ++}
 ++
 ++int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState)
 ++{
 ++     get_basic_attr(SDP_ATTR_RECORD_STATE, svcRecState, uint32);
 ++}
 ++
 ++int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail)
 ++{
 ++     get_basic_attr(SDP_ATTR_SERVICE_AVAILABILITY, svcAvail, uint8);
 ++}
 ++
 ++int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo)
 ++{
 ++     get_basic_attr(SDP_ATTR_SVCINFO_TTL, svcTTLInfo, uint32);
 ++}
 ++
 ++int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState)
 ++{
 ++     get_basic_attr(SDP_ATTR_SVCDB_STATE, svcDBState, uint32);
 ++}
 ++
 ++/*
 ++ * NOTE that none of the setXXX() functions below will
 ++ * actually update the SDP server, unless the
 ++ * {register, update}sdp_record_t() function is invoked.
 ++ */
 ++
 ++int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *value)
 ++{
 ++     sdp_data_t *d = sdp_data_alloc(dtd, value);
 ++     if (d) {
 ++             sdp_attr_replace(rec, attr, d);
 ++             return 0;
 ++     }
 ++     return -1;
 ++}
 ++
 ++/*
 ++ * Set the information attributes of the service
 ++ * pointed to by rec. The attributes are
 ++ * service name, description and provider name
 ++ */
 ++void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc)
 ++{
 ++     if (name)
 ++             sdp_attr_add_new(rec, SDP_ATTR_SVCNAME_PRIMARY, SDP_TEXT_STR8, (void *)name);
 ++     if (prov)
 ++             sdp_attr_add_new(rec, SDP_ATTR_PROVNAME_PRIMARY, SDP_TEXT_STR8, (void *)prov);
 ++     if (desc)
 ++             sdp_attr_add_new(rec, SDP_ATTR_SVCDESC_PRIMARY, SDP_TEXT_STR8, (void *)desc);
 ++}
 ++
 ++static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto)
 ++{
 ++     sdp_data_t *seq = NULL;
 ++     void *dtds[10], *values[10];
 ++     void **seqDTDs, **seqs;
 ++     int i, seqlen;
 ++     sdp_list_t *p;
 ++
 ++     seqlen = sdp_list_len(proto);
 ++     seqDTDs = (void **)malloc(seqlen * sizeof(void *));
 ++     seqs = (void **)malloc(seqlen * sizeof(void *));
 ++     for (i = 0, p = proto; p; p = p->next, i++) {
 ++             sdp_list_t *elt = (sdp_list_t *)p->data;
 ++             sdp_data_t *s;
 ++             int pslen = 0;
 ++             for (; elt && pslen < sizeof(dtds); elt = elt->next, pslen++) {
 ++                     sdp_data_t *d = (sdp_data_t *)elt->data;
 ++                     dtds[pslen] = &d->dtd;
 ++                     switch (d->dtd) {
 ++                     case SDP_UUID16:
 ++                             values[pslen] = &((uuid_t *)d)->value.uuid16;
 ++                             break;
 ++                     case SDP_UUID32:
 ++                             values[pslen] = &((uuid_t *)d)->value.uuid32;
 ++                             break;
 ++                     case SDP_UUID128:
 ++                             values[pslen] = &((uuid_t *)d)->value.uuid128;
 ++                             break;
 ++                     case SDP_UINT8:
 ++                             values[pslen] = &d->val.uint8;
 ++                             break;
 ++                     case SDP_UINT16:
 ++                             values[pslen] = &d->val.uint16;
 ++                             break;
 ++                     case SDP_SEQ8:
 ++                     case SDP_SEQ16:
 ++                     case SDP_SEQ32:
 ++                             values[pslen] = d;
 ++                             break;
 ++                     // FIXME: more
 ++                     }
 ++             }
 ++             s = sdp_seq_alloc(dtds, values, pslen);
 ++             if (s) {
 ++                     seqDTDs[i] = &s->dtd;
 ++                     seqs[i] = s;
 ++             }
 ++     }
 ++     seq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
 ++     free(seqDTDs);
 ++     free(seqs);
 ++     return seq;
 ++}
 ++
 ++/*
 ++ * sets the access protocols of the service specified
 ++ * to the value specified in "access_proto"
 ++ *
 ++ * Note that if there are alternate mechanisms by 
 ++ * which the service is accessed, then they should 
 ++ * be specified as sequences 
 ++ *
 ++ * Using a value of NULL for accessProtocols has
 ++ * effect of removing this attribute (if previously set)
 ++ * 
 ++ * This function replaces the existing sdp_access_proto_t
 ++ * structure (if any) with the new one specified.
 ++ *
 ++ * returns 0 if successful or -1 if there is a failure.
 ++ */
 ++int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
 ++{
 ++     const sdp_list_t *p;
 ++     sdp_data_t *protos = NULL;
 ++
 ++     for (p = ap; p; p = p->next) {
 ++             sdp_data_t *seq = access_proto_to_dataseq(rec, (sdp_list_t *) p->data);
 ++             protos = sdp_seq_append(protos, seq);
 ++     }
 ++
 ++     sdp_attr_add(rec, SDP_ATTR_PROTO_DESC_LIST, protos);
 ++
 ++     return 0;
 ++}
 ++
 ++int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
 ++{
 ++     const sdp_list_t *p;
 ++     sdp_data_t *protos = NULL;
 ++
 ++     for (p = ap; p; p = p->next) {
 ++             sdp_data_t *seq = access_proto_to_dataseq(rec, (sdp_list_t *) p->data);
 ++             protos = sdp_seq_append(protos, seq);
 ++     }
 ++
 ++     sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST,
 ++                     protos ? sdp_data_alloc(SDP_SEQ8, protos) : NULL);
 ++
 ++     return 0;
 ++}
 ++
 ++/*
 ++ * set the "LanguageBase" attributes of the service record
 ++ * record to the value specified in "langAttrList".
 ++ *
 ++ * "langAttrList" is a linked list of "sdp_lang_attr_t"
 ++ * objects, one for each language in which user visible
 ++ * attributes are present in the service record.
 ++ *
 ++ * Using a value of NULL for langAttrList has
 ++ * effect of removing this attribute (if previously set)
 ++ * 
 ++ * This function replaces the exisiting sdp_lang_attr_t
 ++ * structure (if any) with the new one specified.
 ++ *
 ++ * returns 0 if successful or -1 if there is a failure.
 ++ */
 ++int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *seq)
 ++{
 ++     uint8_t uint16 = SDP_UINT16;
 ++     int status = 0, i = 0, seqlen = sdp_list_len(seq);
 ++     void **dtds = (void **)malloc(3 * seqlen * sizeof(void *));
 ++     void **values = (void **)malloc(3 * seqlen * sizeof(void *));
 ++     const sdp_list_t *p;
 ++
 ++     for (p = seq; p; p = p->next) {
 ++             sdp_lang_attr_t *lang = (sdp_lang_attr_t *)p->data;
 ++             if (!lang) {
 ++                     status = -1;
 ++                     break;
 ++             }
 ++             dtds[i] = &uint16;
 ++             values[i] = &lang->code_ISO639;
 ++             i++;
 ++             dtds[i] = &uint16;
 ++             values[i] = &lang->encoding;
 ++             i++;
 ++             dtds[i] = &uint16;
 ++             values[i] = &lang->base_offset;
 ++             i++;
 ++     }
 ++     if (status == 0) {
 ++             sdp_data_t *seq = sdp_seq_alloc(dtds, values, 3 * seqlen);
 ++             sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, seq);
 ++     }
 ++     free(dtds);
 ++     free(values);
 ++     return status;
 ++}
 ++
 ++/*
 ++ * set the "ServiceID" attribute of the service. 
 ++ * 
 ++ * This is the UUID of the service. 
 ++ * 
 ++ * returns 0 if successful or -1 if there is a failure.
 ++ */
 ++void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid)
 ++{
 ++     switch (uuid.type) {
 ++     case SDP_UUID16:
 ++             sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID16, &uuid.value.uuid16);
 ++             break;
 ++     case SDP_UUID32:
 ++             sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID32, &uuid.value.uuid32);
 ++             break;
 ++     case SDP_UUID128:
 ++             sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID128, &uuid.value.uuid128);
 ++             break;
 ++     }
 ++     sdp_pattern_add_uuid(rec, &uuid);
 ++}
 ++
 ++/*
 ++ * set the GroupID attribute of the service record defining a group. 
 ++ * 
 ++ * This is the UUID of the group. 
 ++ * 
 ++ * returns 0 if successful or -1 if there is a failure.
 ++ */
 ++void sdp_set_group_id(sdp_record_t *rec, uuid_t uuid)
 ++{
 ++     switch (uuid.type) {
 ++     case SDP_UUID16:
 ++             sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID16, &uuid.value.uuid16);
 ++             break;
 ++     case SDP_UUID32:
 ++             sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID32, &uuid.value.uuid32);
 ++             break;
 ++     case SDP_UUID128:
 ++             sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID128, &uuid.value.uuid128);
 ++             break;
 ++     }
 ++     sdp_pattern_add_uuid(rec, &uuid);
 ++}
 ++
 ++/*
 ++ * set the ProfileDescriptorList attribute of the service record
 ++ * pointed to by record to the value specified in "profileDesc".
 ++ *
 ++ * Each element in the list is an object of type
 ++ * sdp_profile_desc_t which is a definition of the
 ++ * Bluetooth profile that this service conforms to.
 ++ *
 ++ * Using a value of NULL for profileDesc has
 ++ * effect of removing this attribute (if previously set)
 ++ * 
 ++ * This function replaces the exisiting ProfileDescriptorList
 ++ * structure (if any) with the new one specified.
 ++ *
 ++ * returns 0 if successful or -1 if there is a failure.
 ++ */
 ++int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *profiles)
 ++{
 ++     int status = 0;
 ++     uint8_t uuid16 = SDP_UUID16;
 ++     uint8_t uuid32 = SDP_UUID32;
 ++     uint8_t uuid128 = SDP_UUID128;
 ++     uint8_t uint16 = SDP_UINT16;
 ++     int i = 0, seqlen = sdp_list_len(profiles);
 ++     void **seqDTDs = (void **)malloc(seqlen * sizeof(void *));
 ++     void **seqs = (void **)malloc(seqlen * sizeof(void *));
 ++     const sdp_list_t *p;
 ++
 ++     for (p = profiles; p; p = p->next) {
 ++             sdp_data_t *seq;
 ++             void *dtds[2], *values[2];
 ++             sdp_profile_desc_t *profile = (sdp_profile_desc_t *)p->data;
 ++             if (!profile) {
 ++                     status = -1;
 ++                     break;
 ++             }
 ++             switch (profile->uuid.type) {
 ++             case SDP_UUID16:
 ++                     dtds[0] = &uuid16;
 ++                     values[0] = &profile->uuid.value.uuid16;
 ++                     break;
 ++             case SDP_UUID32:
 ++                     dtds[0] = &uuid32;
 ++                     values[0] = &profile->uuid.value.uuid32;
 ++                     break;
 ++             case SDP_UUID128:
 ++                     dtds[0] = &uuid128;
 ++                     values[0] = &profile->uuid.value.uuid128;
 ++                     break;
 ++             default:
 ++                     status = -1;
 ++                     break;
 ++             }
 ++             dtds[1] = &uint16;
 ++             values[1] = &profile->version;
 ++             seq = sdp_seq_alloc(dtds, values, 2);
 ++             if (seq) {
 ++                     seqDTDs[i] = &seq->dtd;
 ++                     seqs[i] = seq;
 ++                     sdp_pattern_add_uuid(rec, &profile->uuid);
 ++             }
 ++             i++;
 ++     }
 ++     if (status == 0) {
 ++             sdp_data_t *pAPSeq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
 ++             sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, pAPSeq);
 ++     }
 ++     free(seqDTDs);
 ++     free(seqs);
 ++     return status;
 ++}
 ++
 ++/*
 ++ * sets various URL attributes of the service
 ++ * pointed to by record. The URL include
 ++ *
 ++ * client: a URL to the client's
 ++ *   platform specific (WinCE, PalmOS) executable
 ++ *   code that can be used to access this service.
 ++ *
 ++ * doc: a URL pointing to service documentation
 ++ *
 ++ * icon: a URL to an icon that can be used to represent
 ++ *   this service.
 ++ *
 ++ * Note that you need to pass NULL for any URLs
 ++ * that you don't want to set or remove
 ++ */
 ++void sdp_set_url_attr(sdp_record_t *rec, const char *client, const char *doc, const char *icon)
 ++{
 ++     sdp_attr_add_new(rec, SDP_ATTR_CLNT_EXEC_URL, SDP_URL_STR8, client);
 ++     sdp_attr_add_new(rec, SDP_ATTR_DOC_URL, SDP_URL_STR8, doc);
 ++     sdp_attr_add_new(rec, SDP_ATTR_ICON_URL, SDP_URL_STR8, icon);
 ++}
 ++
 ++/*
 ++ * The code in this function is executed only once per
 ++ * thread. We compute the actual bit value of the Bluetooth
 ++ * base UUID which is a string defined in bt_std_values.h 
 ++ * and is assumed to be of the standard form with "-" separators.
 ++ *
 ++ * The algorithm however converts the string to 4 unsigned longs
 ++ * using the strtoul() and assigns the values in sequence to
 ++ * the 128bit value
 ++ */
 ++uint128_t *sdp_create_base_uuid(void)
 ++{
 ++     uint128_t *base_uuid;
 ++     char baseStr[128];
 ++     int delim = '-';
 ++     unsigned long dataLongValue;
 ++     char *delimPtr;
 ++     char *dataPtr;
 ++     char temp[10];
 ++     int toBeCopied;
 ++     uint8_t *data;
 ++
 ++     strcpy(baseStr, BASE_UUID);
 ++     base_uuid = malloc(sizeof(uint128_t));
 ++     if (!base_uuid)
 ++             return NULL;
 ++
 ++     data = base_uuid->data;
 ++     memset(data, '\0', sizeof(uint128_t));
 ++     memset(temp, '\0', 10);
 ++     dataPtr = baseStr;
 ++     delimPtr = NULL;
 ++     delimPtr = strchr(dataPtr, delim);
 ++     toBeCopied = delimPtr - dataPtr;
 ++     if (toBeCopied != 8) {
 ++             SDPDBG("To be copied(1) : %d\n", toBeCopied);
 ++             free(base_uuid);
 ++             return NULL;
 ++     }
 ++     strncpy(temp, dataPtr, toBeCopied);
 ++     dataLongValue = htonl(strtoul(temp, NULL, 16));
 ++     memcpy(&data[0], &dataLongValue, 4);
 ++
 ++     /*
 ++      * Get the next 4 bytes (note that there is a "-"
 ++      * between them now)
 ++      */
 ++     memset(temp, '\0', 10);
 ++     dataPtr = delimPtr + 1;
 ++     delimPtr = strchr(dataPtr, delim);
 ++     toBeCopied = delimPtr - dataPtr;
 ++     if (toBeCopied != 4) {
 ++             SDPDBG("To be copied(2) : %d\n", toBeCopied);
 ++             free(base_uuid);
 ++             return NULL;
 ++     }
 ++     strncpy(temp, dataPtr, toBeCopied);
 ++     dataPtr = delimPtr + 1;
 ++     delimPtr = strchr(dataPtr, delim);
 ++     toBeCopied = delimPtr - dataPtr;
 ++     if (toBeCopied != 4) {
 ++             SDPDBG("To be copied(3) : %d\n", toBeCopied);
 ++             free(base_uuid);
 ++             return NULL;
 ++     }
 ++     strncat(temp, dataPtr, toBeCopied);
 ++     dataLongValue = htonl(strtoul(temp, NULL, 16));
 ++     memcpy(&data[4], &dataLongValue, 4);
 ++
 ++     /*
 ++      * Get the last 4 bytes (note that there are 6 bytes
 ++      * after the last separator, which is truncated (2+4)
 ++      */
 ++     memset(temp, '\0', 10);
 ++     dataPtr = delimPtr + 1;
 ++     dataPtr = delimPtr + 1;
 ++     delimPtr = strchr(dataPtr, delim);
 ++     toBeCopied = delimPtr - dataPtr;
 ++     if (toBeCopied != 4) {
 ++             SDPDBG("To be copied(4) : %d\n", toBeCopied);
 ++             free(base_uuid);
 ++             return NULL;
 ++     }
 ++     strncpy(temp, dataPtr, toBeCopied);
 ++     strncat(temp, (delimPtr + 1), 4);
 ++     dataLongValue = htonl(strtoul(temp, NULL, 16));
 ++     memcpy(&data[8], &dataLongValue, 4);
 ++     dataLongValue = htonl(strtoul(delimPtr + 5, NULL, 16));
 ++     memcpy(&data[12], &dataLongValue, 4);
 ++
 ++     return base_uuid;
 ++}
 ++
 ++uuid_t *sdp_uuid16_create(uuid_t *u, uint16_t val)
 ++{
 ++     memset(u, 0, sizeof(uuid_t));
 ++     u->type = SDP_UUID16;
 ++     u->value.uuid16 = val;
 ++     return u;
 ++}
 ++
 ++uuid_t *sdp_uuid32_create(uuid_t *u, uint32_t val)
 ++{
 ++     memset(u, 0, sizeof(uuid_t));
 ++     u->type = SDP_UUID32;
 ++     u->value.uuid32 = val;
 ++     return u;
 ++}
 ++
 ++uuid_t *sdp_uuid128_create(uuid_t *u, const void *val)
 ++{ 
 ++     memset(u, 0, sizeof(uuid_t));
 ++     u->type = SDP_UUID128;
 ++     memcpy(&u->value.uuid128, val, sizeof(uint128_t));
 ++     return u;
 ++}
 ++
 ++/*
 ++ * UUID comparison function
 ++ * returns 0 if uuidValue1 == uuidValue2 else -1
 ++ */
 ++int sdp_uuid16_cmp(const void *p1, const void *p2)
 ++{
 ++     const uuid_t *u1 = (const uuid_t *)p1;
 ++     const uuid_t *u2 = (const uuid_t *)p2;
 ++     return memcmp(&u1->value.uuid16, &u2->value.uuid16, sizeof(uint16_t));
 ++}
 ++
 ++/*
 ++ * UUID comparison function
 ++ * returns 0 if uuidValue1 == uuidValue2 else -1
 ++ */
 ++int sdp_uuid128_cmp(const void *p1, const void *p2)
 ++{
 ++     const uuid_t *u1 = (const uuid_t *)p1;
 ++     const uuid_t *u2 = (const uuid_t *)p2;
 ++     return memcmp(&u1->value.uuid128, &u2->value.uuid128, sizeof(uint128_t));
 ++}
 ++
 ++/*
 ++ * 128 to 16 bit and 32 to 16 bit UUID conversion functions
 ++ * yet to be implemented. Note that the input is in NBO in
 ++ * both 32 and 128 bit UUIDs and conversion is needed
 ++ */
 ++void sdp_uuid16_to_uuid128(uuid_t *uuid128, uuid_t *uuid16)
 ++{
 ++     /*
 ++      * We have a 16 bit value, which needs to be added to
 ++      * bytes 3 and 4 (at indices 2 and 3) of the Bluetooth base
 ++      */
 ++     unsigned short data1;
 ++
 ++     // allocate a 128bit UUID and init to the Bluetooth base UUID
 ++     uuid128->value.uuid128 = bluetooth_base_uuid;
 ++     uuid128->type = SDP_UUID128;
 ++
 ++     // extract bytes 2 and 3 of 128bit BT base UUID
 ++     memcpy(&data1, &bluetooth_base_uuid.data[2], 2);
 ++
 ++     // add the given UUID (16 bits)
 ++     data1 += htons(uuid16->value.uuid16);
 ++
 ++     // set bytes 2 and 3 of the 128 bit value
 ++     memcpy(&uuid128->value.uuid128.data[2], &data1, 2);
 ++}
 ++
 ++void sdp_uuid32_to_uuid128(uuid_t *uuid128, uuid_t *uuid32)
 ++{
 ++     /*
 ++      * We have a 32 bit value, which needs to be added to
 ++      * bytes 1->4 (at indices 0 thru 3) of the Bluetooth base
 ++      */
 ++     unsigned int data0;
 ++
 ++     // allocate a 128bit UUID and init to the Bluetooth base UUID
 ++     uuid128->value.uuid128 = bluetooth_base_uuid;
 ++     uuid128->type = SDP_UUID128;
 ++
 ++     // extract first 4 bytes
 ++     memcpy(&data0, &bluetooth_base_uuid.data[0], 4);
 ++
 ++     // add the given UUID (32bits)
 ++     data0 += htonl(uuid32->value.uuid32);
 ++
 ++     // set the 4 bytes of the 128 bit value
 ++     memcpy(&uuid128->value.uuid128.data[0], &data0, 4);
 ++}
 ++
 ++uuid_t *sdp_uuid_to_uuid128(uuid_t *uuid)
 ++{
 ++     uuid_t *uuid128 = bt_malloc(sizeof(uuid_t));
 ++     memset(uuid128, 0, sizeof(uuid_t));
 ++     switch (uuid->type) {
 ++     case SDP_UUID128:
 ++             *uuid128 = *uuid;
 ++             break;
 ++     case SDP_UUID32:
 ++             sdp_uuid32_to_uuid128(uuid128, uuid);
 ++             break;
 ++     case SDP_UUID16:
 ++             sdp_uuid16_to_uuid128(uuid128, uuid);
 ++             break;
 ++     }
 ++     return uuid128;
 ++}
 ++
 ++/* 
 ++ * converts a 128-bit uuid to a 16/32-bit one if possible
 ++ * returns true if uuid contains a 16/32-bit UUID at exit
 ++ */
 ++int sdp_uuid128_to_uuid(uuid_t *uuid)
 ++{
 ++     uint128_t *b = &bluetooth_base_uuid;
 ++     uint128_t *u = &uuid->value.uuid128;
 ++     uint32_t data;
 ++     int i;
 ++
 ++     if (uuid->type != SDP_UUID128)
 ++             return 1;
 ++
 ++     for (i = 4; i < sizeof(b->data); i++)
 ++             if (b->data[i] != u->data[i])
 ++                     return 0;
 ++
 ++     memcpy(&data, u->data, 4);
 ++     data = htonl(data);
 ++     if (data <= 0xffff) {
 ++             uuid->type = SDP_UUID16;
 ++             uuid->value.uuid16 = (uint16_t) data;
 ++     } else {
 ++             uuid->type = SDP_UUID32;
 ++             uuid->value.uuid32 = data;
 ++     }
 ++     return 1;
 ++}
 ++
 ++/*
 ++ * convert a UUID to the 16-bit short-form
 ++ */
 ++int sdp_uuid_to_proto(uuid_t *uuid)
 ++{
 ++     uuid_t u = *uuid;
 ++     if (sdp_uuid128_to_uuid(&u)) {
 ++             switch (u.type) {
 ++             case SDP_UUID16:
 ++                     return u.value.uuid16;
 ++             case SDP_UUID32:
 ++                     return u.value.uuid32;
 ++             }
 ++     }
 ++     return 0;
 ++}
 ++
 ++/*
 ++ * This function appends data to the PDU buffer "dst" from source "src". 
 ++ * The data length is also computed and set.
 ++ * Should the PDU length exceed 2^8, then sequence type is
 ++ * set accordingly and the data is memmove()'d.
 ++ */
 ++void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len)
 ++{
 ++     uint8_t *p = dst->data;
 ++     uint8_t dtd = *(uint8_t *) p;
 ++
 ++     SDPDBG("Append src size: %d\n", len);
 ++     SDPDBG("Append dst size: %d\n", dst->data_size);
 ++     SDPDBG("Dst buffer size: %d\n", dst->buf_size);
 ++     if (dst->data_size + len > dst->buf_size) {
 ++             int need = SDP_PDU_CHUNK_SIZE * ((len / SDP_PDU_CHUNK_SIZE) + 1);
 ++             dst->data = realloc(dst->data, dst->buf_size + need);
 ++
 ++             SDPDBG("Realloc'ing : %d\n", need);
 ++
 ++             if (dst->data == NULL) {
 ++                     SDPERR("Realloc fails \n");
 ++             }
 ++             dst->buf_size += need;
 ++     }
 ++     if (dst->data_size == 0 && dtd == 0) {
 ++             // create initial sequence
 ++             *(uint8_t *)p = SDP_SEQ8;
 ++             p += sizeof(uint8_t);
 ++             dst->data_size += sizeof(uint8_t);
 ++             // reserve space for sequence size
 ++             p += sizeof(uint8_t);
 ++             dst->data_size += sizeof(uint8_t);
 ++     }
 ++
 ++     memcpy(dst->data + dst->data_size, data, len);
 ++     dst->data_size += len;
 ++
 ++     dtd = *(uint8_t *)dst->data;
 ++     if (dst->data_size > UCHAR_MAX && dtd == SDP_SEQ8) {
 ++             short offset = sizeof(uint8_t) + sizeof(uint8_t);
 ++             memmove(dst->data + offset + 1, dst->data + offset, dst->data_size - offset);
 ++             p = dst->data;
 ++             *(uint8_t *) p = SDP_SEQ16;
 ++             p += sizeof(uint8_t);
 ++             dst->data_size += 1;
 ++     }
 ++     p = dst->data;
 ++     dtd = *(uint8_t *) p;
 ++     p += sizeof(uint8_t);
 ++     switch (dtd) {
 ++     case SDP_SEQ8:
 ++             *(uint8_t *) p = dst->data_size - sizeof(uint8_t) - sizeof(uint8_t);
 ++             break;
 ++     case SDP_SEQ16:
 ++             bt_put_unaligned(htons(dst->data_size - sizeof(uint8_t) - sizeof(uint16_t)), (uint16_t *) p);
 ++             break;
 ++     case SDP_SEQ32:
 ++             bt_put_unaligned(htonl(dst->data_size - sizeof(uint8_t) - sizeof(uint32_t)), (uint32_t *) p);
 ++             break;
 ++     }
 ++}
 ++
 ++void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d)
 ++{
 ++     uint8_t buf[256];
 ++     sdp_buf_t append;
 ++
 ++     memset(&append, 0, sizeof(sdp_buf_t));
 ++     append.data = buf;
 ++     append.buf_size = sizeof(buf);
 ++     append.data_size = 0;
 ++
 ++     sdp_set_attrid(&append, d->attrId);
 ++     sdp_gen_pdu(&append, d);
 ++     sdp_append_to_buf(pdu, append.data, append.data_size);
 ++}
 ++
 ++/*
 ++ * Registers an sdp record.
 ++ *
 ++ * It is incorrect to call this method on a record that
 ++ * has been already registered with the server.
 ++ *
 ++ * Returns zero on success, otherwise -1 (and sets errno).
 ++ */
 ++int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle)
 ++{
 ++     uint8_t *req, *rsp, *p;
 ++     uint32_t reqsize, rspsize;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     int status;
 ++
 ++     SDPDBG("");
 ++
 ++     if (!session->local) {
 ++             errno = EREMOTE;
 ++             return -1;
 ++     }
 ++     req = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rsp = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (req == NULL || rsp == NULL) {
 ++             status = -1;
 ++             errno = ENOMEM;
 ++             goto end;
 ++     }
 ++
 ++     reqhdr = (sdp_pdu_hdr_t *)req;
 ++     reqhdr->pdu_id = SDP_SVC_REGISTER_REQ;
 ++     reqhdr->tid    = htons(sdp_gen_tid(session));
 ++     reqsize = sizeof(sdp_pdu_hdr_t) + 1;
 ++     p = req + sizeof(sdp_pdu_hdr_t);
 ++
 ++     if (bacmp(device, BDADDR_ANY)) {
 ++             *p++ = flags | SDP_DEVICE_RECORD;
 ++             bacpy((bdaddr_t *) p, device);
 ++             p += sizeof(bdaddr_t);
 ++             reqsize += sizeof(bdaddr_t);
 ++     } else
 ++             *p++ = flags;
 ++
 ++     memcpy(p, data, size);
 ++     reqsize += size;
 ++     reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++
 ++     status = sdp_send_req_w4_rsp(session, req, rsp, reqsize, &rspsize);
 ++     if (status < 0)
 ++             goto end;
 ++
 ++     if (rspsize < sizeof(sdp_pdu_hdr_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             errno = EPROTO;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++
 ++     rsphdr = (sdp_pdu_hdr_t *) rsp;
 ++     p = rsp + sizeof(sdp_pdu_hdr_t);
 ++
 ++     if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++             /* Invalid service record */
 ++             errno = EINVAL;
 ++             status = -1;
 ++     } else if (rsphdr->pdu_id != SDP_SVC_REGISTER_RSP) {
 ++             errno = EPROTO;
 ++             status = -1;
 ++     } else {
 ++             if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint32_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     errno = EPROTO;
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++             if (handle)
 ++                     *handle  = ntohl(bt_get_unaligned((uint32_t *) p));
 ++     }
 ++
 ++end:
 ++     if (req)
 ++             free(req);
 ++
 ++     if (rsp)
 ++             free(rsp);
 ++
 ++     return status;
 ++}
 ++
 ++int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags)
 ++{
 ++     sdp_buf_t pdu;
 ++     uint32_t handle;
 ++     int err;
 ++
 ++     SDPDBG("");
 ++
 ++     if (rec->handle && rec->handle != 0xffffffff) {
 ++             uint32_t handle = rec->handle;
 ++             sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
 ++             sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
 ++     }
 ++
 ++     if (sdp_gen_record_pdu(rec, &pdu) < 0) {
 ++             errno = ENOMEM;
 ++             return -1;
 ++     }
 ++
 ++     err = sdp_device_record_register_binary(session, device,
 ++                             pdu.data, pdu.data_size, flags, &handle);
 ++
 ++     free(pdu.data);
 ++
 ++     if (err == 0) {
 ++             sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
 ++             rec->handle = handle;
 ++             sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
 ++     }
 ++
 ++     return err;
 ++}
 ++
 ++int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags)
 ++{
 ++     return sdp_device_record_register(session, BDADDR_ANY, rec, flags);
 ++}
 ++
 ++/*
 ++ * unregister a service record
 ++ */
 ++int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle)
 ++{
 ++     uint8_t *reqbuf, *rspbuf, *p;
 ++     uint32_t reqsize = 0, rspsize = 0;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     int status;
 ++
 ++     SDPDBG("");
 ++
 ++     if (handle == SDP_SERVER_RECORD_HANDLE) {
 ++             errno = EINVAL;
 ++             return -1;
 ++     }
 ++
 ++     if (!session->local) {
 ++             errno = EREMOTE;
 ++             return -1;
 ++     }
 ++
 ++     reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!reqbuf || !rspbuf) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     reqhdr = (sdp_pdu_hdr_t *) reqbuf;
 ++     reqhdr->pdu_id = SDP_SVC_REMOVE_REQ;
 ++     reqhdr->tid    = htons(sdp_gen_tid(session));
 ++
 ++     p = reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     reqsize = sizeof(sdp_pdu_hdr_t);
 ++     bt_put_unaligned(htonl(handle), (uint32_t *) p);
 ++     reqsize += sizeof(uint32_t);
 ++
 ++     reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++     status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
 ++     if (status < 0)
 ++             goto end;
 ++
 ++     if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             errno = EPROTO;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++
 ++     rsphdr = (sdp_pdu_hdr_t *) rspbuf;
 ++     p = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++     status = bt_get_unaligned((uint16_t *) p);
 ++
 ++     if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++             /* For this case the status always is invalid record handle */
 ++             errno = EINVAL;
 ++             status = -1;
 ++     } else if (rsphdr->pdu_id != SDP_SVC_REMOVE_RSP) {
 ++             errno = EPROTO;
 ++             status = -1;
 ++     }
 ++end:
 ++     if (reqbuf)
 ++             free(reqbuf);
 ++
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++
 ++     return status;
 ++}
 ++
 ++int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec)
 ++{
 ++     int err;
 ++
 ++     err = sdp_device_record_unregister_binary(session, device, rec->handle);
 ++     if (err == 0)
 ++             sdp_record_free(rec);
 ++
 ++     return err;
 ++}
 ++
 ++int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec)
 ++{
 ++     return sdp_device_record_unregister(session, BDADDR_ANY, rec);
 ++}
 ++
 ++/*
 ++ * modify an existing service record
 ++ */
 ++int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size)
 ++{
 ++     return -1;
 ++}
 ++
 ++int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec)
 ++{
 ++     uint8_t *reqbuf, *rspbuf, *p;
 ++     uint32_t reqsize, rspsize;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     uint32_t handle;
 ++     sdp_buf_t pdu;
 ++     int status;
 ++
 ++     SDPDBG("");
 ++
 ++     handle = rec->handle;
 ++
 ++     if (handle == SDP_SERVER_RECORD_HANDLE) {
 ++             errno = EINVAL;
 ++             return -1;
 ++     }
 ++     if (!session->local) {
 ++             errno = EREMOTE;
 ++             return -1;
 ++     }
 ++     reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!reqbuf || !rspbuf) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     reqhdr = (sdp_pdu_hdr_t *) reqbuf;
 ++     reqhdr->pdu_id = SDP_SVC_UPDATE_REQ;
 ++     reqhdr->tid    = htons(sdp_gen_tid(session));
 ++
 ++     p = reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     bt_put_unaligned(htonl(handle), (uint32_t *) p);
 ++     reqsize += sizeof(uint32_t);
 ++     p += sizeof(uint32_t);
 ++
 ++     if (sdp_gen_record_pdu(rec, &pdu) < 0) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     memcpy(p, pdu.data, pdu.data_size);
 ++     reqsize += pdu.data_size;
 ++     free(pdu.data);
 ++
 ++     reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++     status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
 ++     if (status < 0)
 ++             goto end;
 ++
 ++     if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
 ++             SDPERR("Unexpected end of packet");
 ++             errno = EPROTO;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++
 ++     SDPDBG("Send req status : %d\n", status);
 ++
 ++     rsphdr = (sdp_pdu_hdr_t *) rspbuf;
 ++     p = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++     status = bt_get_unaligned((uint16_t *) p);
 ++
 ++     if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++             /* The status can be invalid sintax or invalid record handle */
 ++             errno = EINVAL;
 ++             status = -1;
 ++     } else if (rsphdr->pdu_id != SDP_SVC_UPDATE_RSP) {
 ++             errno = EPROTO;
 ++             status = -1;
 ++     }
 ++end:
 ++     if (reqbuf)
 ++             free(reqbuf);
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++     return status;
 ++}
 ++
 ++int sdp_record_update(sdp_session_t *session, const sdp_record_t *rec)
 ++{
 ++     return sdp_device_record_update(session, BDADDR_ANY, rec);
 ++}
 ++
 ++sdp_record_t *sdp_record_alloc()
 ++{
 ++     sdp_record_t *rec = malloc(sizeof(sdp_record_t));
 ++     memset((void *)rec, 0, sizeof(sdp_record_t));
 ++     rec->handle = 0xffffffff;
 ++     return rec;
 ++}
 ++
 ++/*
 ++ * Free the contents of a service record
 ++ */
 ++void sdp_record_free(sdp_record_t *rec)
 ++{
 ++     sdp_list_free(rec->attrlist, (sdp_free_func_t)sdp_data_free);
 ++     sdp_list_free(rec->pattern, free);
 ++     free(rec);
 ++}
 ++
 ++void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid)
 ++{
 ++     uuid_t *uuid128 = sdp_uuid_to_uuid128(uuid);
 ++
 ++     SDPDBG("SvcRec : 0x%lx\n", (unsigned long)rec);
 ++     SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
 ++     SDPDBG("Trying to add : 0x%lx\n", (unsigned long)uuid128);
 ++
 ++     if (sdp_list_find(rec->pattern, uuid128, sdp_uuid128_cmp) == NULL)
 ++             rec->pattern = sdp_list_insert_sorted(rec->pattern, uuid128, sdp_uuid128_cmp);
 ++     else
 ++             bt_free(uuid128);
 ++
 ++     SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
 ++}
 ++
 ++void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq)
 ++{
 ++     for (; seq; seq = seq->next) {
 ++             uuid_t *uuid = (uuid_t *)seq->data;
 ++             sdp_pattern_add_uuid(rec, uuid);
 ++     }
 ++}
 ++
 ++/*
 ++ * Extract a sequence of service record handles from a PDU buffer
 ++ * and add the entries to a sdp_list_t. Note that the service record
 ++ * handles are not in "data element sequence" form, but just like
 ++ * an array of service handles
 ++ */
 ++static void extract_record_handle_seq(uint8_t *pdu, int bufsize, sdp_list_t **seq, int count, int *scanned)
 ++{
 ++     sdp_list_t *pSeq = *seq;
 ++     uint8_t *pdata = pdu;
 ++     int n;
 ++
 ++     for (n = 0; n < count; n++) {
 ++             if (bufsize < sizeof(uint32_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     break;
 ++             }
 ++             uint32_t *pSvcRec = malloc(sizeof(uint32_t));
 ++             *pSvcRec = ntohl(bt_get_unaligned((uint32_t *) pdata));
 ++             pSeq = sdp_list_append(pSeq, pSvcRec);
 ++             pdata += sizeof(uint32_t);
 ++             *scanned += sizeof(uint32_t);
 ++             bufsize -= sizeof(uint32_t);
 ++     }
 ++     *seq = pSeq;
 ++}
 ++/*
 ++ * Generate the attribute sequence pdu form
 ++ * from sdp_list_t elements. Return length of attr seq
 ++ */
 ++static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd)
 ++{
 ++     sdp_data_t *dataseq;
 ++     void **types, **values;
 ++     sdp_buf_t buf;
 ++     int i, seqlen = sdp_list_len(seq);
 ++
 ++     // Fill up the value and the dtd arrays
 ++     SDPDBG("");
 ++
 ++     memset(&buf, 0, sizeof(sdp_buf_t));
 ++     buf.data = malloc(256);
 ++     buf.buf_size = 256;
 ++
 ++     if (!buf.data)
 ++             return -ENOMEM;
 ++
 ++     SDPDBG("Seq length : %d\n", seqlen);
 ++
 ++     types = malloc(seqlen * sizeof(void *));
 ++     values = malloc(seqlen * sizeof(void *));
 ++     for (i = 0; i < seqlen; i++) {
 ++             void *data = seq->data;
 ++             types[i] = &dtd;
 ++             if (SDP_IS_UUID(dtd))
 ++                     data = &((uuid_t *)data)->value;
 ++             values[i] = data;
 ++             seq = seq->next;
 ++     }
 ++
 ++     dataseq = sdp_seq_alloc(types, values, seqlen);
 ++     SDPDBG("Data Seq : 0x%p\n", seq);
 ++     seqlen = sdp_gen_pdu(&buf, dataseq);
 ++     SDPDBG("Copying : %d\n", buf.data_size);
 ++     memcpy(dst, buf.data, buf.data_size);
 ++
 ++     sdp_data_free(dataseq);
 ++
 ++     free(types);
 ++     free(values);
 ++     free(buf.data);
 ++     return seqlen;
 ++}
 ++
 ++static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq)
 ++{
 ++     uuid_t *uuid = (uuid_t *) seq->data;
 ++     return gen_dataseq_pdu(dst, seq, uuid->type);
 ++}
 ++
 ++static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType)
 ++{
 ++     return gen_dataseq_pdu(dst, seq, dataType);
 ++}
 ++
 ++typedef struct {
 ++     uint8_t length;
 ++     unsigned char data[16];
 ++} __attribute__ ((packed)) sdp_cstate_t;
 ++
 ++static int copy_cstate(uint8_t *pdata, int pdata_len, const sdp_cstate_t *cstate)
 ++{
 ++     if (cstate) {
 ++             uint8_t len = cstate->length;
 ++             if (len >= pdata_len) {
 ++                     SDPERR("Continuation state size exceeds internal buffer");
 ++                     len = pdata_len - 1;
 ++             }
 ++             *pdata++ = len;
 ++             memcpy(pdata, cstate->data, len);
 ++             return len + 1;
 ++     }
 ++     *pdata = 0;
 ++     return 1;
 ++}
 ++
 ++/*
 ++ * This is a service search request. 
 ++ *
 ++ * INPUT :
 ++ *
 ++ *   sdp_list_t *search
 ++ *     Singly linked list containing elements of the search
 ++ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
 ++ *     of the service to be searched
 ++ *
 ++ *   uint16_t max_rec_num
 ++ *      A 16 bit integer which tells the service, the maximum
 ++ *      entries that the client can handle in the response. The
 ++ *      server is obliged not to return > max_rec_num entries
 ++ *
 ++ * OUTPUT :
 ++ *
 ++ *   int return value
 ++ *     0:
 ++ *       The request completed successfully. This does not
 ++ *       mean the requested services were found
 ++ *     -1:
 ++ *       On any failure and sets errno
 ++ *
 ++ *   sdp_list_t **rsp_list
 ++ *     This variable is set on a successful return if there are
 ++ *     non-zero service handles. It is a singly linked list of
 ++ *     service record handles (uint16_t)
 ++ */
 ++int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search,
 ++                     uint16_t max_rec_num, sdp_list_t **rsp)
 ++{
 ++     int status = 0;
 ++     uint32_t reqsize = 0, _reqsize;
 ++     uint32_t rspsize = 0, rsplen;
 ++     int seqlen = 0;
 ++     int scanned, total_rec_count, rec_count, pdata_len;
 ++     uint8_t *pdata, *_pdata;
 ++     uint8_t *reqbuf, *rspbuf;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     sdp_cstate_t *cstate = NULL;
 ++
 ++     reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!reqbuf || !rspbuf) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     reqhdr = (sdp_pdu_hdr_t *) reqbuf;
 ++     reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
 ++     pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add service class IDs for search
 ++     seqlen = gen_searchseq_pdu(pdata, search);
 ++
 ++     SDPDBG("Data seq added : %d\n", seqlen);
 ++
 ++     // set the length and increment the pointer
 ++     reqsize += seqlen;
 ++     pdata += seqlen;
 ++
 ++     // specify the maximum svc rec count that client expects
 ++     bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
 ++     reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     _reqsize = reqsize;
 ++     _pdata   = pdata;
 ++     *rsp = NULL;
 ++
 ++     do {
 ++             // Add continuation state or NULL (first time)
 ++             reqsize = _reqsize + copy_cstate(_pdata,
 ++                                     SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
 ++
 ++             // Set the request header's param length
 ++             reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++
 ++             reqhdr->tid  = htons(sdp_gen_tid(session));
 ++             /*
 ++              * Send the request, wait for response and if
 ++              * no error, set the appropriate values and return
 ++              */
 ++             status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
 ++             if (status < 0)
 ++                     goto end;
 ++
 ++             if (rspsize < sizeof(sdp_pdu_hdr_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             rsphdr = (sdp_pdu_hdr_t *) rspbuf;
 ++             rsplen = ntohs(rsphdr->plen);
 ++
 ++             if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++                     SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++             scanned = 0;
 ++             pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++             pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
 ++
 ++             if (pdata_len < sizeof(uint16_t) + sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             // net service record match count
 ++             total_rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             pdata += sizeof(uint16_t);
 ++             scanned += sizeof(uint16_t);
 ++             pdata_len -= sizeof(uint16_t);
 ++             rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             pdata += sizeof(uint16_t);
 ++             scanned += sizeof(uint16_t);
 ++             pdata_len -= sizeof(uint16_t);
 ++
 ++             SDPDBG("Total svc count: %d\n", total_rec_count);
 ++             SDPDBG("Current svc count: %d\n", rec_count);
 ++             SDPDBG("ResponseLength: %d\n", rsplen);
 ++
 ++             if (!rec_count) {
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++             extract_record_handle_seq(pdata, pdata_len, rsp, rec_count, &scanned);
 ++             SDPDBG("BytesScanned : %d\n", scanned);
 ++
 ++             if (rsplen > scanned) {
 ++                     uint8_t cstate_len;
 ++
 ++                     if (rspsize < sizeof(sdp_pdu_hdr_t) + scanned + sizeof(uint8_t)) {
 ++                             SDPERR("Unexpected end of packet: continuation state data missing");
 ++                             status = -1;
 ++                             goto end;
 ++                     }
 ++
 ++                     pdata = rspbuf + sizeof(sdp_pdu_hdr_t) + scanned;
 ++                     cstate_len = *(uint8_t *) pdata;
 ++                     if (cstate_len > 0) {
 ++                             cstate = (sdp_cstate_t *)pdata;
 ++                             SDPDBG("Cont state length: %d\n", cstate_len);
 ++                     } else
 ++                             cstate = NULL;
 ++             }
 ++     } while (cstate);
 ++
 ++end:
 ++     if (reqbuf)
 ++             free(reqbuf);
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++
 ++     return status;
 ++}
 ++
 ++/*
 ++ * This is a service attribute request. 
 ++ *
 ++ * INPUT :
 ++ *
 ++ *   uint32_t handle
 ++ *     The handle of the service for which the attribute(s) are
 ++ *     requested
 ++ *
 ++ *   sdp_attrreq_type_t reqtype
 ++ *     Attribute identifiers are 16 bit unsigned integers specified
 ++ *     in one of 2 ways described below :
 ++ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
 ++ *        They are the actual attribute identifiers in ascending order
 ++ *
 ++ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
 ++ *        The high-order 16bits is the start of range
 ++ *        the low-order 16bits are the end of range
 ++ *        0x0000 to 0xFFFF gets all attributes
 ++ *
 ++ *   sdp_list_t *attrid
 ++ *     Singly linked list containing attribute identifiers desired.
 ++ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)  
 ++ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
 ++ *
 ++ * OUTPUT :
 ++ *   return sdp_record_t *
 ++ *     0:
 ++ *       On any error and sets errno
 ++ *     !0:
 ++ *    The service record
 ++ */
 ++sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, 
 ++                     sdp_attrreq_type_t reqtype, const sdp_list_t *attrids)
 ++{
 ++     int status = 0;
 ++     uint32_t reqsize = 0, _reqsize;
 ++     uint32_t rspsize = 0, rsp_count;
 ++     int attr_list_len = 0;
 ++     int seqlen = 0, pdata_len;
 ++     uint8_t *pdata, *_pdata;
 ++     uint8_t *reqbuf, *rspbuf;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     sdp_cstate_t *cstate = NULL;
 ++     uint8_t cstate_len = 0;
 ++     sdp_buf_t rsp_concat_buf;
 ++     sdp_record_t *rec = 0;
 ++
 ++     if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
 ++             errno = EINVAL;
 ++             return 0;
 ++     }
 ++
 ++     reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!reqbuf || !rspbuf) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     memset((char *) &rsp_concat_buf, 0, sizeof(sdp_buf_t));
 ++     reqhdr = (sdp_pdu_hdr_t *) reqbuf;
 ++     reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
 ++
 ++     pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add the service record handle
 ++     bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
 ++     reqsize += sizeof(uint32_t);
 ++     pdata += sizeof(uint32_t);
 ++
 ++     // specify the response limit
 ++     bt_put_unaligned(htons(65535), (uint16_t *) pdata);
 ++     reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     // get attr seq PDU form
 ++     seqlen = gen_attridseq_pdu(pdata, attrids, 
 ++             reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
 ++     if (seqlen == -1) {
 ++             errno = EINVAL;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++     pdata += seqlen;
 ++     reqsize += seqlen;
 ++     SDPDBG("Attr list length : %d\n", seqlen);
 ++
 ++     // save before Continuation State
 ++     _pdata = pdata;
 ++     _reqsize = reqsize;
 ++
 ++     do {
 ++             // add NULL continuation state
 ++             reqsize = _reqsize + copy_cstate(_pdata,
 ++                                     SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
 ++
 ++             // set the request header's param length
 ++             reqhdr->tid  = htons(sdp_gen_tid(session));
 ++             reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++
 ++             status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
 ++             if (status < 0)
 ++                     goto end;
 ++
 ++             if (rspsize < sizeof(sdp_pdu_hdr_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             rsp_count = 0;
 ++             rsphdr = (sdp_pdu_hdr_t *) rspbuf;
 ++             if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++                     SDPDBG("PDU ID : 0x%x\n", rsphdr->pdu_id);
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++             pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++             pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
 ++
 ++             if (pdata_len < sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             attr_list_len += rsp_count;
 ++             pdata += sizeof(uint16_t);
 ++             pdata_len -= sizeof(uint16_t);
 ++
 ++             // if continuation state set need to re-issue request before parsing
 ++             if (pdata_len < rsp_count + sizeof(uint8_t)) {
 ++                     SDPERR("Unexpected end of packet: continuation state data missing");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++             cstate_len = *(uint8_t *) (pdata + rsp_count);
 ++
 ++             SDPDBG("Response id : %d\n", rsphdr->pdu_id);
 ++             SDPDBG("Attrlist byte count : %d\n", rsp_count);
 ++             SDPDBG("sdp_cstate_t length : %d\n", cstate_len);
 ++
 ++             /*
 ++              * a split response: concatenate intermediate responses 
 ++              * and the last one (which has cstate_len == 0)
 ++              */
 ++             if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
 ++                     uint8_t *targetPtr = NULL;
 ++
 ++                     cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
 ++
 ++                     // build concatenated response buffer
 ++                     rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
 ++                     rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
 ++                     targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
 ++                     memcpy(targetPtr, pdata, rsp_count);
 ++                     rsp_concat_buf.data_size += rsp_count;
 ++             }
 ++     } while (cstate);
 ++
 ++     if (attr_list_len > 0) {
 ++             int scanned = 0;
 ++             if (rsp_concat_buf.data_size != 0) {
 ++                     pdata = rsp_concat_buf.data;
 ++                     pdata_len = rsp_concat_buf.data_size;
 ++             }
 ++             rec = sdp_extract_pdu_safe(pdata, pdata_len, &scanned);
 ++
 ++             if (!rec)
 ++                     status = -1;
 ++     }
 ++     
 ++end:
 ++     if (reqbuf)
 ++             free(reqbuf);
 ++     if (rsp_concat_buf.data)
 ++             free(rsp_concat_buf.data);
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++     return rec;
 ++}
 ++
 ++/*
 ++ * SDP transaction structure for asynchronous search
 ++ */
 ++struct sdp_transaction {
 ++     sdp_callback_t *cb;     /* called when the transaction finishes */
 ++     void *udata;            /* client user data */
 ++     uint8_t *reqbuf;        /* pointer to request PDU */
 ++     sdp_buf_t rsp_concat_buf;
 ++     uint32_t reqsize;       /* without cstate */
 ++     int err;                /* ZERO if success or the errno if failed */
 ++};
 ++
 ++/*
 ++ * Creates a new sdp session for asynchronous search
 ++ * INPUT:
 ++ *  int sk
 ++ *     non-blocking L2CAP socket
 ++ *
 ++ * RETURN:
 ++ *  sdp_session_t *
 ++ *  NULL - On memory allocation failure
 ++ */
 ++sdp_session_t *sdp_create(int sk, uint32_t flags)
 ++{
 ++     sdp_session_t *session;
 ++     struct sdp_transaction *t;
 ++
 ++     session = malloc(sizeof(sdp_session_t));
 ++     if (!session) {
 ++             errno = ENOMEM;
 ++             return NULL;
 ++     }
 ++     memset(session, 0, sizeof(*session));
 ++
 ++     session->flags = flags;
 ++     session->sock = sk;
 ++
 ++     t = malloc(sizeof(struct sdp_transaction));
 ++     if (!t) {
 ++             errno = ENOMEM;
 ++             free(session);
 ++             return NULL;
 ++     }
 ++     memset(t, 0, sizeof(*t));
 ++
 ++     session->priv = t;
 ++
 ++     return session;
 ++}
 ++
 ++/*
 ++ * Sets the callback function/user data used to notify the application
 ++ * that the asynchronous transaction finished. This function must be
 ++ * called before request an asynchronous search.
 ++ *
 ++ * INPUT:
 ++ *  sdp_session_t *session
 ++ *   Current sdp session to be handled
 ++ *  sdp_callback_t *cb
 ++ *      callback to be called when the transaction finishes
 ++ *  void *udata
 ++ *      user data passed to callback
 ++ * RETURN:
 ++ *    0 - Success
 ++ *   -1 - Failure
 ++ */
 ++int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata)
 ++{
 ++     struct sdp_transaction *t;
 ++
 ++     if (!session || !session->priv)
 ++             return -1;
 ++
 ++     t = session->priv;
 ++     t->cb = func;
 ++     t->udata = udata;
 ++
 ++     return 0;
 ++}
 ++
 ++/*
 ++ * This function starts an asynchronous service search request.
 ++ * The incomming and outgoing data are stored in the transaction structure 
 ++ * buffers. When there is incomming data the sdp_process function must be
 ++ * called to get the data and handle the continuation state.
 ++ *
 ++ * INPUT :
 ++ *  sdp_session_t *session
 ++ *     Current sdp session to be handled
 ++ *
 ++ *   sdp_list_t *search
 ++ *     Singly linked list containing elements of the search
 ++ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
 ++ *     of the service to be searched
 ++ *
 ++ *   uint16_t max_rec_num
 ++ *      A 16 bit integer which tells the service, the maximum
 ++ *      entries that the client can handle in the response. The
 ++ *      server is obliged not to return > max_rec_num entries
 ++ *
 ++ * OUTPUT :
 ++ *
 ++ *   int return value
 ++ *   0  - if the request has been sent properly
 ++ *   -1 - On any failure and sets errno
 ++ */
 ++
 ++int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num)
 ++{
 ++     struct sdp_transaction *t;
 ++     sdp_pdu_hdr_t *reqhdr;
 ++     uint8_t *pdata;
 ++     int cstate_len, seqlen = 0;
 ++
 ++     if (!session || !session->priv)
 ++             return -1;
 ++
 ++     t = session->priv;
 ++
 ++     /* check if the buffer is already allocated */
 ++     if (t->rsp_concat_buf.data)
 ++             free(t->rsp_concat_buf.data);
 ++     memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
 ++
 ++     if (!t->reqbuf) {
 ++             t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++             if (!t->reqbuf) {
 ++                     t->err = ENOMEM;
 ++                     goto end;
 ++             }
 ++     }
 ++     memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
 ++
 ++     reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
 ++     reqhdr->tid = htons(sdp_gen_tid(session));
 ++     reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
 ++
 ++     // generate PDU
 ++     pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     t->reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add service class IDs for search
 ++     seqlen = gen_searchseq_pdu(pdata, search);
 ++
 ++     SDPDBG("Data seq added : %d\n", seqlen);
 ++
 ++     // now set the length and increment the pointer
 ++     t->reqsize += seqlen;
 ++     pdata += seqlen;
 ++
 ++     bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
 ++     t->reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     // set the request header's param length
 ++     cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
 ++     reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
 ++
 ++     if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
 ++             SDPERR("Error sendind data:%s", strerror(errno));
 ++             t->err = errno;
 ++             goto end;
 ++     }
 ++
 ++     return 0;
 ++end:
 ++
 ++     if (t->reqbuf) {
 ++             free(t->reqbuf);
 ++             t->reqbuf = NULL;
 ++     }
 ++
 ++     return -1;
 ++}
 ++
 ++/*
 ++ * This function starts an asynchronous service attribute request.
 ++ * The incomming and outgoing data are stored in the transaction structure 
 ++ * buffers. When there is incomming data the sdp_process function must be
 ++ * called to get the data and handle the continuation state.
 ++ *
 ++ * INPUT :
 ++ *  sdp_session_t *session
 ++ *   Current sdp session to be handled
 ++ *
 ++ *   uint32_t handle
 ++ *     The handle of the service for which the attribute(s) are
 ++ *     requested
 ++ *
 ++ *   sdp_attrreq_type_t reqtype
 ++ *     Attribute identifiers are 16 bit unsigned integers specified
 ++ *     in one of 2 ways described below :
 ++ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
 ++ *        They are the actual attribute identifiers in ascending order
 ++ *
 ++ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
 ++ *        The high-order 16bits is the start of range
 ++ *        the low-order 16bits are the end of range
 ++ *        0x0000 to 0xFFFF gets all attributes
 ++ *
 ++ *   sdp_list_t *attrid_list
 ++ *     Singly linked list containing attribute identifiers desired.
 ++ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)  
 ++ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
 ++ *
 ++ * OUTPUT :
 ++ *   int return value
 ++ *    0 - if the request has been sent properly
 ++ *   -1 - On any failure and sets errno
 ++ */
 ++
 ++int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
 ++{
 ++     struct sdp_transaction *t;
 ++     sdp_pdu_hdr_t *reqhdr;
 ++     uint8_t *pdata;
 ++     int cstate_len, seqlen = 0;
 ++
 ++     if (!session || !session->priv)
 ++             return -1;
 ++
 ++     t = session->priv;
 ++
 ++     /* check if the buffer is already allocated */
 ++     if (t->rsp_concat_buf.data)
 ++             free(t->rsp_concat_buf.data);
 ++     memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
 ++
 ++     if (!t->reqbuf) {
 ++             t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++             if (!t->reqbuf) {
 ++                     t->err = ENOMEM;
 ++                     goto end;
 ++             }
 ++     }
 ++     memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
 ++
 ++     reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
 ++     reqhdr->tid = htons(sdp_gen_tid(session));
 ++     reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
 ++
 ++     // generate PDU
 ++     pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     t->reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add the service record handle
 ++     bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
 ++     t->reqsize += sizeof(uint32_t);
 ++     pdata += sizeof(uint32_t);
 ++
 ++     // specify the response limit
 ++     bt_put_unaligned(htons(65535), (uint16_t *) pdata);
 ++     t->reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     // get attr seq PDU form
 ++     seqlen = gen_attridseq_pdu(pdata, attrid_list,
 ++                     reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
 ++     if (seqlen == -1) {
 ++             t->err = EINVAL;
 ++             goto end;
 ++     }
 ++
 ++     // now set the length and increment the pointer
 ++     t->reqsize += seqlen;
 ++     pdata += seqlen;
 ++     SDPDBG("Attr list length : %d\n", seqlen);
 ++
 ++     // set the request header's param length
 ++     cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
 ++     reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
 ++
 ++     if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
 ++             SDPERR("Error sendind data:%s", strerror(errno));
 ++             t->err = errno;
 ++             goto end;
 ++     }
 ++
 ++     return 0;
 ++end:
 ++
 ++     if (t->reqbuf) {
 ++             free(t->reqbuf);
 ++             t->reqbuf = NULL;
 ++     }
 ++
 ++     return -1;
 ++}
 ++
 ++/*
 ++ * This function starts an asynchronous service search attributes.
 ++ * It is a service search request combined with attribute request. The incomming
 ++ * and outgoing data are stored in the transaction structure buffers. When there
 ++ * is incomming data the sdp_process function must be called to get the data
 ++ * and handle the continuation state.
 ++ *
 ++ * INPUT:
 ++ *  sdp_session_t *session
 ++ *   Current sdp session to be handled
 ++ *
 ++ *   sdp_list_t *search
 ++ *     Singly linked list containing elements of the search
 ++ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
 ++ *     of the service to be searched
 ++ *
 ++ *   AttributeSpecification attrSpec
 ++ *     Attribute identifiers are 16 bit unsigned integers specified
 ++ *     in one of 2 ways described below :
 ++ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
 ++ *        They are the actual attribute identifiers in ascending order
 ++ *
 ++ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
 ++ *        The high-order 16bits is the start of range
 ++ *        the low-order 16bits are the end of range
 ++ *        0x0000 to 0xFFFF gets all attributes
 ++ *
 ++ *   sdp_list_t *attrid_list
 ++ *     Singly linked list containing attribute identifiers desired.
 ++ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)  
 ++ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
 ++ *
 ++
 ++ * RETURN:
 ++ *    0 - if the request has been sent properly
 ++ *   -1 - On any failure
 ++ */
 ++int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
 ++{
 ++     struct sdp_transaction *t;
 ++     sdp_pdu_hdr_t *reqhdr;
 ++     uint8_t *pdata;
 ++     int cstate_len, seqlen = 0;
 ++
 ++     if (!session || !session->priv)
 ++             return -1;
 ++
 ++     t = session->priv;
 ++
 ++     /* check if the buffer is already allocated */
 ++     if (t->rsp_concat_buf.data)
 ++             free(t->rsp_concat_buf.data);
 ++     memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
 ++
 ++     if (!t->reqbuf) {
 ++             t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++             if (!t->reqbuf) {
 ++                     t->err = ENOMEM;
 ++                     goto end;
 ++             }
 ++     }
 ++     memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
 ++
 ++     reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
 ++     reqhdr->tid = htons(sdp_gen_tid(session));
 ++     reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
 ++
 ++     // generate PDU
 ++     pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     t->reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add service class IDs for search
 ++     seqlen = gen_searchseq_pdu(pdata, search);
 ++
 ++     SDPDBG("Data seq added : %d\n", seqlen);
 ++
 ++     // now set the length and increment the pointer
 ++     t->reqsize += seqlen;
 ++     pdata += seqlen;
 ++
 ++     bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
 ++     t->reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
 ++
 ++     // get attr seq PDU form
 ++     seqlen = gen_attridseq_pdu(pdata, attrid_list,
 ++                     reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
 ++     if (seqlen == -1) {
 ++             t->err = EINVAL;
 ++             goto end;
 ++     }
 ++
 ++     pdata += seqlen;
 ++     SDPDBG("Attr list length : %d\n", seqlen);
 ++     t->reqsize += seqlen;
 ++
 ++     // set the request header's param length
 ++     cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
 ++     reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
 ++
 ++     if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
 ++             SDPERR("Error sendind data:%s", strerror(errno));
 ++             t->err = errno;
 ++             goto end;
 ++     }
 ++
 ++     return 0;
 ++end:
 ++
 ++     if (t->reqbuf) {
 ++             free(t->reqbuf);
 ++             t->reqbuf = NULL;
 ++     }
 ++
 ++     return -1;
 ++}
 ++
 ++/*
 ++ * Function used to get the error reason after sdp_callback_t function has been called
 ++ * and the status is 0xffff or if sdp_service_{search, attr, search_attr}_async returns -1.
 ++ * It indicates that an error NOT related to SDP_ErrorResponse happened. Get errno directly
 ++ * is not safe because multiple transactions can be triggered.
 ++ * This function must be used with asynchronous sdp functions only.
 ++ *
 ++ * INPUT:
 ++ *  sdp_session_t *session
 ++ *   Current sdp session to be handled
 ++ * RETURN:
 ++ *    0 = No error in the current transaction
 ++ *   -1 - if the session is invalid
 ++ *   positive value - the errno value
 ++ *
 ++ */
 ++int sdp_get_error(sdp_session_t *session)
 ++{
 ++     struct sdp_transaction *t;
 ++
 ++     if (!session || !session->priv) {
 ++             SDPERR("Invalid session");
 ++             return -1;
 ++     }
 ++
 ++     t = session->priv;
 ++
 ++     return t->err;
 ++}
 ++
 ++/*
 ++ * Receive the incomming SDP PDU. This function must be called when there is data
 ++ * available to be read. On continuation state, the original request (with a new
 ++ * transaction ID) and the continuation state data will be appended in the initial PDU.
 ++ * If an error happens or the transaction finishes the callback function will be called.
 ++ *
 ++ * INPUT:
 ++ *  sdp_session_t *session
 ++ *   Current sdp session to be handled
 ++ * RETURN:
 ++ *   0  - if the transaction is on continuation state
 ++ *   -1 - On any failure or the transaction finished
 ++ */
 ++int sdp_process(sdp_session_t *session)
 ++{
 ++     struct sdp_transaction *t;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     sdp_cstate_t *pcstate;
 ++     uint8_t *pdata, *rspbuf, *targetPtr;
 ++     int rsp_count, err = -1;
 ++     size_t size = 0;
 ++     int n, plen;
 ++     uint16_t status = 0xffff;
 ++     uint8_t pdu_id = 0x00;
 ++
 ++     if (!session || !session->priv) {
 ++             SDPERR("Invalid session");
 ++             return -1;
 ++     }
 ++
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!rspbuf) {
 ++             SDPERR("Response buffer alloc failure:%s (%d)",
 ++                             strerror(errno), errno);
 ++             return -1;
 ++     }
 ++
 ++     memset(rspbuf, 0, SDP_RSP_BUFFER_SIZE);
 ++
 ++     t = session->priv;
 ++     reqhdr = (sdp_pdu_hdr_t *)t->reqbuf;
 ++     rsphdr = (sdp_pdu_hdr_t *)rspbuf;
 ++
 ++     pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++
 ++     n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
 ++     if (n < 0) {
 ++             SDPERR("Read response:%s (%d)", strerror(errno), errno);
 ++             t->err = errno;
 ++             goto end;
 ++     }
 ++
 ++     if (n == 0 || reqhdr->tid != rsphdr->tid ||
 ++             (n != (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t)))) {
 ++             t->err = EPROTO;
 ++             SDPERR("Protocol error.");
 ++             goto end;
 ++     }
 ++
 ++     pdu_id = rsphdr->pdu_id;
 ++     switch (rsphdr->pdu_id) {
 ++     uint8_t *ssr_pdata;
 ++     uint16_t tsrc, csrc;
 ++     case SDP_SVC_SEARCH_RSP:
 ++             /*
 ++              * TSRC: Total Service Record Count (2 bytes)
 ++              * CSRC: Current Service Record Count (2 bytes)
 ++              */
 ++             ssr_pdata = pdata;
 ++             tsrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
 ++             ssr_pdata += sizeof(uint16_t);
 ++             csrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
 ++
 ++             /* csrc should never be larger than tsrc */
 ++             if (csrc > tsrc) {
 ++                     t->err = EPROTO;
 ++                     SDPERR("Protocol error: wrong current service record count value.");
 ++                     goto end;
 ++             }
 ++
 ++             SDPDBG("Total svc count: %d\n", tsrc);
 ++             SDPDBG("Current svc count: %d\n", csrc);
 ++
 ++             /* parameter length without continuation state */
 ++             plen = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
 ++
 ++             if (t->rsp_concat_buf.data_size == 0) {
 ++                     /* first fragment */
 ++                     rsp_count = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
 ++             } else {
 ++                     /* point to the first csrc */
 ++                     uint16_t *pcsrc = (uint16_t *) (t->rsp_concat_buf.data + 2);
 ++
 ++                     /* FIXME: update the interface later. csrc doesn't need be passed to clients */
 ++
 ++                     pdata += sizeof(uint16_t); /* point to csrc */
 ++
 ++                     /* the first csrc contains the sum of partial csrc responses */
 ++                     *pcsrc += bt_get_unaligned((uint16_t *) pdata); 
 ++
 ++                     pdata += sizeof(uint16_t); /* point to the first handle */
 ++                     rsp_count = csrc * 4;
 ++             }
 ++             status = 0x0000;
 ++             break;
 ++     case SDP_SVC_ATTR_RSP:
 ++     case SDP_SVC_SEARCH_ATTR_RSP:
 ++             rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             SDPDBG("Attrlist byte count : %d\n", rsp_count);
 ++     
 ++             /* 
 ++              * Number of bytes in the AttributeLists parameter(without
 ++              * continuation state) + AttributeListsByteCount field size.
 ++              */
 ++             plen = sizeof(uint16_t) + rsp_count;
 ++
 ++             pdata += sizeof(uint16_t); // points to attribute list
 ++             status = 0x0000;
 ++             break;
 ++     case SDP_ERROR_RSP:
 ++             status = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             size = ntohs(rsphdr->plen);
 ++
 ++             /* error code + error info */
 ++             plen = size;
 ++             goto end;
 ++     default:
 ++             t->err = EPROTO;
 ++             SDPERR("Illegal PDU ID: 0x%x", rsphdr->pdu_id);
 ++             goto end;
 ++     }
 ++
 ++     pcstate = (sdp_cstate_t *) (pdata + rsp_count);
 ++
 ++     SDPDBG("Cstate length : %d\n", pcstate->length);
 ++
 ++     /* 
 ++      * Check out of bound. Continuation state must have at least
 ++      * 1 byte: ZERO to indicate that it is not a partial response.
 ++      */
 ++     if ((n - sizeof(sdp_pdu_hdr_t))  != (plen + pcstate->length + 1)) {
 ++             t->err = EPROTO;
 ++             SDPERR("Protocol error: wrong PDU size.");
 ++             status = 0xffff;
 ++             goto end;
 ++     }
 ++
 ++     /*
 ++      * This is a split response, need to concatenate intermediate
 ++      * responses and the last one which will have cstate length == 0
 ++      */
 ++     t->rsp_concat_buf.data = realloc(t->rsp_concat_buf.data, t->rsp_concat_buf.data_size + rsp_count);
 ++     targetPtr = t->rsp_concat_buf.data + t->rsp_concat_buf.data_size;
 ++     t->rsp_concat_buf.buf_size = t->rsp_concat_buf.data_size + rsp_count;
 ++     memcpy(targetPtr, pdata, rsp_count);
 ++     t->rsp_concat_buf.data_size += rsp_count;
 ++
 ++     if (pcstate->length > 0) {
 ++             int reqsize, cstate_len;
 ++
 ++             reqhdr->tid = htons(sdp_gen_tid(session));
 ++
 ++             // add continuation state
 ++             cstate_len = copy_cstate(t->reqbuf + t->reqsize,
 ++                             SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate);
 ++
 ++             reqsize = t->reqsize + cstate_len;
 ++
 ++             // set the request header's param length
 ++             reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++     
 ++             if (sdp_send_req(session, t->reqbuf, reqsize) < 0) {
 ++                     SDPERR("Error sendind data:%s(%d)", strerror(errno), errno);
 ++                     status = 0xffff;
 ++                     t->err = errno;
 ++                     goto end;
 ++             }
 ++             err = 0;
 ++     }
 ++
 ++end:
 ++     if (err) {
 ++             if (t->rsp_concat_buf.data_size != 0) {
 ++                     pdata = t->rsp_concat_buf.data;
 ++                     size = t->rsp_concat_buf.data_size;
 ++             }
 ++             if (t->cb)
 ++                     t->cb(pdu_id, status, pdata, size, t->udata);
 ++     }
 ++
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++
 ++     return err;
 ++}
 ++
 ++/*
 ++ * This is a service search request combined with the service
 ++ * attribute request. First a service class match is done and
 ++ * for matching service, requested attributes are extracted
 ++ *
 ++ * INPUT :
 ++ *
 ++ *   sdp_list_t *search
 ++ *     Singly linked list containing elements of the search
 ++ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
 ++ *     of the service to be searched
 ++ *
 ++ *   AttributeSpecification attrSpec
 ++ *     Attribute identifiers are 16 bit unsigned integers specified
 ++ *     in one of 2 ways described below :
 ++ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
 ++ *        They are the actual attribute identifiers in ascending order
 ++ *
 ++ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
 ++ *        The high-order 16bits is the start of range
 ++ *        the low-order 16bits are the end of range
 ++ *        0x0000 to 0xFFFF gets all attributes
 ++ *
 ++ *   sdp_list_t *attrids
 ++ *     Singly linked list containing attribute identifiers desired.
 ++ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)  
 ++ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
 ++ *
 ++ * OUTPUT :
 ++ *   int return value
 ++ *     0:
 ++ *       The request completed successfully. This does not
 ++ *       mean the requested services were found
 ++ *     -1:
 ++ *       On any error and sets errno
 ++ *
 ++ *   sdp_list_t **rsp
 ++ *     This variable is set on a successful return to point to
 ++ *     service(s) found. Each element of this list is of type
 ++ *     sdp_record_t* (of the services which matched the search list)
 ++ */
 ++int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids, sdp_list_t **rsp)
 ++{
 ++     int status = 0;
 ++     uint32_t reqsize = 0, _reqsize;
 ++     uint32_t rspsize = 0;
 ++     int seqlen = 0, attr_list_len = 0;
 ++     int rsp_count = 0, cstate_len = 0, pdata_len;
 ++     uint8_t *pdata, *_pdata;
 ++     uint8_t *reqbuf, *rspbuf;
 ++     sdp_pdu_hdr_t *reqhdr, *rsphdr;
 ++     uint8_t dataType;
 ++     sdp_list_t *rec_list = NULL;
 ++     sdp_buf_t rsp_concat_buf;
 ++     sdp_cstate_t *cstate = NULL;
 ++
 ++     if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
 ++             errno = EINVAL;
 ++             return -1;
 ++     }
 ++     reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
 ++     rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
 ++     if (!reqbuf || !rspbuf) {
 ++             errno = ENOMEM;
 ++             status = -1;
 ++             goto end;
 ++     }
 ++
 ++     memset((char *)&rsp_concat_buf, 0, sizeof(sdp_buf_t));
 ++     reqhdr = (sdp_pdu_hdr_t *) reqbuf;
 ++     reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
 ++
 ++     // generate PDU
 ++     pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
 ++     reqsize = sizeof(sdp_pdu_hdr_t);
 ++
 ++     // add service class IDs for search
 ++     seqlen = gen_searchseq_pdu(pdata, search);
 ++
 ++     SDPDBG("Data seq added : %d\n", seqlen);
 ++
 ++     // now set the length and increment the pointer
 ++     reqsize += seqlen;
 ++     pdata += seqlen;
 ++
 ++     bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
 ++     reqsize += sizeof(uint16_t);
 ++     pdata += sizeof(uint16_t);
 ++
 ++     SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
 ++
 ++     // get attr seq PDU form 
 ++     seqlen = gen_attridseq_pdu(pdata, attrids,
 ++             reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
 ++     if (seqlen == -1) {
 ++             status = EINVAL;
 ++             goto end;
 ++     }
 ++     pdata += seqlen;
 ++     SDPDBG("Attr list length : %d\n", seqlen);
 ++     reqsize += seqlen;
 ++     *rsp = 0;
 ++
 ++     // save before Continuation State
 ++     _pdata = pdata;
 ++     _reqsize = reqsize;
 ++
 ++     do {
 ++             reqhdr->tid = htons(sdp_gen_tid(session));
 ++
 ++             // add continuation state (can be null)
 ++             reqsize = _reqsize + copy_cstate(_pdata,
 ++                                     SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
 ++
 ++             // set the request header's param length
 ++             reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
 ++             rsphdr = (sdp_pdu_hdr_t *) rspbuf;
 ++             status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
 ++             if (rspsize < sizeof(sdp_pdu_hdr_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             if (status < 0) {
 ++                     SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
 ++                     goto end;
 ++             }
 ++       
 ++             if (rsphdr->pdu_id == SDP_ERROR_RSP) {
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++       
 ++             pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
 ++             pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
 ++
 ++             if (pdata_len < sizeof(uint16_t)) {
 ++                     SDPERR("Unexpected end of packet");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
 ++             attr_list_len += rsp_count;
 ++             pdata += sizeof(uint16_t);      // pdata points to attribute list
 ++             pdata_len -= sizeof(uint16_t);
 ++
 ++             if (pdata_len < rsp_count + sizeof(uint8_t)) {
 ++                     SDPERR("Unexpected end of packet: continuation state data missing");
 ++                     status = -1;
 ++                     goto end;
 ++             }
 ++
 ++             cstate_len = *(uint8_t *) (pdata + rsp_count);
 ++
 ++             SDPDBG("Attrlist byte count : %d\n", attr_list_len);
 ++             SDPDBG("Response byte count : %d\n", rsp_count);
 ++             SDPDBG("Cstate length : %d\n", cstate_len);
 ++             /*
 ++              * This is a split response, need to concatenate intermediate
 ++              * responses and the last one which will have cstate_len == 0
 ++              */
 ++             if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
 ++                     uint8_t *targetPtr = NULL;
 ++
 ++                     cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
 ++
 ++                     // build concatenated response buffer
 ++                     rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
 ++                     targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
 ++                     rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
 ++                     memcpy(targetPtr, pdata, rsp_count);
 ++                     rsp_concat_buf.data_size += rsp_count;
 ++             }
 ++     } while (cstate);
 ++
 ++     if (attr_list_len > 0) {
 ++             int scanned = 0;
 ++
 ++             if (rsp_concat_buf.data_size != 0) {
 ++                     pdata = rsp_concat_buf.data;
 ++                     pdata_len = rsp_concat_buf.data_size;
 ++             }
 ++
 ++             /*
 ++              * Response is a sequence of sequence(s) for one or
 ++              * more data element sequence(s) representing services
 ++              * for which attributes are returned
 ++              */
 ++             scanned = sdp_extract_seqtype_safe(pdata, pdata_len, &dataType, &seqlen);
 ++
 ++             SDPDBG("Bytes scanned : %d\n", scanned);
 ++             SDPDBG("Seq length : %d\n", seqlen);
 ++
 ++             if (scanned && seqlen) {
 ++                     pdata += scanned;
 ++                     pdata_len -= scanned;
 ++                     do {
 ++                             int recsize = 0;
 ++                             sdp_record_t *rec = sdp_extract_pdu_safe(pdata, pdata_len, &recsize);
 ++                             if (rec == NULL) {
 ++                                     SDPERR("SVC REC is null\n");
 ++                                     status = -1;
 ++                                     goto end;
 ++                             }
 ++                             if (!recsize) {
 ++                                     sdp_record_free(rec);
 ++                                     break;
 ++                             }
 ++                             scanned += recsize;
 ++                             pdata += recsize;
 ++                             pdata_len -= recsize;
 ++
 ++                             SDPDBG("Loc seq length : %d\n", recsize);
 ++                             SDPDBG("Svc Rec Handle : 0x%x\n", rec->handle);
 ++                             SDPDBG("Bytes scanned : %d\n", scanned);
 ++                             SDPDBG("Attrlist byte count : %d\n", attr_list_len);
 ++                             rec_list = sdp_list_append(rec_list, rec);
 ++                     } while (scanned < attr_list_len && pdata_len > 0);
 ++
 ++                     SDPDBG("Successful scan of service attr lists\n");
 ++                     *rsp = rec_list;
 ++             }
 ++     }
 ++  end:
 ++     if (rsp_concat_buf.data)
 ++             free(rsp_concat_buf.data);
 ++     if (reqbuf)
 ++             free(reqbuf);
 ++     if (rspbuf)
 ++             free(rspbuf);
 ++     return status;
 ++}
 ++
 ++/*
 ++ * Find devices in the piconet.
 ++ */
 ++int sdp_general_inquiry(inquiry_info *ii, int num_dev, int duration, uint8_t *found)
 ++{
 ++     int n = hci_inquiry(-1, 10, num_dev, NULL, &ii, 0);
 ++     if (n < 0) {
 ++             SDPERR("Inquiry failed:%s", strerror(errno));
 ++             return -1;
 ++     }
 ++     *found = n;
 ++     return 0;
 ++}
 ++
 ++int sdp_close(sdp_session_t *session)
 ++{
 ++     struct sdp_transaction *t;
 ++     int ret;
 ++     
 ++     if (!session)
 ++             return -1;
 ++
 ++     ret = close(session->sock);
 ++
 ++     t = session->priv;
 ++
 ++     if (t) {
 ++             if (t->reqbuf)
 ++                     free(t->reqbuf);
 ++
 ++             if (t->rsp_concat_buf.data)
 ++                     free(t->rsp_concat_buf.data);
 ++
 ++             free(t);
 ++     }
 ++     free(session);
 ++     return ret;
 ++}
 ++
 ++static inline int sdp_is_local(const bdaddr_t *device)
 ++{
 ++     return memcmp(device, BDADDR_LOCAL, sizeof(bdaddr_t)) == 0;
 ++}
 ++
 ++static int sdp_connect_local(sdp_session_t *session)
 ++{
 ++     struct sockaddr_un sa;
 ++
 ++     session->sock = socket(PF_UNIX, SOCK_STREAM, 0);
 ++     if (session->sock < 0)
 ++             return -1;
 ++     session->local = 1;
 ++
 ++     sa.sun_family = AF_UNIX;
 ++     strcpy(sa.sun_path, SDP_UNIX_PATH);
 ++
 ++     return connect(session->sock, (struct sockaddr *)&sa, sizeof(sa));
 ++}
 ++
 ++static int sdp_connect_l2cap(const bdaddr_t *src,
 ++                          const bdaddr_t *dst, sdp_session_t *session)
 ++{
 ++     uint32_t flags = session->flags;
 ++     struct sockaddr_l2 sa;
 ++     int sk;
 ++
 ++     session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
 ++     if (session->sock < 0)
 ++             return -1;
 ++     session->local = 0;
 ++
 ++     sk = session->sock;
 ++
 ++     if (flags & SDP_NON_BLOCKING) {
 ++             long arg = fcntl(sk, F_GETFL, 0);
 ++             fcntl(sk, F_SETFL, arg | O_NONBLOCK);
 ++     }
 ++
 ++     sa.l2_family = AF_BLUETOOTH;
 ++     sa.l2_psm = 0;
 ++
 ++     if (bacmp(src, BDADDR_ANY)) {
 ++             sa.l2_bdaddr = *src;
 ++             if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
 ++                     return -1;
 ++     }
 ++
 ++     if (flags & SDP_WAIT_ON_CLOSE) {
 ++             struct linger l = { .l_onoff = 1, .l_linger = 1 };
 ++             setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
 ++     }
 ++
 ++     sa.l2_psm = htobs(SDP_PSM);
 ++     sa.l2_bdaddr = *dst;
 ++
 ++     do {
 ++             int ret = connect(sk, (struct sockaddr *) &sa, sizeof(sa));
 ++             if (!ret)
 ++                     return 0;
 ++             if (ret < 0 && (flags & SDP_NON_BLOCKING) &&
 ++                 (errno == EAGAIN || errno == EINPROGRESS))
 ++                     return 0;
 ++     } while (errno == EBUSY && (flags & SDP_RETRY_IF_BUSY));
 ++
 ++     return -1;
 ++}
 ++
 ++sdp_session_t *sdp_connect(const bdaddr_t *src,
 ++                        const bdaddr_t *dst, uint32_t flags)
 ++{
 ++     sdp_session_t *session;
 ++     int err;
 ++
 ++     if ((flags & SDP_RETRY_IF_BUSY) && (flags & SDP_NON_BLOCKING)) {
 ++             errno = EINVAL;
 ++             return NULL;
 ++     }
 ++
 ++     session = sdp_create(-1, flags);
 ++     if (!session)
 ++             return NULL;
 ++
 ++     if (sdp_is_local(dst)) {
 ++             if (sdp_connect_local(session) < 0)
 ++                     goto fail;
 ++     } else {
 ++             if (sdp_connect_l2cap(src, dst, session) < 0)
 ++                     goto fail;
 ++     }
 ++
 ++     return session;
 ++
 ++fail:
 ++     err = errno;
 ++     if (session->sock >= 0)
 ++             close(session->sock);
 ++     if (session->priv)
 ++             free(session->priv);
 ++     free(session);
 ++     errno = err;
 ++
 ++     return NULL;
 ++}
 ++
 ++int sdp_get_socket(const sdp_session_t *session)
 ++{
 ++     return session->sock;
 ++}
 ++
 ++uint16_t sdp_gen_tid(sdp_session_t *session)
 ++{
 ++     return session->tid++;
 ++}
index 0000000,0000000,0000000..5053e7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,1 @@@@
+++include $(call all-subdir-makefiles)
diff --cc utils/NOTICE
Simple merge
index 0000000,0000000,0000000..b6ed509
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,56 @@@@
+++# Note: this currently does not build due to a dependency on
+++# libhciserver, which the Android build of bluez-utils/hcid
+++# does not produce.
+++
+++BUILD_HEADSETD:=0
+++ifeq ($(BUILD_HEADSETD),1)
+++
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# headsetd 
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, dbus) \
+++     $(call include-path-for, bluez-utils)/common \
+++     $(call include-path-for, bluez-utils)/sdpd \
+++     $(call include-path-for, bluez-utils)/eglib \
+++     $(call include-path-for, bluez-utils)/gdbus
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" \
+++     -DSTORAGEDIR=\"/data\" \
+++     -DCONFIGDIR=\"/etc\" \
+++     -DENABLE_DEBUG \
+++     -D__S_IFREG=0100000  # missing from bionic stat.h
+++
+++LOCAL_SRC_FILES:= \
+++     a2dp.c \
+++     avdtp.c \
+++     control.c \
+++     device.c \
+++     headset.c \
+++     ipc.c \
+++     sink.c \
+++     unix.c \
+++     manager.c \
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth \
+++     libdbus
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static \
+++     libeglib_static \
+++     libsdpserver_static \
+++     libgdbus_static
+++
+++LOCAL_MODULE:=headsetd
+++
+++include $(BUILD_EXECUTABLE)
+++
+++endif
index 0000000,0000000,0000000..d1b91bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,32 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_SRC_FILES:= \
+++     error.c \
+++     glib-helper.c \
+++     logging.c \
+++     oui.c \
+++     sdp-glib.c \
+++     sdp-xml.c \
+++     textfile.c \
+++     android_bluez.c
+++
+++LOCAL_C_INCLUDES:= \
+++     $(LOCAL_PATH) \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/eglib/ \
+++     $(call include-path-for, bluez-utils)/gdbus/ \
+++     external/expat/lib/ \
+++     $(call include-path-for, dbus)
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libeglib_static \
+++     libgdbus_static
+++
+++LOCAL_MODULE:=libbluez-utils-common-static
+++
+++LOCAL_CFLAGS+= \
+++     -O3 \
+++     -DNEED_DBUS_WATCH_GET_UNIX_FD
+++
+++include $(BUILD_STATIC_LIBRARY)
index 0000000,0000000,0000000..60317a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,20 @@@@
+++#include <private/android_filesystem_config.h>
+++#include <sys/prctl.h>
+++#include <linux/capability.h>
+++
+++/* Set UID to bluetooth w/ CAP_NET_RAW, CAP_NET_ADMIN and CAP_NET_BIND_SERVICE
+++ * (Android's init.rc does not yet support applying linux capabilities) */
+++void android_set_aid_and_cap() {
+++    prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+++    setuid(AID_BLUETOOTH);
+++
+++    struct __user_cap_header_struct header;
+++    struct __user_cap_data_struct cap;
+++    header.version = _LINUX_CAPABILITY_VERSION;
+++    header.pid = 0;
+++    cap.effective = cap.permitted = 1 << CAP_NET_RAW |
+++                                    1 << CAP_NET_ADMIN |
+++                                    1 << CAP_NET_BIND_SERVICE;
+++    cap.inheritable = 0;
+++    capset(&header, &cap);
+++}
index 0000000,0000000,0000000..e8aa861
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,32 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# hcid
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, dbus) \
+++     $(call include-path-for, bluez-utils)/common
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" \
+++     -DSTORAGEDIR=\"/data\" \
+++     -DCONFIGDIR=\"/etc\" \
+++
+++LOCAL_SRC_FILES:= \
+++     dun.c     \
+++     main.c    \
+++     msdun.c   \
+++     sdp.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth 
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=dund
+++
+++include $(BUILD_EXECUTABLE)
index 0000000,0000000,0000000..af13202
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,15 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_SRC_FILES:= \
+++     gmain.c \
+++     gmodule.c
+++
+++LOCAL_C_INCLUDES:= \
+++        $(LOCAL_PATH)
+++
+++LOCAL_MODULE:=libeglib_static
+++
+++LOCAL_CFLAGS+=-O3
+++
+++include $(BUILD_STATIC_LIBRARY)
index 0000000,0000000,1ca3d67..3475fdb
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,1976 +1,1977 @@@@
++ #include <stdio.h>
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <stdarg.h>
++ #include <string.h>
++ #include <limits.h>
++ #include <sys/time.h>
++ #include <time.h>
++ #include <sys/types.h>
++ #include <sys/wait.h>
++ #include <fcntl.h>
++ #include <sys/stat.h>
++ #include <sys/mman.h>
++ #include <sys/file.h>
++ #include <ctype.h>
++ #include <dlfcn.h>
++ #include <dirent.h>
+++#include <signal.h>
++ 
++ #include <gmain.h>
++ 
++ struct timeout {
++      guint id;
++      guint interval;
++      struct timeval expiration;
++      gpointer data;
++      GSourceFunc function;
++ };
++ 
++ struct _GIOChannel {
++      int fd;
++      int ref_count;
++      gboolean closed;
++      gboolean close_on_unref;
++ };
++ 
++ struct child_watch {
++      guint id;
++      GPid pid;
++      GChildWatchFunc function;
++      gpointer user_data;
++ };
++ 
++ struct _GMainContext {
++      guint next_id;
++      glong next_timeout;
++ 
++      GSList *timeouts;
++      GSList *proc_timeouts;
++      gboolean timeout_lock;
++ 
++      GSList *io_watches;
++      GSList *proc_io_watches;
++      gboolean io_lock;
++ 
++      GSList *child_watches;
++      GSList *proc_child_watches;
++      gboolean child_lock;
++ };
++ 
++ struct _GMainLoop {
++      gboolean is_running;
++      GMainContext *context;
++ };
++ 
++ struct _GDir
++ {
++      DIR *dirp;
++ };
++ 
++ GIOError g_io_channel_read(GIOChannel *channel, gchar *buf, gsize count,
++                              gsize *bytes_read)
++ {
++      int fd = channel->fd;
++      gssize result;
++ 
++      if (channel->closed)
++              return G_IO_STATUS_ERROR;
++ 
++      /* At least according to the Debian manpage for read */
++      if (count > SSIZE_MAX)
++              count = SSIZE_MAX;
++ 
++ retry:
++      result = read (fd, buf, count);
++ 
++      if (result < 0) {
++              *bytes_read = 0;
++ 
++              switch (errno) {
++ #ifdef EINTR
++              case EINTR:
++                      goto retry;
++ #endif
++ #ifdef EAGAIN
++              case EAGAIN:
++                      return G_IO_STATUS_AGAIN;
++ #endif
++              default:
++                      return G_IO_STATUS_ERROR;
++              }
++      }
++ 
++      *bytes_read = result;
++ 
++      return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
++ }
++ 
++ GIOError g_io_channel_write(GIOChannel *channel, const gchar *buf, gsize count,
++                              gsize *bytes_written)
++ {
++      int fd = channel->fd;
++      gssize result;
++ 
++      if (channel->closed)
++              return G_IO_STATUS_ERROR;
++ 
++      /* At least according to the Debian manpage for read */
++      if (count > SSIZE_MAX)
++              count = SSIZE_MAX;
++ 
++ retry:
++      result = write(fd, buf, count);
++ 
++      if (result < 0) {
++              *bytes_written = 0;
++ 
++              switch (errno) {
++ #ifdef EINTR
++              case EINTR:
++                      goto retry;
++ #endif
++ #ifdef EAGAIN
++              case EAGAIN:
++                      return G_IO_STATUS_AGAIN;
++ #endif
++              default:
++                      return G_IO_STATUS_ERROR;
++              }
++      }
++ 
++      *bytes_written = result;
++ 
++      return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
++ }
++ 
++ void g_io_channel_close(GIOChannel *channel)
++ {
++      if (!channel || channel->closed)
++              return;
++ 
++      close(channel->fd);
++ 
++      channel->closed = TRUE;
++ }
++ 
++ void g_io_channel_unref(GIOChannel *channel)
++ {
++      if (!channel)
++              return;
++ 
++      if (--channel->ref_count > 0)
++              return;
++ 
++      if (channel->close_on_unref && channel->fd >= 0)
++              g_io_channel_close(channel);
++ 
++      g_free(channel);
++ }
++ 
++ GIOChannel *g_io_channel_ref(GIOChannel *channel)
++ {
++      channel->ref_count++;
++      return channel;
++ }
++ 
++ GIOChannel *g_io_channel_unix_new(int fd)
++ {
++      GIOChannel *channel;
++ 
++      channel = g_new0(GIOChannel, 1);
++ 
++      channel->fd = fd;
++      channel->ref_count = 1;
++ 
++      return channel;
++ }
++ 
++ void g_io_channel_set_close_on_unref(GIOChannel *channel, gboolean do_close)
++ {
++      channel->close_on_unref = do_close;
++ }
++ 
++ gint g_io_channel_unix_get_fd(GIOChannel *channel)
++ {
++      if (channel->closed)
++              return -1;
++ 
++      return channel->fd;
++ }
++ 
++ static int set_flags(int fd, long flags)
++ {
++      long arg;
++ 
++      arg = fcntl(fd, F_GETFL);
++      if (arg < 0)
++              return -errno;
++ 
++      /* Return if already set */
++      if ((arg & flags) == flags)
++              return 0;
++ 
++      arg |= flags;
++      if (fcntl(fd, F_SETFL, arg) < 0)
++              return -errno;
++ 
++      return 0;
++ }
++ 
++ GIOStatus g_io_channel_set_flags(GIOChannel *channel, GIOFlags flags,
++                              GError **error)
++ {
++      int err, fd;
++      long fd_flags = 0;
++ 
++      if (!channel || channel->closed)
++              return G_IO_STATUS_ERROR;
++ 
++      fd = g_io_channel_unix_get_fd(channel);
++ 
++      if (flags & G_IO_FLAG_APPEND)
++              fd_flags |= O_APPEND;
++      if (flags & G_IO_FLAG_NONBLOCK)
++              fd_flags |= O_NONBLOCK;
++ 
++      err = set_flags(fd, fd_flags);
++      if (err < 0) {
++              if (error)
++                      g_set_error(error, 0, 0, "Unable to set flags: %s",
++                                      strerror(-err));
++              return G_IO_STATUS_ERROR;
++      }
++ 
++      return G_IO_STATUS_NORMAL;
++ }
++ 
++ struct io_watch {
++      guint id;
++      GIOChannel *channel;
++      gint priority;
++      GIOCondition condition;
++      short *revents;
++      GIOFunc func;
++      gpointer user_data;
++      GDestroyNotify destroy;
++ };
++ 
++ static GMainContext *default_context = NULL;
++ 
++ static void watch_free(struct io_watch *watch)
++ {
++      if (watch->destroy)
++              watch->destroy(watch->user_data);
++      g_io_channel_unref(watch->channel);
++      g_free(watch);
++ }
++ 
++ static GMainContext *g_main_context_default()
++ {
++      if (default_context)
++              return default_context;
++ 
++      default_context = g_new0(GMainContext, 1);
++ 
++      default_context->next_timeout = -1;
++      default_context->next_id = 1;
++ 
++      return default_context;
++ }
++ 
++ static gboolean g_io_remove_watch(GMainContext *context, guint id)
++ {
++      GSList *l;
++      struct io_watch *w;
++ 
++      for (l = context->io_watches; l != NULL; l = l->next) {
++              w = l->data;
++ 
++              if (w->id != id)
++                      continue;
++ 
++              context->io_watches = g_slist_remove(context->io_watches, w);
++              watch_free(w);
++ 
++              return TRUE;
++      }
++ 
++      for (l = context->proc_io_watches; l != NULL; l = l->next) {
++              w = l->data;
++ 
++              if (w->id != id)
++                      continue;
++ 
++              context->proc_io_watches = g_slist_remove(context->proc_io_watches, w);
++              watch_free(w);
++ 
++              return TRUE;
++      }
++ 
++      return FALSE;
++ }
++ 
++ static gboolean g_timeout_remove(GMainContext *context, const guint id)
++ {
++      GSList *l;
++      struct timeout *t;
++ 
++      l = context->timeouts;
++ 
++      while (l) {
++              t = l->data;
++              l = l->next;
++ 
++              if (t->id != id)
++                      continue;
++ 
++              context->timeouts = g_slist_remove(context->timeouts, t);
++              g_free(t);
++ 
++              return TRUE;
++      }
++ 
++      l = context->proc_timeouts;
++ 
++      while (l) {
++              t = l->data;
++              l = l->next;
++ 
++              if (t->id != id)
++                      continue;
++ 
++              context->proc_timeouts = g_slist_remove(context->proc_timeouts, t);
++              g_free(t);
++ 
++              return TRUE;
++      }
++ 
++      return FALSE;
++ }
++ 
++ int watch_prio_cmp(struct io_watch *w1, struct io_watch *w2)
++ {
++      return w1->priority - w2->priority;
++ }
++ 
++ #define watch_list_add(l, w) g_slist_insert_sorted((l), (w), (GCompareFunc) watch_prio_cmp)
++ 
++ guint g_io_add_watch_full(GIOChannel *channel, gint priority,
++                              GIOCondition condition, GIOFunc func,
++                              gpointer user_data, GDestroyNotify notify)
++ {
++      struct io_watch *watch;
++      GMainContext *context = g_main_context_default();
++ 
++      watch = g_new(struct io_watch, 1);
++ 
++      watch->id = context->next_id++;
++      watch->channel = g_io_channel_ref(channel);
++      watch->priority = priority;
++      watch->condition = condition;
++      watch->func = func;
++      watch->user_data = user_data;
++      watch->destroy = notify;
++ 
++      if (context->io_lock)
++              context->proc_io_watches = watch_list_add(context->proc_io_watches, watch);
++      else
++              context->io_watches = watch_list_add(context->io_watches, watch);
++ 
++      return watch->id;
++ }
++ 
++ guint g_io_add_watch(GIOChannel *channel, GIOCondition condition,
++                                      GIOFunc func, gpointer user_data)
++ {
++      return g_io_add_watch_full(channel, 0, condition,
++                                              func, user_data, NULL);
++ }
++ 
++ GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running)
++ {
++      GMainLoop *ml;
++ 
++      if (!context)
++              context = g_main_context_default();
++ 
++      ml = g_new0(GMainLoop, 1);
++ 
++      ml->context = context;
++      ml->is_running = is_running;
++ 
++      return ml;
++ }
++ 
++ static void timeout_handlers_prepare(GMainContext *context)
++ {
++      GSList *l;
++      struct timeval tv;
++      glong msec, timeout = LONG_MAX;
++ 
++      gettimeofday(&tv, NULL);
++ 
++      for (l = context->timeouts; l != NULL; l = l->next) {
++              struct timeout *t = l->data;
++ 
++              /* calculate the remainning time */
++              msec = (t->expiration.tv_sec - tv.tv_sec) * 1000 +
++                              (t->expiration.tv_usec - tv.tv_usec) / 1000;
++              if (msec < 0)
++                      msec = 0;
++ 
++              timeout = MIN_TIMEOUT(timeout, msec);
++      }
++ 
++      /* set to min value found or NO timeout */
++      context->next_timeout = (timeout != LONG_MAX ? timeout : -1);
++ }
++ 
++ static int ptr_cmp(const void *t1, const void *t2)
++ {
++      return t1 - t2;
++ }
++ 
++ static void timeout_handlers_check(GMainContext *context)
++ {
++      struct timeval tv;
++ 
++      gettimeofday(&tv, NULL);
++ 
++      context->timeout_lock = TRUE;
++ 
++      while (context->timeouts) {
++              struct timeout *t = context->timeouts->data;
++              glong secs, msecs;
++              gboolean ret;
++ 
++              if (timercmp(&tv, &t->expiration, <)) {
++                      context->timeouts = g_slist_remove(context->timeouts, t);
++                      context->proc_timeouts = g_slist_append(context->proc_timeouts, t);
++                      continue;
++              }
++ 
++              ret = t->function(t->data);
++ 
++              /* Check if the handler was removed/freed by the callback
++               * function */
++              if (!g_slist_find_custom(context->timeouts, t, ptr_cmp))
++                      continue;
++ 
++              context->timeouts = g_slist_remove(context->timeouts, t);
++ 
++              if (!ret) {
++                      g_free(t);
++                      continue;
++              }
++ 
++              /* update the next expiration time */
++              secs = t->interval / 1000;
++              msecs = t->interval - secs * 1000;
++ 
++              t->expiration.tv_sec = tv.tv_sec + secs;
++              t->expiration.tv_usec = tv.tv_usec + msecs * 1000;
++              if (t->expiration.tv_usec >= 1000000) {
++                      t->expiration.tv_usec -= 1000000;
++                      t->expiration.tv_sec++;
++              }
++ 
++              context->proc_timeouts = g_slist_append(context->proc_timeouts, t);
++      }
++ 
++      context->timeouts = context->proc_timeouts;
++      context->proc_timeouts = NULL;
++      context->timeout_lock = FALSE;
++ }
++ 
++ void g_main_loop_run(GMainLoop *loop)
++ {
++      int open_max = sysconf(_SC_OPEN_MAX);
++      struct pollfd *ufds;
++      GMainContext *context = loop->context;
++ 
++      ufds = g_new(struct pollfd, open_max);
++ 
++      loop->is_running = TRUE;
++ 
++      while (loop->is_running) {
++              int nfds;
++              GSList *l;
++              struct io_watch *w;
++ 
++              for (nfds = 0, l = context->io_watches; l != NULL; l = l->next, nfds++) {
++                      w = l->data;
++                      ufds[nfds].fd = w->channel->fd;
++                      ufds[nfds].events = w->condition;
++                      ufds[nfds].revents = 0;
++                      w->revents = &ufds[nfds].revents;
++              }
++ 
++              /* calculate the next timeout */
++              timeout_handlers_prepare(context);
++ 
++              if (poll(ufds, nfds, context->next_timeout) < 0)
++                      continue;
++ 
++              context->io_lock = TRUE;
++ 
++              while (context->io_watches) {
++                      gboolean ret;
++ 
++                      w = context->io_watches->data;
++ 
++                      if (!*w->revents) {
++                              context->io_watches = g_slist_remove(context->io_watches, w);
++                              context->proc_io_watches = watch_list_add(context->proc_io_watches, w);
++                              continue;
++                      }
++ 
++                      ret = w->func(w->channel, *w->revents, w->user_data);
++ 
++                      /* Check if the watch was removed/freed by the callback
++                       * function */
++                      if (!g_slist_find_custom(context->io_watches, w, ptr_cmp))
++                              continue;
++ 
++                      context->io_watches = g_slist_remove(context->io_watches, w);
++ 
++                      if (!ret) {
++                              watch_free(w);
++                              continue;
++                      }
++ 
++                      context->proc_io_watches = watch_list_add(context->proc_io_watches, w);
++              }
++ 
++              context->io_watches = context->proc_io_watches;
++              context->proc_io_watches = NULL;
++              context->io_lock = FALSE;
++ 
++              /* check expired timers */
++              timeout_handlers_check(loop->context);
++      }
++ 
++      g_free(ufds);
++ }
++ 
++ void g_main_loop_quit(GMainLoop *loop)
++ {
++      loop->is_running = FALSE;
++ }
++ 
++ void g_main_loop_unref(GMainLoop *loop)
++ {
++      if (!loop->context)
++              return;
++ 
++      g_slist_foreach(loop->context->io_watches, (GFunc)watch_free, NULL);
++      g_slist_free(loop->context->io_watches);
++ 
++      g_slist_foreach(loop->context->timeouts, (GFunc)g_free, NULL);
++      g_slist_free(loop->context->timeouts);
++ 
++      g_free(loop->context);
++      loop->context = NULL;
++ }
++ 
++ guint g_timeout_add(guint interval, GSourceFunc function, gpointer data)
++ {
++      GMainContext *context = g_main_context_default();
++      struct timeval tv;
++      guint secs;
++      guint msecs;
++      struct timeout *t;
++ 
++      t = g_new0(struct timeout, 1);
++ 
++      t->interval = interval;
++      t->function = function;
++      t->data = data;
++ 
++      gettimeofday(&tv, NULL);
++ 
++      secs = interval /1000;
++      msecs = interval - secs * 1000;
++ 
++      t->expiration.tv_sec = tv.tv_sec + secs;
++      t->expiration.tv_usec = tv.tv_usec + msecs * 1000;
++ 
++      if (t->expiration.tv_usec >= 1000000) {
++              t->expiration.tv_usec -= 1000000;
++              t->expiration.tv_sec++;
++      }
++ 
++      /* attach the timeout the default context */
++      t->id = context->next_id++;
++ 
++      if (context->timeout_lock)
++              context->proc_timeouts = g_slist_prepend(context->proc_timeouts, t);
++      else
++              context->timeouts = g_slist_prepend(context->timeouts, t);
++ 
++      return t->id;
++ }
++ 
++ guint g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
++ {
++      return g_timeout_add(interval, function, data);
++ }
++ 
++ guint g_idle_add(GSourceFunc function, gpointer data)
++ {
++      return g_timeout_add(1, function, data);
++ }
++ 
++ /* GError */
++ 
++ GError* g_error_new_literal(GQuark domain, gint code, const gchar *message)
++ {
++      GError *err;
++ 
++      err = g_new(GError, 1);
++ 
++      err->domain = domain;
++      err->code = code;
++      err->message = g_strdup(message);
++ 
++      return err;
++ }
++ 
++ void g_set_error(GError **err, GQuark domain, gint code,
++                      const gchar *format, ...)
++ {
++      gchar msg[1024];
++      va_list ap;
++ 
++      if (!err)
++              return;
++ 
++      va_start(ap, format);
++ 
++      vsnprintf(msg, sizeof(msg) - 1, format, ap);
++ 
++      va_end(ap);
++ 
++      *err = g_error_new_literal(domain, code, msg);
++ }
++ 
++ void g_error_free(GError *err)
++ {
++      g_free(err->message);
++      g_free(err);
++ }
++ 
++ /* Spawning related functions */
++ 
++ static int child_watch_pipe[2] = { -1, -1 };
++ 
++ static void sigchld_handler(int signal)
++ {
++      int ret;
++      ret = write(child_watch_pipe[1], "B", 1);
++ }
++ 
++ static gboolean child_watch_remove(GMainContext *context, guint id)
++ {
++      GSList *l;
++      struct child_watch *w;
++ 
++      for (l = context->child_watches; l != NULL; l = l->next) {
++              w = l->data;
++ 
++              if (w->id != id)
++                      continue;
++ 
++              context->child_watches =
++                      g_slist_remove(context->child_watches, w);
++              g_free(w);
++ 
++              return TRUE;
++      }
++ 
++      for (l = context->proc_child_watches; l != NULL; l = l->next) {
++              w = l->data;
++ 
++              if (w->id != id)
++                      continue;
++ 
++              context->proc_child_watches =
++                      g_slist_remove(context->proc_child_watches, w);
++              g_free(w);
++ 
++              return TRUE;
++      }
++ 
++ 
++      return FALSE;
++ }
++ 
++ static gboolean child_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
++ {
++      int ret;
++      char b[20];
++      GMainContext *context = g_main_context_default();
++ 
++      ret = read(child_watch_pipe[0], b, 20);
++ 
++      context->child_lock = TRUE;
++ 
++      while (context->child_watches) {
++              gint status;
++              struct child_watch *w = context->child_watches->data;
++ 
++              if (waitpid(w->pid, &status, WNOHANG) <= 0) {
++                      context->child_watches =
++                              g_slist_remove(context->child_watches, w);
++                      context->proc_child_watches =
++                              watch_list_add(context->proc_child_watches, w);
++                      continue;
++              }
++ 
++              w->function(w->pid, status, w->user_data);
++ 
++              /* Check if the callback already removed us */
++              if (!g_slist_find(context->child_watches, w))
++                      continue;
++ 
++              context->child_watches = g_slist_remove(context->child_watches, w);
++              g_free(w);
++      }
++ 
++      context->child_watches = context->proc_child_watches;
++      context->proc_child_watches = NULL;
++      context->child_lock = FALSE;
++ 
++      return TRUE;
++ }
++ 
++ static void init_child_pipe(void)
++ {
++      struct sigaction action;
++      GIOChannel *io;
++ 
++      if (pipe(child_watch_pipe) < 0) {
++              fprintf(stderr, "Unable to initialize child watch pipe: %s (%d)\n",
++                              strerror(errno), errno);
++              abort();
++      }
++ 
++      fcntl(child_watch_pipe[1], F_SETFL,
++                      O_NONBLOCK | fcntl(child_watch_pipe[1], F_GETFL));
++ 
++      action.sa_handler = sigchld_handler;
++      sigemptyset(&action.sa_mask);
++      action.sa_flags = SA_NOCLDSTOP;
++      sigaction(SIGCHLD, &action, NULL);
++ 
++      io = g_io_channel_unix_new(child_watch_pipe[0]);
++      g_io_add_watch(io, G_IO_IN, child_watch, NULL);
++      g_io_channel_unref(io);
++ }
++ 
++ static void exec_child(const gchar *working_directory,
++                      gchar **argv, gchar **envp,
++                      GSpawnFlags flags,
++                      GSpawnChildSetupFunc child_setup,
++                      gpointer user_data)
++ {
++      int null;
++ 
++      if (working_directory && chdir(working_directory) < 0)
++              _exit(EXIT_FAILURE);
++ 
++      if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN)) {
++              int open_max, fd, ret;
++ 
++              ret = 0;
++              open_max = sysconf(_SC_OPEN_MAX);
++              for (fd = 3; fd < open_max && ret == 0; fd++)
++                      ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
++      }
++ 
++      null = open("/dev/null", O_RDWR);
++      if (!(flags & G_SPAWN_CHILD_INHERITS_STDIN))
++              dup2(null, STDIN_FILENO);
++      if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
++              dup2(null, STDOUT_FILENO);
++      if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
++              dup2(null, STDERR_FILENO);
++      if (null > 2)
++              close(null);
++ 
++      if (child_setup)
++              child_setup(user_data);
++ 
++      if (envp)
++              execve(argv[0], argv, envp);
++      else
++              execv(argv[0], argv);
++ 
++      /* exec failed if we get here */
++      _exit(EXIT_FAILURE);
++ }
++ 
++ gboolean g_spawn_async(const gchar *working_directory,
++                      gchar **argv, gchar **envp,
++                      GSpawnFlags flags,
++                      GSpawnChildSetupFunc child_setup,
++                      gpointer user_data,
++                      GPid *child_pid,
++                      GError **error)
++ {
++      GPid pid;
++ 
++      if (access(argv[0], X_OK) < 0) {
++              g_set_error(error, 0, 0, "%s is not executable", argv[0]);
++              return FALSE;
++      }
++ 
++      if (child_watch_pipe[0] < 0)
++              init_child_pipe();
++ 
++      /* Flush output streams so child doesn't get them */
++      fflush(NULL);
++ 
++      switch (pid = fork()) {
++      case -1:
++              g_set_error(error, 0, 0, "fork failed: %s", strerror(errno));
++              return FALSE;
++      case 0:
++              exec_child(working_directory, argv, envp, flags,
++                              child_setup, user_data);
++              break;
++      default:
++              if (child_pid)
++                      *child_pid = pid;
++              return TRUE;
++      }
++ 
++      /* Never reached */
++      return FALSE;
++ }
++ 
++ void g_spawn_close_pid(GPid pid)
++ {
++      return;
++ }
++ 
++ guint g_child_watch_add(GPid pid, GChildWatchFunc func, gpointer user_data)
++ {
++      struct child_watch *w;
++      GMainContext *context = g_main_context_default();
++ 
++      if (child_watch_pipe[0] < 0)
++              init_child_pipe();
++ 
++      w = g_new(struct child_watch, 1);
++ 
++      w->id = context->next_id++;
++      w->pid = pid;
++      w->function = func;
++      w->user_data = user_data;
++ 
++      if (context->child_lock)
++              context->proc_child_watches =
++                      watch_list_add(context->proc_child_watches, w);
++      else
++              context->child_watches =
++                      watch_list_add(context->child_watches, w);
++ 
++      return w->id;
++ }
++ 
++ gboolean g_source_remove(guint tag)
++ {
++      GMainContext *context = g_main_context_default();
++ 
++      if (g_io_remove_watch(context, tag))
++              return TRUE;
++ 
++      if (g_timeout_remove(context, tag))
++              return TRUE;
++ 
++      if (child_watch_remove(context, tag))
++              return TRUE;
++ 
++      return FALSE;
++ }
++ 
++ /* UTF-8 Validation: approximate copy/paste from glib2. */
++ 
++ #define UNICODE_VALID(c)                     \
++      ((c) < 0x110000 &&                      \
++      (((c) & 0xFFFFF800) != 0xD800) &&       \
++      ((c) < 0xFDD0 || (c) > 0xFDEF) &&       \
++      ((c) & 0xFFFE) != 0xFFFE)
++ 
++ #define CONTINUATION_CHAR(c, val)                            \
++      do {                                                    \
++              if (((c) & 0xc0) != 0x80) /* 10xxxxxx */        \
++                      goto failed;                            \
++              (val) <<= 6;                                    \
++              (val) |= (c) & 0x3f;                            \
++      } while (0)
++ 
++ #define INCREMENT_AND_CHECK_MAX(p, i, max_len)                                       \
++      do {                                                                    \
++              (i)++;                                                          \
++              if ((p)[(i)] == '\0' || ((max_len) >= 0 && (i) >= (max_len)))   \
++                      goto failed;                                            \
++      } while (0)
++ 
++ 
++ gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end)
++ {
++      unsigned long val, min, i;
++      const unsigned char *p, *last;
++ 
++      min = val = 0;
++ 
++      for (p = (unsigned char *) str, i = 0; p[i]; i++) {
++              if (max_len >= 0 && i >= max_len)
++                      break;
++ 
++              if (p[i] < 128)
++                      continue;
++ 
++              last = &p[i];
++ 
++              if ((p[i] & 0xe0) == 0xc0) { /* 110xxxxx */
++                      if ((p[i] & 0x1e) == 0)
++                              goto failed;
++                      INCREMENT_AND_CHECK_MAX(p, i, max_len);
++                      if ((p[i] & 0xc0) != 0x80)
++                              goto failed; /* 10xxxxxx */
++              } else {
++                      if ((p[i] & 0xf0) == 0xe0) {
++                              /* 1110xxxx */
++                              min = (1 << 11);
++                              val = p[i] & 0x0f;
++                              goto two_remaining;
++                      } else if ((p[i] & 0xf8) == 0xf0) {
++                              /* 11110xxx */
++                              min = (1 << 16);
++                              val = p[i] & 0x07;
++                      } else
++                              goto failed;
++ 
++                      INCREMENT_AND_CHECK_MAX(p, i, max_len);
++                      CONTINUATION_CHAR(p[i], val);
++ two_remaining:
++                      INCREMENT_AND_CHECK_MAX(p, i, max_len);
++                      CONTINUATION_CHAR(p[i], val);
++ 
++                      INCREMENT_AND_CHECK_MAX(p, i, max_len);
++                      CONTINUATION_CHAR(p[i], val);
++ 
++                      if (val < min || !UNICODE_VALID(val))
++                              goto failed;
++              }
++      }
++ 
++      if (end)
++              *end = (const gchar *) &p[i];
++ 
++      return TRUE;
++ 
++ failed:
++      if (end)
++              *end = (const gchar *) last;
++ 
++      return FALSE;
++ }
++ 
++ /* GSList functions */
++ 
++ GSList *g_slist_append(GSList *list, void *data)
++ {
++      GSList *entry, *tail;
++ 
++      entry = g_new(GSList, 1);
++ 
++      entry->data = data;
++      entry->next = NULL;
++ 
++      if (!list)
++              return entry;
++ 
++      /* Find the end of the list */
++      for (tail = list; tail->next; tail = tail->next);
++ 
++      tail->next = entry;
++ 
++      return list;
++ }
++ 
++ GSList *g_slist_prepend(GSList *list, void *data)
++ {
++      GSList *entry;
++ 
++      entry = g_new(GSList, 1);
++ 
++      entry->data = data;
++      entry->next = list;
++ 
++      return entry;
++ }
++ 
++ GSList *g_slist_insert_sorted(GSList *list, void *data, GCompareFunc cmp_func)
++ {
++      GSList *tmp, *prev, *entry;
++      int cmp;
++ 
++      entry = g_new(GSList, 1);
++ 
++      entry->data = data;
++      entry->next = NULL;
++ 
++      if (!list)
++              return entry;
++ 
++      prev = NULL;
++      tmp = list;
++ 
++      cmp = cmp_func(data, tmp->data);
++ 
++      while (tmp->next && cmp > 0) {
++              prev = tmp;
++              tmp = tmp->next;
++ 
++              cmp = cmp_func(data, tmp->data);
++      }
++ 
++      if (!tmp->next && cmp > 0) {
++              tmp->next = entry;
++              return list;
++      }
++ 
++      if (prev) {
++              prev->next = entry;
++              entry->next = tmp;
++              return list;
++      } else {
++              entry->next = list;
++              return entry;
++      }
++ }
++ 
++ GSList *g_slist_remove(GSList *list, void *data)
++ {
++      GSList *l, *next, *prev = NULL, *match = NULL;
++ 
++      if (!list)
++              return NULL;
++ 
++      for (l = list; l != NULL; l = l->next) {
++              if (l->data == data) {
++                      match = l;
++                      break;
++              }
++              prev = l;
++      }
++ 
++      if (!match)
++              return list;
++ 
++      next = match->next;
++ 
++      g_free(match);
++ 
++      /* If the head was removed, return the next element */
++      if (!prev)
++              return next;
++ 
++      prev->next = next;
++ 
++      return list;
++ }
++ 
++ GSList *g_slist_find(GSList *list, gconstpointer data)
++ {
++      GSList *l;
++ 
++      for (l = list; l != NULL; l = l->next) {
++              if (l->data == data)
++                      return l;
++      }
++ 
++      return NULL;
++ }
++ 
++ GSList *g_slist_find_custom(GSList *list, const void *data,
++                      GCompareFunc cmp_func)
++ {
++      GSList *l;
++ 
++      for (l = list; l != NULL; l = l->next) {
++              if (!cmp_func(l->data, data))
++                      return l;
++      }
++ 
++      return NULL;
++ }
++ 
++ static GSList *g_slist_sort_merge(GSList *l1, GSList *l2,
++                                      GCompareFunc cmp_func)
++ {
++      GSList list, *l;
++      int cmp;
++ 
++      l = &list;
++ 
++      while (l1 && l2) {
++              cmp = cmp_func(l1->data, l2->data);
++ 
++              if (cmp <= 0) {
++                      l = l->next = l1;
++                      l1 = l1->next;
++              } else {
++                      l = l->next = l2;
++                      l2 = l2->next;
++              }
++      }
++ 
++      l->next = l1 ? l1 : l2;
++ 
++      return list.next;
++ }
++ 
++ GSList *g_slist_sort(GSList *list, GCompareFunc cmp_func)
++ {
++      GSList *l1, *l2;
++ 
++      if (!list || !list->next)
++              return list;
++ 
++      l1 = list;
++      l2 = list->next;
++ 
++      while ((l2 = l2->next) != NULL) {
++              if ((l2 = l2->next) == NULL)
++                      break;
++              l1 = l1->next;
++      }
++ 
++      l2 = l1->next;
++      l1->next = NULL;
++ 
++      return g_slist_sort_merge(g_slist_sort(list, cmp_func),
++                              g_slist_sort(l2, cmp_func), cmp_func);
++ }
++ 
++ int g_slist_length(GSList *list)
++ {
++      int len;
++ 
++      for (len = 0; list != NULL; list = list->next)
++              len++;
++ 
++      return len;
++ }
++ 
++ void g_slist_foreach(GSList *list, GFunc func, void *user_data)
++ {
++      while (list) {
++              GSList *next = list->next;
++              func(list->data, user_data);
++              list = next;
++      }
++ }
++ 
++ void g_slist_free(GSList *list)
++ {
++      GSList *l, *next;
++ 
++      for (l = list; l != NULL; l = next) {
++              next = l->next;
++              g_free(l);
++      }
++ }
++ 
++ GSList *g_slist_nth(GSList *list, guint n)
++ {
++      while (n-- > 0 && list)
++              list = list->next;
++ 
++      return list;
++ }
++ 
++ gpointer g_slist_nth_data(GSList *list, guint n)
++ {
++      while (n-- > 0 && list)
++              list = list->next;
++ 
++      return list ? list->data : NULL;
++ }
++ 
++ gint g_slist_position(GSList *list, GSList *link)
++ {
++      gint i;
++ 
++      for (i = 0; list; list = list->next, i++) {
++              if (list == link)
++                      return i;
++      }
++ 
++      return -1;
++ }
++ 
++ GSList* g_slist_last(GSList *list)
++ {
++      if (list)
++              while (list->next)
++                      list = list->next;
++ 
++      return list;
++ }
++ 
++ static inline GSList* _g_slist_remove_link(GSList *list, GSList *link)
++ {
++      GSList *tmp;
++      GSList *prev;
++ 
++      prev = NULL;
++      tmp = list;
++ 
++      while (tmp) {
++              if (tmp == link) {
++                      if (prev)
++                              prev->next = tmp->next;
++                      if (list == tmp)
++                              list = list->next;
++ 
++                      tmp->next = NULL;
++                      break;
++              }
++ 
++              prev = tmp;
++              tmp = tmp->next;
++      }
++ 
++      return list;
++ }
++ 
++ GSList* g_slist_delete_link(GSList *list, GSList *link)
++ {
++      list = _g_slist_remove_link(list, link);
++      g_free(link);
++ 
++      return list;
++ }
++ 
++ /* Memory allocation functions */
++ 
++ gpointer g_malloc(gulong n_bytes)
++ {
++      gpointer mem;
++ 
++      if (!n_bytes)
++              return NULL;
++ 
++      mem = malloc((size_t) n_bytes);
++      if (!mem) {
++              fprintf(stderr, "g_malloc: failed to allocate %lu bytes",
++                              n_bytes);
++              abort();
++      }
++ 
++      return mem;
++ }
++ 
++ gpointer g_malloc0(gulong n_bytes)
++ {
++      gpointer mem;
++ 
++      if (!n_bytes)
++              return NULL;
++ 
++      mem = g_malloc(n_bytes);
++ 
++      memset(mem, 0, (size_t) n_bytes);
++ 
++      return mem;
++ }
++ 
++ gpointer g_try_malloc(gulong n_bytes)
++ {
++      if (!n_bytes)
++              return NULL;
++ 
++      return malloc((size_t) n_bytes);
++ }
++ 
++ gpointer g_try_malloc0(gulong n_bytes)
++ {
++      gpointer mem;
++ 
++      mem = g_try_malloc(n_bytes);
++      if (mem)
++              memset(mem, 0, (size_t) n_bytes);
++ 
++      return mem;
++ }
++ 
++ gpointer g_realloc(gpointer mem, gulong n_bytes)
++ {
++      mem = realloc(mem, n_bytes);
++      if (!mem) {
++              fprintf(stderr, "g_realloc: failed to allocate %lu bytes",
++                              n_bytes);
++              abort();
++      }
++ 
++      return mem;
++ }
++ 
++ void g_free(gpointer mem)
++ {
++      if (mem)
++              free(mem);
++ }
++ 
++ gchar *g_strdup(const gchar *str)
++ {
++      gchar *s;
++ 
++      if (!str)
++              return NULL;
++ 
++      s = strdup(str);
++      if (!s) {
++              fprintf(stderr, "strdup: failed to allocate new string");
++              abort();
++      }
++ 
++      return s;
++ }
++ 
++ gchar *g_strdup_printf(const gchar *format, ...)
++ {
++      va_list args;
++      gchar buffer[1024];
++      gint length;
++ 
++      va_start(args, format);
++      length = vsnprintf(buffer, sizeof(buffer) - 1, format, args);
++      va_end(args);
++ 
++      return g_strdup(buffer);
++ }
++ 
++ gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar new_delim)
++ {
++      register gchar *c;
++ 
++      if (!string)
++              return NULL;
++ 
++      for (c = string; *c; c++)
++              if (strchr(delimiters, *c))
++                      *c = new_delim;
++ 
++      return string;
++ }
++ 
++ gchar *g_strconcat(const gchar *string1, ...)
++ {
++      gsize l;
++      va_list args;
++      gchar *s, *concat;
++ 
++      if (!string1)
++              return NULL;
++ 
++      l = 1 + strlen(string1);
++      va_start(args, string1);
++      s = va_arg(args, gchar *);
++      while (s) {
++              l += strlen(s);
++              s = va_arg(args, gchar *);
++      }
++      va_end (args);
++ 
++      concat = g_new(gchar, l);
++      concat[0] = '\0';
++ 
++      va_start(args, string1);
++      s = va_arg(args, gchar*);
++      while (s) {
++              strcat(concat, s);
++              s = va_arg(args, gchar *);
++      }
++      va_end (args);
++ 
++      return concat;
++ }
++ 
++ gsize g_strlcat(gchar *dest, const gchar *src, gsize dest_size)
++ {
++      gchar *d = dest;
++      const gchar *s = src;
++      gsize bytes_left = dest_size;
++      gsize dlength;  /* Logically, MIN(strlen(d), dest_size) */
++ 
++      if (!d || !s)
++              return 0;
++ 
++      /* Find the end of dst and adjust bytes left but don't go past end */
++      while (*d != 0 && bytes_left-- != 0)
++              d++;
++      dlength = d - dest;
++      bytes_left = dest_size - dlength;
++ 
++      if (bytes_left == 0)
++              return dlength + strlen(s);
++ 
++      while (*s != 0) {
++              if (bytes_left != 1) {
++                      *d++ = *s;
++                      bytes_left--;
++              }
++              s++;
++      }
++      *d = 0;
++ 
++      return dlength + (s - src);  /* count does not include NULL */
++ }
++ 
++ gchar **g_strsplit(const gchar *string, const gchar *delimiter, gint max_tokens)
++ {
++      GSList *string_list = NULL, *slist;
++      gchar **str_array, *s;
++      guint n = 0;
++      const gchar *remainder;
++ 
++      if (string == NULL || delimiter == NULL || delimiter[0] == '\0')
++              return NULL;
++ 
++      if (max_tokens < 1)
++              max_tokens = INT_MAX;
++ 
++      remainder = string;
++      s = strstr(remainder, delimiter);
++      if (s) {
++              gsize delimiter_len = strlen(delimiter);
++ 
++              while (--max_tokens && s) {
++                      gsize len;
++                      gchar *tmp;
++ 
++                      len = s - remainder;
++                      tmp = g_new(char, len);
++                      memcpy(tmp, remainder, len);
++                      string_list = g_slist_prepend(string_list, tmp);
++                      n++;
++                      remainder = s + delimiter_len;
++                      s = strstr(remainder, delimiter);
++              }
++      }
++      if (*string) {
++              n++;
++              string_list = g_slist_prepend(string_list, g_strdup(remainder));
++      }
++ 
++      str_array = g_new(gchar *, n + 1);
++ 
++      str_array[n--] = NULL;
++      for (slist = string_list; slist; slist = slist->next)
++              str_array[n--] = slist->data;
++ 
++      g_slist_free(string_list);
++ 
++      return str_array;
++ }
++ 
++ gchar *g_ascii_strup(const gchar *str, gssize len)
++ {
++      int i;
++      gchar *s;
++ 
++      s = g_strdup(str);
++      if (!s)
++              return NULL;
++ 
++      if (len < 0)
++              len = strlen(s);
++ 
++      for (i = 0; i < len; i++)
++              s[i] = toupper(s[i]);
++ 
++      return s;
++ }
++ 
++ gboolean g_str_equal(gconstpointer v1, gconstpointer v2)
++ {
++      const gchar *string1 = v1;
++      const gchar *string2 = v2;
++ 
++      return strcmp(string1, string2) == 0;
++ }
++ 
++ gboolean g_str_has_prefix(const gchar *str, const gchar *prefix)
++ {
++      int str_len;
++      int prefix_len;
++ 
++      if (str == NULL || prefix == NULL)
++              return FALSE;
++ 
++      str_len = strlen (str);
++      prefix_len = strlen (prefix);
++ 
++      if (str_len < prefix_len)
++              return FALSE;
++ 
++      return strncmp(str, prefix, prefix_len) == 0;
++ }
++ 
++ gboolean g_str_has_suffix(const gchar *str, const gchar *suffix)
++ {
++      int str_len;
++      int suffix_len;
++ 
++      if (!str || !suffix)
++              return FALSE;
++ 
++      str_len = strlen(str);
++      suffix_len = strlen(suffix);
++ 
++      if (str_len < suffix_len)
++              return FALSE;
++ 
++      return strcmp(str + str_len - suffix_len, suffix) == 0;
++ }
++ 
++ void g_strfreev(gchar **str_array)
++ {
++      int i;
++ 
++      if (str_array == NULL)
++              return;
++ 
++      for(i = 0; str_array[i] != NULL; i++)
++              g_free(str_array[i]);
++ 
++      g_free(str_array);
++ }
++ 
++ /* GKeyFile */
++ 
++ struct _GKeyFile {
++      gchar *filename;
++ };
++ 
++ GKeyFile *g_key_file_new(void)
++ {
++      return g_new0(GKeyFile, 1);
++ }
++ 
++ void g_key_file_free(GKeyFile *key_file)
++ {
++      g_free(key_file->filename);
++      g_free(key_file);
++ }
++ 
++ gboolean g_key_file_load_from_file(GKeyFile *key_file,
++                              const gchar *file,
++                              GKeyFileFlags flags,
++                              GError **error)
++ {
++      key_file->filename = g_strdup(file);
++      return TRUE;
++ }
++ 
++ static char *next_line(const char *ptr)
++ {
++      char *nl;
++ 
++      nl = strchr(ptr, '\n');
++      if (!nl)
++              return NULL;
++ 
++      if (nl[1] == '\0')
++              return NULL;
++ 
++      return nl + 1;
++ }
++ 
++ gchar *g_key_file_get_string(GKeyFile *key_file,
++                              const gchar *group_name,
++                              const gchar *key,
++                              GError **error)
++ {
++      struct stat st;
++      char *map, *line, *group = NULL, *value = NULL;
++      off_t size;
++      size_t key_len, group_len;
++      int fd, err = 0;
++ 
++      fd = open(key_file->filename, O_RDONLY);
++      if (fd < 0) {
++              g_set_error(error, 0, 0, "%s: %s", key_file->filename,
++                              strerror(errno));
++              return NULL;
++      }
++ 
++      if (flock(fd, LOCK_SH) < 0) {
++              err = errno;
++              goto close;
++      }
++ 
++      if (fstat(fd, &st) < 0) {
++              err = errno;
++              goto unlock;
++      }
++ 
++      size = st.st_size;
++ 
++      map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
++      if (!map || map == MAP_FAILED) {
++              err = errno;
++              goto unlock;
++      }
++ 
++      group_len = strlen(group_name);
++      key_len = strlen(key);
++ 
++      for (line = map; line != NULL; line = next_line(line)) {
++              int i;
++              size_t to_copy, value_len;
++              char tmp[1024], *nl;
++ 
++              if (*line == '#')
++                      continue;
++ 
++              if (!group) {
++                      if (line[0] != '[' || strncmp(line + 1, group_name, group_len))
++                              continue;
++                      if (line[group_len + 1] == ']')
++                              group = line + 1;
++                      continue;
++              }
++ 
++              if (strncmp(line, key, key_len))
++                      continue;
++ 
++              for (i = key_len; line[i] != '\n'; i++) {
++                      if (line[i] == '=')
++                              break;
++                      if (!isspace(line[i]))
++                              break;
++              }
++ 
++              if (line[i] != '=')
++                      continue;
++ 
++              nl = strchr(line, '\n');
++              if (!nl)
++                      continue;
++ 
++              value_len = nl - (line + i + 1);
++              to_copy = value_len > (sizeof(tmp) - 1) ? sizeof(tmp) - 1 : value_len;
++              memset(tmp, 0, sizeof(tmp));
++              strncpy(tmp, line + i + 1, to_copy);
++ 
++              value = g_strdup(tmp);
++              break;
++      }
++ 
++      munmap(map, size);
++ 
++ unlock:
++      flock(fd, LOCK_UN);
++ 
++ close:
++      close(fd);
++ 
++      if (err)
++              g_set_error(error, 0, 0, "%s: %s", key_file->filename,
++                              strerror(err));
++      else if (!group)
++              g_set_error(error, 0, 0, "%s: group %s not found",
++                              key_file->filename, group_name);
++      else if (!value)
++              g_set_error(error, 0, 0, "%s: key %s not found",
++                              key_file->filename, key);
++ 
++      return value;
++ }
++ 
++ gboolean g_key_file_get_boolean(GKeyFile *key_file,
++                              const gchar *group_name,
++                              const gchar *key,
++                              GError **error)
++ {
++      gboolean ret;
++      gchar *str;
++ 
++      str = g_key_file_get_string(key_file, group_name, key, error);
++      if (!str)
++              return FALSE;
++ 
++      if (strcmp(str, "true") == 0 || strcmp(str, "1") == 0)
++              ret = TRUE;
++      else
++              ret = FALSE;
++ 
++      g_free(str);
++ 
++      return ret;
++ }
++ 
++ gint g_key_file_get_integer(GKeyFile *key_file,
++                              const gchar *group_name,
++                              const gchar *key,
++                              GError **error)
++ {
++      int ret;
++      gchar *str;
++ 
++      str = g_key_file_get_string(key_file, group_name, key, error);
++      if (!str)
++              return 0;
++ 
++      ret = atoi(str);
++ 
++      g_free(str);
++ 
++      return ret;
++ }
++ 
++ gchar **g_key_file_get_string_list(GKeyFile *key_file, const gchar *group_name,
++                                      const gchar *key, gsize *length,
++                                      GError **error)
++ {
++      gchar *str, *item, **list;
++      int items = 0;
++ 
++      str = g_key_file_get_string(key_file, group_name, key, error);
++      if (!str)
++              return NULL;
++ 
++      items = 0;
++      list = g_new0(char *, 1);
++ 
++      item = strtok(str, ",");
++      while (item) {
++              items++;
++ 
++              list = g_renew(char *, list, items + 1);
++ 
++              list[items - 1] = g_strdup(item);
++              list[items] = NULL;
++ 
++              item = strtok(NULL, ",");
++      }
++ 
++      g_free(str);
++ 
++      return list;
++ }
++ 
++ /* GString */
++ 
++ #define MY_MAXSIZE ((gsize)-1)
++ 
++ static gsize nearest_power(gsize base, gsize num)
++ {
++      gsize n = base;
++ 
++      if (num > MY_MAXSIZE / 2)
++              return MY_MAXSIZE;
++ 
++      while (n < num)
++              n <<= 1;
++ 
++      return n;
++ }
++ 
++ static void g_string_maybe_expand(GString *string, gsize len)
++ {
++      if (string->len + len < string->allocated_len)
++              return;
++ 
++      string->allocated_len = nearest_power(1, string->len + len + 1);
++      string->str = g_realloc(string->str, string->allocated_len);
++ }
++ 
++ static GString *g_string_sized_new(gsize dfl_size)
++ {
++      GString *string;
++ 
++      string = g_new0(GString, 1);
++ 
++      g_string_maybe_expand(string, dfl_size);
++      string->str[0] = '\0';
++ 
++      return string;
++ }
++ 
++ static GString *g_string_append_len(GString *string, const gchar *val, gssize len)
++ {
++      g_string_maybe_expand(string, len);
++ 
++      if (len == 1)
++              string->str[string->len] = *val;
++      else
++              memcpy(string->str + string->len, val, len);
++ 
++      string->len += len;
++      string->str[string->len] = '\0';
++ 
++      return string;
++ }
++ 
++ GString *g_string_new(const gchar *init)
++ {
++      GString *string;
++      gint len;
++ 
++      if (init == NULL || *init == '\0')
++              return g_string_sized_new(2);
++ 
++      len = strlen(init);
++      string = g_string_sized_new(len + 2);
++ 
++      g_string_append_len(string, init, len);
++ 
++      return string;
++ }
++ 
++ void g_string_append_printf(GString *string, const gchar *format, ...)
++ {
++      gchar buffer[1024];
++      gint length;
++      va_list args;
++ 
++      va_start(args, format);
++      length = vsnprintf(buffer, sizeof(buffer) - 1, format, args);
++      va_end(args);
++ 
++      g_string_append_len(string, buffer, length);
++ }
++ 
++ gchar *g_string_free(GString *string, gboolean free_segment)
++ {
++      gchar *segment;
++ 
++      if (free_segment) {
++              g_free(string->str);
++              segment = NULL;
++      } else
++              segment = string->str;
++ 
++      g_free(string);
++ 
++      return segment;
++ }
++ 
++ /* GMarkup */
++ 
++ struct _GMarkupParseContext {
++      char dummy;
++ };
++ 
++ GMarkupParseContext *g_markup_parse_context_new(const GMarkupParser *parser,
++                                      GMarkupParseFlags flags,
++                                      gpointer user_data,
++                                      GDestroyNotify user_data_dnotify)
++ {
++      return g_new0(GMarkupParseContext, 1);
++ }
++ 
++ gboolean g_markup_parse_context_parse(GMarkupParseContext *context,
++                                      const gchar *text, gssize text_len,
++                                      GError **error)
++ {
++      g_set_error(error, 0, 0, "Not implemented");
++      return FALSE;
++ }
++ 
++ void g_markup_parse_context_free(GMarkupParseContext *context)
++ {
++      g_free(context);
++ }
++ 
++ static gchar *g_build_pathname_va(const gchar *first_element,
++                                              va_list args, gpointer *data)
++ {
++      gchar result[PATH_MAX], *element;
++ 
++      strncpy(result, first_element, PATH_MAX);
++      element = va_arg(args, gchar *);
++ 
++      while (element) {
++              g_strlcat(result, "/", PATH_MAX);
++              g_strlcat(result, element, PATH_MAX);
++              element = va_arg(args, gchar *);
++      }
++ 
++      va_end(args);
++ 
++      return g_strdup(result);
++ }
++ 
++ gchar *g_build_filename(const gchar *first_element, ...)
++ {
++      gchar *str;
++      va_list args;
++ 
++      va_start(args, first_element);
++      str = g_build_pathname_va(first_element, args, NULL);
++      va_end(args);
++ 
++      return str;
++ }
++ 
++ /* GDir */
++ 
++ GDir *g_dir_open(const gchar *path, guint flags, GError **error)
++ {
++      GDir *dir;
++ 
++      if (path == NULL)
++              return NULL;
++ 
++      dir = g_new(GDir, 1);
++ 
++      dir->dirp = opendir(path);
++ 
++      if (dir->dirp)
++              return dir;
++ 
++      /* error case */
++      g_set_error(error, 0, 0, "Error opening directory '%s': %s",
++                                              path, strerror(errno));
++ 
++      g_free(dir);
++ 
++      return NULL;
++ }
++ 
++ const gchar *g_dir_read_name(GDir *dir)
++ {
++      struct dirent *entry;
++ 
++      if (dir == NULL)
++              return NULL;
++ 
++      entry = readdir(dir->dirp);
++ 
++      while (entry && (strcmp(entry->d_name, ".") == 0 ||
++                                      strcmp(entry->d_name, "..") == 0))
++              entry = readdir(dir->dirp);
++ 
++      return entry ? entry->d_name : NULL;
++ }
++ 
++ void g_dir_close(GDir *dir)
++ {
++      if (dir == NULL)
++              return;
++ 
++      closedir(dir->dirp);
++      g_free(dir);
++ }
index 0000000,0000000,0000000..3156885
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,26 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_SRC_FILES:= \
+++     mainloop.c object.c watch.c
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, dbus) \
+++     $(call include-path-for, bluez-utils)/common/ \
+++     $(call include-path-for, bluez-utils)/eglib/ \
+++        $(LOCAL_PATH)
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth \
+++     libdbus
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libeglib_static \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE:=libgdbus_static
+++
+++LOCAL_CFLAGS+=-O3 -DNEED_DBUS_WATCH_GET_UNIX_FD
+++
+++include $(BUILD_STATIC_LIBRARY)
index 0000000,0000000,0000000..772ff59
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,61 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# hcid
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, dbus) \
+++     $(call include-path-for, bluez-utils)/common/ \
+++     $(call include-path-for, bluez-utils)/eglib/ \
+++     $(call include-path-for, bluez-utils)/gdbus/ \
+++     $(call include-path-for, bluez-utils)/sdpd/ 
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" \
+++     -DSTORAGEDIR=\"/data/misc/hcid\" \
+++     -DCONFIGDIR=\"/etc\" \
+++     -DSERVICEDIR=\"/system/bin\" \
+++     -DPLUGINDIR=\"\" \
+++     -DANDROID_SET_AID_AND_CAP \
+++     -DANDROID_EXPAND_NAME
+++
+++LOCAL_SRC_FILES:= \
+++     adapter.c \
+++     agent.c \
+++     dbus-common.c \
+++     dbus-database.c \
+++     dbus-error.c \
+++     dbus-hci.c \
+++     dbus-sdp.c \
+++     dbus-security.c \
+++     dbus-service.c \
+++     device.c \
+++     kword.c \
+++     lexer.c \
+++     main.c \
+++     manager.c \
+++     parser.c \
+++     security.c \
+++     server.c \
+++     storage.c \
+++     plugin.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth \
+++     libdbus \
+++     libexpat \
+++     libcutils
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libsdpserver_static \
+++     libeglib_static \
+++     libgdbus_static \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE:=hcid
+++
+++include $(BUILD_EXECUTABLE)
index 0000000,0000000,5879627..25bab32
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,4577 +1,4585 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2006-2007  Nokia Corporation
++  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #define _GNU_SOURCE
++ #include <stdio.h>
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <time.h>
++ #include <sys/param.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/l2cap.h>
++ #include <bluetooth/sdp.h>
++ #include <bluetooth/sdp_lib.h>
++ 
++ #include <glib.h>
++ #include <dbus/dbus.h>
++ #include <gdbus.h>
++ 
++ #include "hcid.h"
++ 
++ #include "adapter.h"
++ #include "device.h"
++ 
++ #include "textfile.h"
++ #include "oui.h"
++ #include "dbus-common.h"
++ #include "dbus-hci.h"
++ #include "dbus-sdp.h"
++ #include "dbus-database.h"
++ #include "dbus-service.h"
++ #include "dbus-security.h"
++ #include "dbus-error.h"
++ #include "error.h"
++ #include "glib-helper.h"
++ #include "logging.h"
++ #include "agent.h"
++ 
++ #define NUM_ELEMENTS(table) (sizeof(table)/sizeof(const char *))
++ 
++ #define IO_CAPABILITY_DISPLAYONLY    0x00
++ #define IO_CAPABILITY_DISPLAYYESNO   0x01
++ #define IO_CAPABILITY_KEYBOARDONLY   0x02
++ #define IO_CAPABILITY_NOINPUTOUTPUT  0x03
++ #define IO_CAPABILITY_INVALID                0xFF
++ 
++ struct mode_req {
++      struct adapter  *adapter;
++      DBusConnection  *conn;          /* Connection reference */
++      DBusMessage     *msg;           /* Message reference */
++      uint8_t         mode;           /* Requested mode */
++      guint           id;             /* Listener id */
++ };
++ 
++ static const char *service_cls[] = {
++      "positioning",
++      "networking",
++      "rendering",
++      "capturing",
++      "object transfer",
++      "audio",
++      "telephony",
++      "information"
++ };
++ 
++ static const char *major_cls[] = {
++      "miscellaneous",
++      "computer",
++      "phone",
++      "access point",
++      "audio/video",
++      "peripheral",
++      "imaging",
++      "wearable",
++      "toy",
++      "uncategorized"
++ };
++ 
++ static const char *computer_minor_cls[] = {
++      "uncategorized",
++      "desktop",
++      "server",
++      "laptop",
++      "handheld",
++      "palm",
++      "wearable"
++ };
++ 
++ static const char *phone_minor_cls[] = {
++      "uncategorized",
++      "cellular",
++      "cordless",
++      "smart phone",
++      "modem",
++      "isdn"
++ };
++ 
++ static const char *access_point_minor_cls[] = {
++      "fully",
++      "1-17 percent",
++      "17-33 percent",
++      "33-50 percent",
++      "50-67 percent",
++      "67-83 percent",
++      "83-99 percent",
++      "not available"
++ };
++ 
++ static const char *audio_video_minor_cls[] = {
++      "uncategorized",
++      "headset",
++      "handsfree",
++      "unknown",
++      "microphone",
++      "loudspeaker",
++      "headphones",
++      "portable audio",
++      "car audio",
++      "set-top box",
++      "hifi audio",
++      "vcr",
++      "video camera",
++      "camcorder",
++      "video monitor",
++      "video display and loudspeaker",
++      "video conferencing",
++      "unknown",
++      "gaming/toy"
++ };
++ 
++ static const char *peripheral_minor_cls[] = {
++      "uncategorized",
++      "keyboard",
++      "pointing",
++      "combo"
++ };
++ 
++ #if 0
++ static const char *peripheral_2_minor_cls[] = {
++      "uncategorized",
++      "joystick",
++      "gamepad",
++      "remote control",
++      "sensing",
++      "digitizer tablet",
++      "card reader"
++ };
++ #endif
++ 
++ static const char *imaging_minor_cls[] = {
++      "display",
++      "camera",
++      "scanner",
++      "printer"
++ };
++ 
++ static const char *wearable_minor_cls[] = {
++      "wrist watch",
++      "pager",
++      "jacket",
++      "helmet",
++      "glasses"
++ };
++ 
++ static const char *toy_minor_cls[] = {
++      "robot",
++      "vehicle",
++      "doll",
++      "controller",
++      "game"
++ };
++ 
++ static inline DBusMessage *invalid_args(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
++                      "Invalid arguments in method call");
++ }
++ 
++ static inline DBusMessage *not_available(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
++                      "Not Available");
++ }
++ 
++ static inline DBusMessage *adapter_not_ready(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady",
++                      "Adapter is not ready");
++ }
++ 
++ static inline DBusMessage *no_such_adapter(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter",
++                                                      "No such adapter");
++ }
++ 
++ static inline DBusMessage *failed_strerror(DBusMessage *msg, int err)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
++                      strerror(err));
++ }
++ 
++ static inline DBusMessage *in_progress(DBusMessage *msg, const char *str)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", str);
++ }
++ 
++ static inline DBusMessage *not_in_progress(DBusMessage *msg, const char *str)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NotInProgress", str);
++ }
++ 
++ static inline DBusMessage *not_authorized(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized",
++                      "Not authorized");
++ }
++ 
++ static inline DBusMessage *unsupported_major_class(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg,
++                      ERROR_INTERFACE ".UnsupportedMajorClass",
++                      "Unsupported Major Class");
++ }
++ 
++ static int auth_req_cmp(const void *p1, const void *p2)
++ {
++      const struct pending_auth_info *pb1 = p1;
++      const bdaddr_t *bda = p2;
++ 
++      return bda ? bacmp(&pb1->bdaddr, bda) : -1;
++ }
++ 
++ void adapter_auth_request_replied(struct adapter *adapter, bdaddr_t *dba)
++ {
++      GSList *l;
++      struct pending_auth_info *auth;
++ 
++      l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp);
++      if (!l)
++              return;
++ 
++      auth = l->data;
++ 
++      auth->replied = TRUE;
++ }
++ 
++ struct pending_auth_info *adapter_find_auth_request(struct adapter *adapter,
++                                                      bdaddr_t *dba)
++ {
++      GSList *l;
++ 
++      l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp);
++      if (l)
++              return l->data;
++ 
++      return NULL;
++ }
++ 
++ void adapter_remove_auth_request(struct adapter *adapter, bdaddr_t *dba)
++ {
++      GSList *l;
++      struct pending_auth_info *auth;
++ 
++      l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp);
++      if (!l)
++              return;
++ 
++      auth = l->data;
++ 
++      adapter->auth_reqs = g_slist_remove(adapter->auth_reqs, auth);
++ 
++      g_free(auth);
++ }
++ 
++ struct pending_auth_info *adapter_new_auth_request(struct adapter *adapter,
++                                                      bdaddr_t *dba,
++                                                      auth_type_t type)
++ {
++      struct pending_auth_info *info;
++ 
++      debug("hcid_dbus_new_auth_request");
++ 
++      info = g_new0(struct pending_auth_info, 1);
++ 
++      bacpy(&info->bdaddr, dba);
++      info->type = type;
++      adapter->auth_reqs = g_slist_append(adapter->auth_reqs, info);
++ 
++      if (adapter->bonding && !bacmp(dba, &adapter->bonding->bdaddr))
++              adapter->bonding->auth_active = 1;
++ 
++      return info;
++ }
++ 
++ int pending_remote_name_cancel(struct adapter *adapter)
++ {
++      struct remote_dev_info *dev, match;
++      GSList *l;
++      int dd, err = 0;
++ 
++      /* find the pending remote name request */
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, BDADDR_ANY);
++      match.name_status = NAME_REQUESTED;
++ 
++      l = g_slist_find_custom(adapter->found_devices, &match,
++                      (GCompareFunc) found_device_cmp);
++      if (!l) /* no pending request */
++              return 0;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return -ENODEV;
++ 
++      dev = l->data;
++ 
++      if (hci_read_remote_name_cancel(dd, &dev->bdaddr, 1000) < 0) {
++              error("Remote name cancel failed: %s(%d)", strerror(errno), errno);
++              err = -errno;
++      }
++ 
++      /* free discovered devices list */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      hci_close_dev(dd);
++      return err;
++ }
++ 
++ static int auth_info_agent_cmp(const void *a, const void *b)
++ {
++      const struct pending_auth_info *auth = a;
++      const struct agent *agent = b;
++ 
++      if (auth->agent == agent)
++              return 0;
++ 
++      return -1;
++ }
++ 
++ static void device_agent_removed(struct agent *agent, void *user_data)
++ {
++      struct device *device = user_data;
++      struct pending_auth_info *auth;
++      GSList *l;
++ 
++      device->agent = NULL;
++ 
++      l = g_slist_find_custom(device->adapter->auth_reqs, agent,
++                                      auth_info_agent_cmp);
++      if (!l)
++              return;
++ 
++      auth = l->data;
++      auth->agent = NULL;
++ }
++ 
++ static struct bonding_request_info *bonding_request_new(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      struct adapter *adapter,
++                                                      const char *address,
++                                                      const char *agent_path,
++                                                      uint8_t capability)
++ {
++      struct bonding_request_info *bonding;
++      struct device *device;
++ 
++      debug("bonding_request_new(%s)", address);
++ 
++      if (hcid_dbus_use_experimental() && agent_path) {
++              const char *name = dbus_message_get_sender(msg);
++ 
++              device = adapter_get_device(conn, adapter, address);
++              if (!device)
++                      return NULL;
++ 
++              device->agent = agent_create(adapter, name, agent_path,
++                                              capability,
++                                              device_agent_removed,
++                                              device);
++              debug("Temporary agent registered for hci%d/%s at %s:%s",
++                              adapter->dev_id, device->address, name,
++                              agent_path);
++      }
++ 
++      bonding = g_new0(struct bonding_request_info, 1);
++ 
++      bonding->conn = dbus_connection_ref(conn);
++      bonding->msg = dbus_message_ref(msg);
++      bonding->adapter = adapter;
++ 
++      str2ba(address, &bonding->bdaddr);
++ 
++      return bonding;
++ }
++ 
++ const char *mode2str(uint8_t mode)
++ {
++      switch(mode) {
++      case MODE_OFF:
++              return "off";
++      case MODE_CONNECTABLE:
++              return "connectable";
++      case MODE_DISCOVERABLE:
++              return "discoverable";
++      case MODE_LIMITED:
++              return "limited";
++      default:
++              return "unknown";
++      }
++ }
++ 
++ static uint8_t on_mode(const char *addr)
++ {
++      char mode[14];
++      bdaddr_t sba;
++ 
++      str2ba(addr, &sba);
++ 
++      if (read_on_mode(&sba, mode, sizeof(mode)) < 0)
++              return MODE_CONNECTABLE;
++ 
++      return str2mode(addr, mode);
++ }
++ 
++ uint8_t str2mode(const char *addr, const char *mode)
++ {
++      if (strcasecmp("off", mode) == 0)
++              return MODE_OFF;
++      else if (strcasecmp("connectable", mode) == 0)
++              return MODE_CONNECTABLE;
++      else if (strcasecmp("discoverable", mode) == 0)
++              return MODE_DISCOVERABLE;
++      else if (strcasecmp("limited", mode) == 0)
++              return MODE_LIMITED;
++      else if (strcasecmp("on", mode) == 0)
++              return on_mode(addr);
++      else
++              return MODE_UNKNOWN;
++ }
++ 
++ static DBusMessage *adapter_get_info(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *property;
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter dict;
++      bdaddr_t ba;
++      char str[249];
++      uint8_t cls[3];
++ 
++      if (check_address(adapter->address) < 0)
++              return adapter_not_ready(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
++                      DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
++                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
++ 
++      property = adapter->address;
++      dbus_message_iter_append_dict_entry(&dict, "address",
++                      DBUS_TYPE_STRING, &property);
++ 
++      memset(str, 0, sizeof(str));
++      property = str;
++      str2ba(adapter->address, &ba);
++ 
++      if (!read_local_name(&ba, str))
++              dbus_message_iter_append_dict_entry(&dict, "name",
++                      DBUS_TYPE_STRING, &property);
++ 
++      get_device_version(adapter->dev_id, str, sizeof(str));
++      dbus_message_iter_append_dict_entry(&dict, "version",
++                      DBUS_TYPE_STRING, &property);
++ 
++      get_device_revision(adapter->dev_id, str, sizeof(str));
++      dbus_message_iter_append_dict_entry(&dict, "revision",
++                      DBUS_TYPE_STRING, &property);
++ 
++      get_device_manufacturer(adapter->dev_id, str, sizeof(str));
++      dbus_message_iter_append_dict_entry(&dict, "manufacturer",
++                      DBUS_TYPE_STRING, &property);
++ 
++      get_device_company(adapter->dev_id, str, sizeof(str));
++      dbus_message_iter_append_dict_entry(&dict, "company",
++                      DBUS_TYPE_STRING, &property);
++ 
++      property = mode2str(adapter->mode);
++ 
++      dbus_message_iter_append_dict_entry(&dict, "mode",
++                      DBUS_TYPE_STRING, &property);
++ 
++      dbus_message_iter_append_dict_entry(&dict, "discoverable_timeout",
++                              DBUS_TYPE_UINT32, &adapter->discov_timeout);
++ 
++      if (!read_local_class(&ba, cls)) {
++              uint32_t class;
++ 
++              memcpy(&class, cls, 3);
++              dbus_message_iter_append_dict_entry(&dict, "class",
++                      DBUS_TYPE_UINT32, &class);
++ 
++              property = major_class_str(class);
++              dbus_message_iter_append_dict_entry(&dict, "major_class",
++                      DBUS_TYPE_STRING, &property);
++ 
++              property = minor_class_str(class);
++              dbus_message_iter_append_dict_entry(&dict, "minor_class",
++                      DBUS_TYPE_STRING, &property);
++      }
++ 
++      dbus_message_iter_close_container(&iter, &dict);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_address(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *paddr = adapter->address;
++      DBusMessage *reply;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      if (check_address(paddr) < 0)
++              return adapter_not_ready(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &paddr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_version(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[20], *str_ptr = str;
++      int err;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      err = get_device_version(adapter->dev_id, str, sizeof(str));
++      if (err < 0)
++              return failed_strerror(msg, -err);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_revision(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[64], *str_ptr = str;
++      int err;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      err = get_device_revision(adapter->dev_id, str, sizeof(str));
++      if (err < 0)
++              return failed_strerror(msg, -err);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_manufacturer(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[64], *str_ptr = str;
++      int err;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      err = get_device_manufacturer(adapter->dev_id, str, sizeof(str));
++      if (err < 0)
++              return failed_strerror(msg, -err);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_company(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[64], *str_ptr = str;
++      int err;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      err = get_device_company(adapter->dev_id, str, sizeof(str));
++      if (err < 0)
++              return failed_strerror(msg, -err);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_list_modes(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      const char *mode_ptr[] = { "off", "connectable", "discoverable", "limited" };
++      int i;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++      for (i = 0; i < 4; i++)
++              dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
++                                                              &mode_ptr[i]);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_mode(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply = NULL;
++      const char *mode;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      mode = mode2str(adapter->mode);
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &mode,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *set_mode(DBusConnection *conn, DBusMessage *msg,
++                              uint8_t new_mode, void *data)
++ {
++      struct adapter *adapter = data;
++      uint8_t scan_enable;
++      uint8_t current_scan = adapter->scan_enable;
++      bdaddr_t local;
++      gboolean limited;
++      int err, dd;
++      const char *mode;
++ 
++      switch(new_mode) {
++      case MODE_OFF:
++              scan_enable = SCAN_DISABLED;
++              break;
++      case MODE_CONNECTABLE:
++              scan_enable = SCAN_PAGE;
++              break;
++      case MODE_DISCOVERABLE:
++      case MODE_LIMITED:
++              scan_enable = (SCAN_PAGE | SCAN_INQUIRY);
++              break;
++      default:
++              return invalid_args(msg);
++      }
++ 
++      /* Do reverse resolution in case of "on" mode */
++      mode = mode2str(new_mode);
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return no_such_adapter(msg);
++ 
++      if (!adapter->up &&
++                      (hcid.offmode == HCID_OFFMODE_NOSCAN ||
++                       (hcid.offmode == HCID_OFFMODE_DEVDOWN &&
++                        scan_enable != SCAN_DISABLED))) {
++              /* Start HCI device */
++              if (ioctl(dd, HCIDEVUP, adapter->dev_id) == 0)
++                      goto done; /* on success */
++ 
++              if (errno != EALREADY) {
++                      err = errno;
++                      error("Can't init device hci%d: %s (%d)\n",
++                              adapter->dev_id, strerror(errno), errno);
++ 
++                      hci_close_dev(dd);
++                      return failed_strerror(msg, err);
++              }
++      }
++ 
++      if (adapter->up && scan_enable == SCAN_DISABLED &&
++                      hcid.offmode == HCID_OFFMODE_DEVDOWN) {
++              if (ioctl(dd, HCIDEVDOWN, adapter->dev_id) < 0) {
++                      hci_close_dev(dd);
++                      return failed_strerror(msg, errno);
++              }
++ 
++              goto done;
++      }
++ 
++      limited = (new_mode == MODE_LIMITED ? TRUE : FALSE);
++      err = set_limited_discoverable(dd, adapter->class, limited);
++      if (err < 0) {
++              hci_close_dev(dd);
++              return failed_strerror(msg, -err);
++      }
++ 
++      if (current_scan != scan_enable) {
++              struct hci_request rq;
++              uint8_t status = 0;
++ 
++              memset(&rq, 0, sizeof(rq));
++              rq.ogf    = OGF_HOST_CTL;
++              rq.ocf    = OCF_WRITE_SCAN_ENABLE;
++              rq.cparam = &scan_enable;
++              rq.clen   = sizeof(scan_enable);
++              rq.rparam = &status;
++              rq.rlen   = sizeof(status);
++              rq.event = EVT_CMD_COMPLETE;
++ 
++              if (hci_send_req(dd, &rq, 1000) < 0) {
++                      err = errno;
++                      error("Sending write scan enable command failed: %s (%d)",
++                                      strerror(errno), errno);
++                      hci_close_dev(dd);
++                      return failed_strerror(msg, err);
++              }
++ 
++              if (status) {
++                      error("Setting scan enable failed with status 0x%02x",
++                                      status);
++                      hci_close_dev(dd);
++                      return failed_strerror(msg, bt_error(status));
++              }
++      } else {
++              /* discoverable or limited */
++              if ((scan_enable & SCAN_INQUIRY) && (new_mode != adapter->mode)) {
++                      g_dbus_emit_signal(conn,
++                                      dbus_message_get_path(msg),
++                                      ADAPTER_INTERFACE,
++                                      "ModeChanged",
++                                      DBUS_TYPE_STRING, &mode,
++                                      DBUS_TYPE_INVALID);
++ 
++                      if (adapter->timeout_id)
++                              g_source_remove(adapter->timeout_id);
++ 
++                      if (!adapter->sessions && !adapter->discov_timeout)
++                              adapter->timeout_id = g_timeout_add(adapter->discov_timeout * 1000,
++                                              discov_timeout_handler, adapter);
++              }
++      }
++ done:
++      str2ba(adapter->address, &local);
++      write_device_mode(&local, mode);
++ 
++      hci_close_dev(dd);
++ 
++      adapter->mode = new_mode;
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ gint find_session(struct mode_req *req, DBusMessage *msg)
++ {
++      const char *name = dbus_message_get_sender(req->msg);
++      const char *sender = dbus_message_get_sender(msg);
++ 
++      return strcmp(name, sender);
++ }
++ 
++ static void confirm_mode_cb(struct agent *agent, DBusError *err, void *data)
++ {
++      struct mode_req *req = data;
++      DBusMessage *reply;
++ 
++      if (err && dbus_error_is_set(err)) {
++              reply = dbus_message_new_error(req->msg, err->name, err->message);
++              dbus_connection_send(req->conn, reply, NULL);
++              dbus_message_unref(reply);
++              goto cleanup;
++      }
++ 
++      reply = set_mode(req->conn, req->msg, req->mode, req->adapter);
++      dbus_connection_send(req->conn, reply, NULL);
++      dbus_message_unref(reply);
++ 
++      if (!g_slist_find_custom(req->adapter->sessions, req->msg,
++                      (GCompareFunc) find_session))
++              goto cleanup;
++ 
++      return;
++ 
++ cleanup:
++      dbus_message_unref(req->msg);
++      if (req->id)
++              g_dbus_remove_watch(req->conn, req->id);
++      dbus_connection_unref(req->conn);
++      g_free(req);
++ }
++ 
++ static DBusMessage *confirm_mode(DBusConnection *conn, DBusMessage *msg,
++                                      const char *mode, void *data)
++ {
++      struct adapter *adapter = data;
++      struct mode_req *req;
++      int ret;
++ 
++      if (!adapter->agent)
++              return dbus_message_new_method_return(msg);
++ 
++      req = g_new0(struct mode_req, 1);
++      req->adapter = adapter;
++      req->conn = dbus_connection_ref(conn);
++      req->msg = dbus_message_ref(msg);
++      req->mode = str2mode(adapter->address, mode);
++ 
++      ret = agent_confirm_mode_change(adapter->agent, mode, confirm_mode_cb,
++                                      req);
++      if (ret < 0) {
++              dbus_connection_unref(req->conn);
++              dbus_message_unref(req->msg);
++              g_free(req);
++              return invalid_args(msg);
++      }
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *adapter_set_mode(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *mode;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &mode,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (!mode)
++              return invalid_args(msg);
++ 
++      adapter->global_mode = str2mode(adapter->address, mode);
++ 
++      if (adapter->global_mode == adapter->mode)
++              return dbus_message_new_method_return(msg);
++ 
++      if (adapter->sessions && adapter->global_mode < adapter->mode)
++              return confirm_mode(conn, msg, mode, data);
++ 
++      return set_mode(conn, msg, str2mode(adapter->address, mode), data);
++ }
++ 
++ static DBusMessage *adapter_get_discoverable_to(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_UINT32, &adapter->discov_timeout,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void resolve_paths(DBusMessage *msg, char **old_path, char **new_path)
++ {
++      const char *path = dbus_message_get_path(msg);
++ 
++      if (!path)
++              return;
++ 
++      if (old_path)
++              *old_path = NULL;
++ 
++      if (new_path)
++              *new_path = NULL;
++ 
++      /* old path calls */
++      if (g_str_has_prefix(path, BASE_PATH)) {
++              if (old_path)
++                      *old_path = g_strdup(path);
++ 
++              if (hcid_dbus_use_experimental() && new_path)
++                      *new_path = g_strdup(path + ADAPTER_PATH_INDEX);
++ 
++              return;
++      }
++ 
++      if (old_path)
++              *old_path = g_strconcat(BASE_PATH, path, NULL);
++ 
++      if (new_path)
++              *new_path = g_strdup(path);
++ }
++ 
++ static DBusMessage *set_discoverable_timeout(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      uint32_t timeout,
++                                                      void *data)
++ {
++      struct adapter *adapter = data;
++      bdaddr_t bdaddr;
++      char *old_path, *new_path;
++ 
++      if (adapter->timeout_id) {
++              g_source_remove(adapter->timeout_id);
++              adapter->timeout_id = 0;
++      }
++ 
++      if ((timeout != 0) && (adapter->scan_enable & SCAN_INQUIRY))
++              adapter->timeout_id = g_timeout_add(timeout * 1000,
++                                              discov_timeout_handler,
++                                              adapter);
++ 
++      adapter->discov_timeout = timeout;
++ 
++      str2ba(adapter->address, &bdaddr);
++      write_discoverable_timeout(&bdaddr, timeout);
++ 
++      resolve_paths(msg, &old_path, &new_path);
++ 
++      g_dbus_emit_signal(conn, old_path,
++                                      ADAPTER_INTERFACE,
++                                      "DiscoverableTimeoutChanged",
++                                      DBUS_TYPE_UINT32, &timeout,
++                                      DBUS_TYPE_INVALID);
++      if (new_path) {
++              dbus_connection_emit_property_changed(conn, new_path,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoverableTimeout",
++                                              DBUS_TYPE_UINT32, &timeout);
++      }
++ 
++      g_free(old_path);
++      g_free(new_path);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_set_discoverable_to(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      uint32_t timeout;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_UINT32, &timeout,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      return set_discoverable_timeout(conn, msg, timeout, data);
++ }
++ 
++ static DBusMessage *adapter_is_connectable(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply;
++      const uint8_t scan_enable = adapter->scan_enable;
++      dbus_bool_t connectable = FALSE;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      if (scan_enable & SCAN_PAGE)
++              connectable = TRUE;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connectable,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_is_discoverable(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply;
++      const uint8_t scan_enable = adapter->scan_enable;
++      dbus_bool_t discoverable = FALSE;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      if (scan_enable & SCAN_INQUIRY)
++              discoverable = TRUE;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &discoverable,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_is_connected(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      dbus_bool_t connected = FALSE;
++ 
++      struct adapter *adapter = data;
++      GSList *l = adapter->active_conn;
++ 
++      const char *peer_addr;
++      bdaddr_t peer_bdaddr;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &peer_addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(peer_addr) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(peer_addr, &peer_bdaddr);
++ 
++      l = g_slist_find_custom(l, &peer_bdaddr, active_conn_find_by_bdaddr);
++      if (l)
++              connected = TRUE;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_list_connections(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      struct adapter *adapter = data;
++      GSList *l = adapter->active_conn;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                                      DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      while (l) {
++              char peer_addr[18];
++              const char *paddr = peer_addr;
++              struct active_conn_info *dev = l->data;
++ 
++              ba2str(&dev->bdaddr, peer_addr);
++ 
++              dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
++                                              &paddr);
++ 
++              l = l->next;
++      }
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_major_class(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply;
++      const char *str_ptr = "computer";
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      /* FIXME: Currently, only computer major class is supported */
++      if ((adapter->class[1] & 0x1f) != 1)
++              return unsupported_major_class(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_list_minor_classes(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const struct adapter *adapter = data;
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      const char **minor_ptr;
++      uint8_t major_class;
++      int size, i;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      major_class = adapter->class[1] & 0x1F;
++ 
++      switch (major_class) {
++      case 1: /* computer */
++              minor_ptr = computer_minor_cls;
++              size = sizeof(computer_minor_cls) / sizeof(*computer_minor_cls);
++              break;
++      case 2: /* phone */
++              minor_ptr = phone_minor_cls;
++              size = sizeof(phone_minor_cls) / sizeof(*phone_minor_cls);
++              break;
++      default:
++              return unsupported_major_class(msg);
++      }
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++      for (i = 0; i < size; i++)
++              dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
++                                              &minor_ptr[i]);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_minor_class(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      const char *str_ptr = "";
++      uint8_t minor_class;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      /* FIXME: Currently, only computer major class is supported */
++      if ((adapter->class[1] & 0x1f) != 1)
++              return unsupported_major_class(msg);
++ 
++      minor_class = adapter->class[0] >> 2;
++ 
++      /* Validate computer minor class */
++      if (minor_class > (sizeof(computer_minor_cls) / sizeof(*computer_minor_cls)))
++              goto failed;
++ 
++      str_ptr = computer_minor_cls[minor_class];
++ 
++ failed:
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_set_minor_class(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *minor;
++      uint32_t dev_class = 0xFFFFFFFF;
++      int i, dd;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &minor,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (!minor)
++              return invalid_args(msg);
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return no_such_adapter(msg);
++ 
++      /* Currently, only computer major class is supported */
++      if ((adapter->class[1] & 0x1f) != 1) {
++              hci_close_dev(dd);
++              return unsupported_major_class(msg);
++      }
++      for (i = 0; i < sizeof(computer_minor_cls) / sizeof(*computer_minor_cls); i++)
++              if (!strcasecmp(minor, computer_minor_cls[i])) {
++                      /* Remove the format type */
++                      dev_class = i << 2;
++                      break;
++              }
++ 
++      /* Check if it's a valid minor class */
++      if (dev_class == 0xFFFFFFFF) {
++              hci_close_dev(dd);
++              return invalid_args(msg);
++      }
++ 
++      /* set the service class and major class  */
++      dev_class |= (adapter->class[2] << 16) | (adapter->class[1] << 8);
++ 
++      if (hci_write_class_of_dev(dd, dev_class, 2000) < 0) {
++              int err = errno;
++              error("Can't write class of device on hci%d: %s(%d)",
++                              adapter->dev_id, strerror(errno), errno);
++              hci_close_dev(dd);
++              return failed_strerror(msg, err);
++      }
++ 
++      g_dbus_emit_signal(conn, dbus_message_get_path(msg),
++                                      ADAPTER_INTERFACE, "MinorClassChanged",
++                                      DBUS_TYPE_STRING, &minor,
++                                      DBUS_TYPE_INVALID);
++ 
++      hci_close_dev(dd);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_get_service_classes(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      const char *str_ptr;
++      int i;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      for (i = 0; i < (sizeof(service_cls) / sizeof(*service_cls)); i++) {
++              if (adapter->class[2] & (1 << i)) {
++                      str_ptr = service_cls[i];
++                      dbus_message_iter_append_basic(&array_iter,
++                                              DBUS_TYPE_STRING, &str_ptr);
++              }
++      }
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_name(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[249], *str_ptr = str;
++      int err;
++      bdaddr_t ba;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &ba);
++ 
++      err = read_local_name(&ba, str);
++      if (err < 0) {
++              if (!adapter->up)
++                      return adapter_not_ready(msg);
++ 
++              err = get_device_name(adapter->dev_id, str, sizeof(str));
++              if (err < 0)
++                      return failed_strerror(msg, -err);
++      }
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg,
++                                      const char *name, void *data)
++ {
++      struct adapter *adapter = data;
++      bdaddr_t bdaddr;
++      int ecode;
++      char *new_path;
++ 
++      if (!g_utf8_validate(name, -1, NULL)) {
++              error("Name change failed: the supplied name isn't valid UTF-8");
++              return invalid_args(msg);
++      }
++ 
++      str2ba(adapter->address, &bdaddr);
++ 
++      write_local_name(&bdaddr, (char *) name);
++ 
++      if (!adapter->up)
++              goto done;
++ 
++      ecode = set_device_name(adapter->dev_id, name);
++      if (ecode < 0)
++              return failed_strerror(msg, -ecode);
++ done:
++      resolve_paths(msg, NULL, &new_path);
++ 
++      if (new_path) {
++              dbus_connection_emit_property_changed(conn, new_path,
++                                              ADAPTER_INTERFACE,
++                                              "Name", DBUS_TYPE_STRING,
++                                              &name);
++      }
++ 
++      g_free(new_path);
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_set_name(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      char *str_ptr;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &str_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      return set_name(conn, msg, str_ptr, data);
++ }
++ 
++ static DBusMessage *adapter_get_remote_info(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter dict;
++      bdaddr_t src, dst;
++      const char *addr_ptr;
++      char filename[PATH_MAX + 1];
++      char buf[64];
++      const char *ptr;
++      char *str;
++      dbus_bool_t boolean;
++      uint32_t class;
++      int compid, ver, subver;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
++                      DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
++                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
++ 
++      /* Name */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "names");
++      str = textfile_caseget(filename, addr_ptr);
++      if (str) {
++              dbus_message_iter_append_dict_entry(&dict, "name",
++                              DBUS_TYPE_STRING, &str);
++              free(str);
++      }
++ 
++      str2ba(adapter->address, &src);
++      str2ba(addr_ptr, &dst);
++ 
++      /* Remote device class */
++      if (read_remote_class(&src, &dst, &class) == 0) {
++ 
++              dbus_message_iter_append_dict_entry(&dict, "class",
++                              DBUS_TYPE_UINT32, &class);
++ 
++              ptr = major_class_str(class);
++              dbus_message_iter_append_dict_entry(&dict, "major_class",
++                              DBUS_TYPE_STRING, &ptr);
++ 
++              ptr = minor_class_str(class);
++              dbus_message_iter_append_dict_entry(&dict, "minor_class",
++                              DBUS_TYPE_STRING, &ptr);
++      }
++ 
++      /* Alias */
++      if (get_device_alias(adapter->dev_id, &dst, buf, sizeof(buf)) > 0) {
++              ptr = buf;
++              dbus_message_iter_append_dict_entry(&dict, "alias",
++                              DBUS_TYPE_STRING, &ptr);
++      }
++ 
++      /* Bonded */
++      create_name(filename, PATH_MAX, STORAGEDIR,
++                      adapter->address, "linkkeys");
++      str = textfile_caseget(filename, addr_ptr);
++      if (str) {
++              boolean = TRUE;
++              free(str);
++      } else {
++              boolean = FALSE;
++      }
++ 
++      dbus_message_iter_append_dict_entry(&dict, "bonded",
++                      DBUS_TYPE_BOOLEAN, &boolean);
++ 
++      /* Trusted */
++      boolean = read_trust(&src, addr_ptr, GLOBAL_TRUST);
++      dbus_message_iter_append_dict_entry(&dict, "trusted",
++                      DBUS_TYPE_BOOLEAN, &boolean);
++ 
++      /* Connected */
++      if (g_slist_find_custom(adapter->active_conn, &dst,
++                              active_conn_find_by_bdaddr))
++              boolean = TRUE;
++      else
++              boolean = FALSE;
++ 
++      dbus_message_iter_append_dict_entry(&dict, "connected",
++                      DBUS_TYPE_BOOLEAN, &boolean);
++ 
++      /* HCI Revision/Manufacturer/Version */
++      create_name(filename, PATH_MAX, STORAGEDIR,
++                      adapter->address, "manufacturers");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              goto done;
++ 
++      if (sscanf(str, "%d %d %d", &compid, &ver, &subver) != 3) {
++              /* corrupted file data */
++              free(str);
++              goto done;
++      }
++ 
++      free(str);
++ 
++      ptr = buf;
++      snprintf(buf, 16, "HCI 0x%X", subver);
++      dbus_message_iter_append_dict_entry(&dict, "revision",
++                      DBUS_TYPE_STRING, &ptr);
++ 
++      ptr = bt_compidtostr(compid);
++      dbus_message_iter_append_dict_entry(&dict, "manufacturer",
++                      DBUS_TYPE_STRING, &ptr);
++ 
++      str = lmp_vertostr(ver);
++      snprintf(buf, 64, "Bluetooth %s", str);
++      bt_free(str);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR,
++                      adapter->address, "features");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (str) {
++              if (strlen(str) == 16) {
++                      uint8_t features;
++                      /* Getting the third byte */
++                      features  = ((str[6] - 48) << 4) | (str[7] - 48);
++                      if (features & (LMP_EDR_ACL_2M | LMP_EDR_ACL_3M))
++                              snprintf(buf, 64, "Bluetooth %s + EDR", ptr);
++ 
++              }
++              free(str);
++      }
++      ptr = buf;
++      dbus_message_iter_append_dict_entry(&dict, "version",
++                      DBUS_TYPE_STRING, &ptr);
++ 
++ done:
++      dbus_message_iter_close_container(&iter, &dict);
++ 
++      return reply;
++ }
++ 
+++static DBusMessage *adapter_get_remote_svc_channel(DBusConnection *conn,
+++                                     DBusMessage *msg, void *data)
+++{
+++     return get_remote_svc_channel(conn, msg, data);
+++}
+++
++ static DBusMessage *adapter_get_remote_svc(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_BINARY);
++ }
++ 
++ static DBusMessage *adapter_get_remote_svc_xml(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_XML);
++ }
++ 
++ static DBusMessage *adapter_get_remote_svc_handles(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      return get_remote_svc_handles(conn, msg, data);
++ }
++ 
++ static DBusMessage *adapter_get_remote_svc_identifiers(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      return get_remote_svc_identifiers(conn, msg, data);
++ }
++ 
++ static DBusMessage *adapter_finish_sdp_transact(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      return finish_remote_svc_transact(conn, msg, data);
++ }
++ 
++ static DBusMessage *adapter_get_remote_version(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++      char *str_ver = NULL;
++      char info_array[64], *info = info_array;
++      int compid, ver, subver;
++ 
++      memset(info_array, 0, 64);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "manufacturers");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              return not_available(msg);
++ 
++      if (sscanf(str, "%d %d %d", &compid, &ver, &subver) != 3) {
++              /* corrupted file data */
++              free(str);
++              goto failed;
++      }
++ 
++      free(str);
++ 
++      str_ver = lmp_vertostr(ver);
++ 
++      /* Default value */
++      snprintf(info, 64, "Bluetooth %s", str_ver);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "features");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              goto failed;
++ 
++      /* Check if the data is not corrupted */
++      if (strlen(str) == 16) {
++              uint8_t features;
++              /* Getting the third byte */
++              features  = ((str[6] - 48) << 4) | (str[7] - 48);
++              if (features & (LMP_EDR_ACL_2M | LMP_EDR_ACL_3M))
++                      snprintf(info, 64, "Bluetooth %s + EDR", str_ver);
++      }
++ 
++      free(str);
++ 
++ failed:
++      if (str_ver)
++              bt_free(str_ver);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_revision(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++      char info_array[16], *info = info_array;
++      int compid, ver, subver;
++ 
++      memset(info_array, 0, 16);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "manufacturers");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              return not_available(msg);
++ 
++      if (sscanf(str, "%d %d %d", &compid, &ver, &subver) == 3)
++              snprintf(info, 16, "HCI 0x%X", subver);
++ 
++      free(str);
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_manufacturer(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++      char info_array[64], *info = info_array;
++      int compid, ver, subver;
++ 
++      memset(info_array, 0, 64);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "manufacturers");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              return not_available(msg);
++ 
++      if (sscanf(str, "%d %d %d", &compid, &ver, &subver) == 3)
++              info = bt_compidtostr(compid);
++ 
++      free(str);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_company(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      bdaddr_t bdaddr;
++      char oui[9], *str_bdaddr, *tmp;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &str_bdaddr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      str2ba(str_bdaddr, &bdaddr);
++      ba2oui(&bdaddr, oui);
++ 
++      tmp = ouitocomp(oui);
++      if (!tmp)
++              return not_available(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              free(tmp);
++              return NULL;
++      }
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &tmp,
++                                      DBUS_TYPE_INVALID);
++ 
++      free(tmp);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *get_remote_class(DBusConnection *conn, DBusMessage *msg,
++                                              void *data, uint32_t *class)
++ {
++      struct adapter *adapter = data;
++      char *addr_peer;
++      bdaddr_t local, peer;
++      int ecode;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_peer,
++                              DBUS_TYPE_INVALID)) {
++              return invalid_args(msg);
++      }
++ 
++      if (check_address(addr_peer) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(addr_peer, &peer);
++      str2ba(adapter->address, &local);
++ 
++      ecode = read_remote_class(&local, &peer, class);
++      if (ecode < 0)
++              return not_available(msg);
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *adapter_get_remote_major_class(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      const char *major_class;
++      uint32_t class;
++ 
++      reply = get_remote_class(conn, msg, data, &class);
++      if (reply)
++              return reply;
++ 
++      major_class = major_class_str(class);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &major_class,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_minor_class(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      const char *minor_class;
++      uint32_t class;
++ 
++      reply = get_remote_class(conn, msg, data, &class);
++      if (reply)
++              return reply;
++ 
++      minor_class = minor_class_str(class);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &minor_class,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void append_class_string(const char *class, DBusMessageIter *iter)
++ {
++      dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &class);
++ }
++ 
++ static DBusMessage *adapter_get_remote_service_cls(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      DBusMessage *reply;
++      DBusMessageIter iter, array_iter;
++      GSList *service_classes;
++      uint32_t class;
++ 
++      reply = get_remote_class(conn, msg, data, &class);
++      if (reply)
++              return reply;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      service_classes = service_classes_str(class);
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      g_slist_foreach(service_classes, (GFunc) append_class_string,
++                      &array_iter);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      g_slist_free(service_classes);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_class(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      uint32_t class;
++ 
++      reply = get_remote_class(conn, msg, data, &class);
++      if (reply)
++              return reply;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_UINT32, &class,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_features(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      char filename[PATH_MAX + 1];
++      struct adapter *adapter = data;
++      DBusMessage *reply = NULL;
++      DBusMessageIter iter, array_iter;
++      uint8_t features[8], *ptr = features;
++      const char *addr;
++      char *str;
++      int i;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "features");
++ 
++      str = textfile_caseget(filename, addr);
++      if (!str)
++              return not_available(msg);
++ 
++      memset(features, 0, sizeof(features));
++      for (i = 0; i < sizeof(features); i++) {
++              char tmp[3];
++ 
++              memcpy(tmp, str + (i * 2), 2);
++              tmp[2] = '\0';
++ 
++              features[i] = (uint8_t) strtol(tmp, NULL, 16);
++      }
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              free(str);
++              return NULL;
++      }
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_BYTE_AS_STRING, &array_iter);
++ 
++      dbus_message_iter_append_fixed_array(&array_iter,
++                              DBUS_TYPE_BYTE, &ptr, sizeof(features));
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      free(str);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_remote_name(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      char filename[PATH_MAX + 1];
++      struct adapter *adapter = data;
++      DBusMessage *reply = NULL;
++      const char *peer_addr;
++      bdaddr_t peer_bdaddr;
++      char *str;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &peer_addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(peer_addr) < 0)
++              return invalid_args(msg);
++ 
++      /* check if it is in the cache */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "names");
++ 
++      str = textfile_caseget(filename, peer_addr);
++ 
++      if (str) {
++              reply = dbus_message_new_method_return(msg);
++              if (!reply) {
++                      free(str);
++                      return NULL;
++              }
++ 
++              /* send the cached name */
++              dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
++                                              DBUS_TYPE_INVALID);
++ 
++              free(str);
++              return reply;
++      }
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      /* If the discover process is not running, return an error */
++      if (!adapter->discov_active && !adapter->pdiscov_active)
++              return not_available(msg);
++ 
++      /* Queue the request when there is a discovery running */
++      str2ba(peer_addr, &peer_bdaddr);
++      found_device_add(&adapter->found_devices, &peer_bdaddr, 0, NAME_REQUIRED);
++ 
++         return g_dbus_create_error(msg,
++                      ERROR_INTERFACE ".RequestDeferred",
++                      "Request Deferred");
++ }
++ 
++ static DBusMessage *adapter_get_remote_alias(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char str[249], *str_ptr = str, *addr_ptr;
++      bdaddr_t bdaddr;
++      int ecode;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(addr_ptr, &bdaddr);
++ 
++      ecode = get_device_alias(adapter->dev_id, &bdaddr, str, sizeof(str));
++      if (ecode < 0)
++              return not_available(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_set_remote_alias(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      char *alias, *addr, *old_path, *new_path;
++      bdaddr_t bdaddr;
++      int ecode;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr,
++                              DBUS_TYPE_STRING, &alias,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if ((strlen(alias) == 0) || (check_address(addr) < 0)) {
++              error("Alias change failed: Invalid parameter");
++              return invalid_args(msg);
++      }
++ 
++      str2ba(addr, &bdaddr);
++ 
++      ecode = set_device_alias(adapter->dev_id, &bdaddr, alias);
++      if (ecode < 0)
++              return failed_strerror(msg, -ecode);
++ 
++      resolve_paths(msg, &old_path, &new_path);
++ 
++      g_dbus_emit_signal(conn, old_path,
++                                      ADAPTER_INTERFACE, "RemoteAliasChanged",
++                                      DBUS_TYPE_STRING, &addr,
++                                      DBUS_TYPE_STRING, &alias,
++                                      DBUS_TYPE_INVALID);
++ 
++      if (new_path) {
++              struct device *device;
++ 
++              device = adapter_find_device(adapter, addr);
++              if (device) {
++                      dbus_connection_emit_property_changed(conn,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Alias", DBUS_TYPE_STRING, &alias);
++              }
++      }
++ 
++      g_free(old_path);
++      g_free(new_path);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_clear_remote_alias(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      char *addr_ptr;
++      bdaddr_t bdaddr;
++      int ecode, had_alias = 1;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0) {
++              error("Alias clear failed: Invalid parameter");
++              return invalid_args(msg);
++      }
++ 
++      str2ba(addr_ptr, &bdaddr);
++ 
++      ecode = get_device_alias(adapter->dev_id, &bdaddr, NULL, 0);
++      if (ecode == -ENXIO)
++              had_alias = 0;
++ 
++      ecode = set_device_alias(adapter->dev_id, &bdaddr, NULL);
++      if (ecode < 0)
++              return failed_strerror(msg, -ecode);
++ 
++      if (had_alias)
++              g_dbus_emit_signal(conn, dbus_message_get_path(msg),
++                                              ADAPTER_INTERFACE,
++                                              "RemoteAliasCleared",
++                                              DBUS_TYPE_STRING, &addr_ptr,
++                                              DBUS_TYPE_INVALID);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_last_seen(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "lastseen");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              return not_available(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              free(str);
++              return NULL;
++      }
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
++                                      DBUS_TYPE_INVALID);
++ 
++      free(str);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_last_used(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "lastused");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (!str)
++              return not_available(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              free(str);
++              return NULL;
++      }
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
++                                      DBUS_TYPE_INVALID);
++ 
++      free(str);
++ 
++      return reply;
++ }
++ 
++ gboolean dc_pending_timeout_handler(void *data)
++ {
++      int dd;
++      struct adapter *adapter = data;
++      struct pending_dc_info *pending_dc = adapter->pending_dc;
++      DBusMessage *reply;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++ 
++      if (dd < 0) {
++              error_no_such_adapter(pending_dc->conn,
++                                    pending_dc->msg);
++              dc_pending_timeout_cleanup(adapter);
++              return FALSE;
++      }
++ 
++      /* Send the HCI disconnect command */
++      if (hci_disconnect(dd, htobs(pending_dc->conn_handle),
++                              HCI_OE_USER_ENDED_CONNECTION,
++                              500) < 0) {
++              int err = errno;
++              error("Disconnect failed");
++              error_failed_errno(pending_dc->conn, pending_dc->msg, err);
++      } else {
++              reply = dbus_message_new_method_return(pending_dc->msg);
++              if (reply) {
++                      dbus_connection_send(pending_dc->conn, reply, NULL);
++                      dbus_message_unref(reply);
++              } else
++                      error("Failed to allocate disconnect reply");
++      }
++ 
++      hci_close_dev(dd);
++      dc_pending_timeout_cleanup(adapter);
++ 
++      return FALSE;
++ }
++ 
++ void dc_pending_timeout_cleanup(struct adapter *adapter)
++ {
++      dbus_connection_unref(adapter->pending_dc->conn);
++      dbus_message_unref(adapter->pending_dc->msg);
++      g_free(adapter->pending_dc);
++      adapter->pending_dc = NULL;
++ }
++ 
++ static DBusMessage *adapter_dc_remote_device(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      GSList *l = adapter->active_conn;
++      const char *peer_addr;
++      bdaddr_t peer_bdaddr;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &peer_addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(peer_addr) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(peer_addr, &peer_bdaddr);
++ 
++      l = g_slist_find_custom(l, &peer_bdaddr, active_conn_find_by_bdaddr);
++      if (!l)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".NotConnected",
++                              "Device not connected");
++ 
++      if (adapter->pending_dc)
++              return in_progress(msg, "Disconnection in progress");
++ 
++      adapter->pending_dc = g_new0(struct pending_dc_info, 1);
++ 
++      /* Start waiting... */
++      adapter->pending_dc->timeout_id =
++              g_timeout_add(DC_PENDING_TIMEOUT,
++                            dc_pending_timeout_handler,
++                            adapter);
++ 
++      if (!adapter->pending_dc->timeout_id) {
++              g_free(adapter->pending_dc);
++              adapter->pending_dc = NULL;
++              return NULL;
++      }
++ 
++      adapter->pending_dc->conn = dbus_connection_ref(conn);
++      adapter->pending_dc->msg = dbus_message_ref(msg);
++      adapter->pending_dc->conn_handle =
++              ((struct active_conn_info *) l->data)->handle;
++ 
++      g_dbus_emit_signal(conn, dbus_message_get_path(msg),
++                                      ADAPTER_INTERFACE,
++                                      "RemoteDeviceDisconnectRequested",
++                                      DBUS_TYPE_STRING, &peer_addr,
++                                      DBUS_TYPE_INVALID);
++ 
++      return NULL;
++ }
++ 
++ static void reply_authentication_failure(struct bonding_request_info *bonding)
++ {
++      DBusMessage *reply;
++      int status;
++ 
++      status = bonding->hci_status ?
++                      bonding->hci_status : HCI_AUTHENTICATION_FAILURE;
++ 
++      reply = new_authentication_return(bonding->msg, status);
++      if (reply) {
++              dbus_connection_send(bonding->conn, reply, NULL);
++              dbus_message_unref(reply);
++      }
++ }
++ 
++ struct device *adapter_find_device(struct adapter *adapter, const char *dest)
++ {
++      struct device *device;
++      GSList *l;
++ 
++      if (!adapter)
++              return NULL;
++ 
++      l = g_slist_find_custom(adapter->devices,
++                              dest, (GCompareFunc) device_address_cmp);
++      if (!l)
++              return NULL;
++ 
++      device = l->data;
++ 
++      return device;
++ }
++ 
++ struct device *adapter_create_device(DBusConnection *conn,
++                              struct adapter *adapter, const char *address)
++ {
++      struct device *device;
++ 
++      debug("adapter_create_device(%s)", address);
++ 
++      device = device_create(conn, adapter, address);
++      if (!device)
++              return NULL;
++ 
++      device->temporary = TRUE;
++ 
++      adapter->devices = g_slist_append(adapter->devices, device);
++ 
++      return device;
++ }
++ 
++ static DBusMessage *remove_bonding(DBusConnection *conn, DBusMessage *msg,
++                                      const char *address, void *data)
++ {
++      struct adapter *adapter = data;
++      struct device *device;
++      char path[MAX_PATH_LENGTH], filename[PATH_MAX + 1];
++      char *str;
++      bdaddr_t src, dst;
++      GSList *l;
++      int dev, err;
++      gboolean paired;
++ 
++      str2ba(adapter->address, &src);
++      str2ba(address, &dst);
++ 
++      dev = hci_open_dev(adapter->dev_id);
++      if (dev < 0 && msg)
++              return no_such_adapter(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "linkkeys");
++ 
++      /* textfile_del doesn't return an error when the key is not found */
++      str = textfile_caseget(filename, address);
++      paired = str ? TRUE : FALSE;
++      g_free(str);
++ 
++      if (!paired && msg) {
++              hci_close_dev(dev);
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Bonding does not exist");
++      }
++ 
++      /* Delete the link key from storage */
++      if (textfile_casedel(filename, address) < 0 && msg) {
++              hci_close_dev(dev);
++              err = errno;
++              return failed_strerror(msg, err);
++      }
++ 
++      /* Delete the link key from the Bluetooth chip */
++      hci_delete_stored_link_key(dev, &dst, 0, 1000);
++ 
++      /* find the connection */
++      l = g_slist_find_custom(adapter->active_conn, &dst,
++                              active_conn_find_by_bdaddr);
++      if (l) {
++              struct active_conn_info *con = l->data;
++              /* Send the HCI disconnect command */
++              if ((hci_disconnect(dev, htobs(con->handle),
++                                      HCI_OE_USER_ENDED_CONNECTION, 500) < 0)
++                                      && msg){
++                      int err = errno;
++                      error("Disconnect failed");
++                      hci_close_dev(dev);
++                      return failed_strerror(msg, err);
++              }
++      }
++ 
++      hci_close_dev(dev);
++ 
++      if (paired) {
++              snprintf(path, MAX_PATH_LENGTH, BASE_PATH "/hci%d",
++                      adapter->dev_id);
++              g_dbus_emit_signal(conn, path,
++                                      ADAPTER_INTERFACE, "BondingRemoved",
++                                      DBUS_TYPE_STRING, &address,
++                                      DBUS_TYPE_INVALID);
++      }
++ 
++      device = adapter_find_device(adapter, address);
++      if (!device)
++              goto proceed;
++ 
++      if (paired) {
++              gboolean paired = FALSE;
++              dbus_connection_emit_property_changed(conn, device->path,
++                                      DEVICE_INTERFACE, "Paired",
++                                      DBUS_TYPE_BOOLEAN, &paired);
++      }
++ 
++ proceed:
++      if(!msg)
++              goto done;
++ 
++      return dbus_message_new_method_return(msg);
++ 
++ done:
++      return NULL;
++ }
++ 
++ 
++ void adapter_remove_device(DBusConnection *conn, struct adapter *adapter,
++                              struct device *device)
++ {
++      bdaddr_t src;
++      char path[MAX_PATH_LENGTH];
++ 
++      str2ba(adapter->address, &src);
++      delete_entry(&src, "profiles", device->address);
++ 
++      remove_bonding(conn, NULL, device->address, adapter);
++ 
++      if (!device->temporary) {
++              snprintf(path, MAX_PATH_LENGTH, "/hci%d", adapter->dev_id);
++              g_dbus_emit_signal(conn, path,
++                              ADAPTER_INTERFACE,
++                              "DeviceRemoved",
++                              DBUS_TYPE_OBJECT_PATH, &device->path,
++                              DBUS_TYPE_INVALID);
++      }
++ 
++      if (device->agent) {
++              agent_destroy(device->agent, FALSE);
++              device->agent = NULL;
++      }
++ 
++      adapter->devices = g_slist_remove(adapter->devices, device);
++ 
++      device_remove(conn, device);
++ }
++ 
++ struct device *adapter_get_device(DBusConnection *conn,
++                              struct adapter *adapter, const gchar *address)
++ {
++      struct device *device;
++ 
++      debug("adapter_get_device(%s)", address);
++ 
++      if (!adapter)
++              return NULL;
++ 
++      device = adapter_find_device(adapter, address);
++      if (device)
++              return device;
++ 
++      return adapter_create_device(conn, adapter, address);
++ }
++ 
++ void remove_pending_device(struct adapter *adapter)
++ {
++      struct device *device;
++      char address[18];
++ 
++      ba2str(&adapter->bonding->bdaddr, address);
++      device = adapter_find_device(adapter, address);
++      if (!device)
++              return;
++ 
++      if (device->temporary)
++              adapter_remove_device(adapter->bonding->conn, adapter, device);
++ }
++ 
++ static gboolean create_bonding_conn_complete(GIOChannel *io, GIOCondition cond,
++                                              struct adapter *adapter)
++ {
++      struct hci_request rq;
++      auth_requested_cp cp;
++      evt_cmd_status rp;
++      struct l2cap_conninfo cinfo;
++      socklen_t len;
++      int sk, dd, ret;
++ 
++      if (!adapter->bonding) {
++              /* If we come here it implies a bug somewhere */
++              debug("create_bonding_conn_complete: no pending bonding!");
++              g_io_channel_close(io);
++              g_io_channel_unref(io);
++              return FALSE;
++      }
++ 
++      if (cond & G_IO_NVAL) {
++              error_authentication_canceled(adapter->bonding->conn,
++                                              adapter->bonding->msg);
++              goto cleanup;
++      }
++ 
++      if (cond & (G_IO_HUP | G_IO_ERR)) {
++              debug("Hangup or error on bonding IO channel");
++ 
++              if (!adapter->bonding->auth_active)
++                      error_connection_attempt_failed(adapter->bonding->conn,
++                                                      adapter->bonding->msg,
++                                                      ENETDOWN);
++              else
++                      reply_authentication_failure(adapter->bonding);
++ 
++              goto failed;
++      }
++ 
++      sk = g_io_channel_unix_get_fd(io);
++ 
++      len = sizeof(ret);
++      if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
++              error("Can't get socket error: %s (%d)",
++                              strerror(errno), errno);
++              error_failed_errno(adapter->bonding->conn, adapter->bonding->msg,
++                              errno);
++              goto failed;
++      }
++ 
++      if (ret != 0) {
++              if (adapter->bonding->auth_active)
++                      reply_authentication_failure(adapter->bonding);
++              else
++                      error_connection_attempt_failed(adapter->bonding->conn,
++                                                      adapter->bonding->msg,
++                                                      ret);
++              goto failed;
++      }
++ 
++      len = sizeof(cinfo);
++      if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &cinfo, &len) < 0) {
++              error("Can't get connection info: %s (%d)",
++                              strerror(errno), errno);
++              error_failed_errno(adapter->bonding->conn, adapter->bonding->msg,
++                              errno);
++              goto failed;
++      }
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error_no_such_adapter(adapter->bonding->conn,
++                                      adapter->bonding->msg);
++              goto failed;
++      }
++ 
++      memset(&rp, 0, sizeof(rp));
++ 
++      memset(&cp, 0, sizeof(cp));
++      cp.handle = htobs(cinfo.hci_handle);
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_AUTH_REQUESTED;
++      rq.cparam = &cp;
++      rq.clen   = AUTH_REQUESTED_CP_SIZE;
++      rq.rparam = &rp;
++      rq.rlen   = EVT_CMD_STATUS_SIZE;
++      rq.event  = EVT_CMD_STATUS;
++ 
++      if (hci_send_req(dd, &rq, 500) < 0) {
++              error("Unable to send HCI request: %s (%d)",
++                                      strerror(errno), errno);
++              error_failed_errno(adapter->bonding->conn, adapter->bonding->msg,
++                              errno);
++              hci_close_dev(dd);
++              goto failed;
++      }
++ 
++      if (rp.status) {
++              error("HCI_Authentication_Requested failed with status 0x%02x",
++                              rp.status);
++              error_failed_errno(adapter->bonding->conn, adapter->bonding->msg,
++                              bt_error(rp.status));
++              hci_close_dev(dd);
++              goto failed;
++      }
++ 
++      hci_close_dev(dd);
++ 
++      adapter->bonding->auth_active = 1;
++ 
++      adapter->bonding->io_id = g_io_add_watch(io,
++                                              G_IO_NVAL | G_IO_HUP | G_IO_ERR,
++                                              (GIOFunc) create_bonding_conn_complete,
++                                              adapter);
++ 
++      return FALSE;
++ 
++ failed:
++      g_io_channel_close(io);
++      remove_pending_device(adapter);
++ 
++ cleanup:
++      g_dbus_remove_watch(adapter->bonding->conn,
++                              adapter->bonding->listener_id);
++      bonding_request_free(adapter->bonding);
++      adapter->bonding = NULL;
++ 
++      return FALSE;
++ }
++ 
++ static void cancel_auth_request(struct pending_auth_info *auth, int dev_id)
++ {
++      int dd;
++ 
++      if (auth->replied)
++              return;
++ 
++      dd = hci_open_dev(dev_id);
++      if (dd < 0) {
++              error("hci_open_dev: %s (%d)", strerror(errno), errno);
++              return;
++      }
++ 
++      switch (auth->type) {
++      case AUTH_TYPE_PINCODE:
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY,
++                              6, &auth->bdaddr);
++              break;
++      case AUTH_TYPE_CONFIRM:
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
++                              6, &auth->bdaddr);
++              break;
++      case AUTH_TYPE_PASSKEY:
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_PASSKEY_NEG_REPLY,
++                              6, &auth->bdaddr);
++              break;
++      case AUTH_TYPE_NOTIFY:
++              /* User Notify doesn't require any reply */
++              break;
++      }
++ 
++      auth->replied = TRUE;
++ 
++      hci_close_dev(dd);
++ }
++ 
++ static void create_bond_req_exit(void *user_data)
++ {
++      struct adapter *adapter = user_data;
++      struct pending_auth_info *auth;
++      char path[MAX_PATH_LENGTH];
++ 
++      snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, adapter->dev_id);
++ 
++      debug("CreateConnection requestor exited before bonding was completed");
++ 
++      cancel_passkey_agent_requests(adapter->passkey_agents, path,
++                                      &adapter->bonding->bdaddr);
++      release_passkey_agents(adapter, &adapter->bonding->bdaddr);
++ 
++      auth = adapter_find_auth_request(adapter, &adapter->bonding->bdaddr);
++      if (auth) {
++              cancel_auth_request(auth, adapter->dev_id);
++              if (auth->agent)
++                      agent_cancel(auth->agent);
++              adapter_remove_auth_request(adapter, &adapter->bonding->bdaddr);
++      }
++ 
++      remove_pending_device(adapter);
++ 
++      g_io_channel_close(adapter->bonding->io);
++      if (adapter->bonding->io_id)
++              g_source_remove(adapter->bonding->io_id);
++      bonding_request_free(adapter->bonding);
++      adapter->bonding = NULL;
++ }
++ 
++ static DBusMessage *create_bonding(DBusConnection *conn, DBusMessage *msg,
++                              const char *address, const char *agent_path,
++                              uint8_t capability, void *data)
++ {
++      char filename[PATH_MAX + 1];
++      char *str;
++      struct adapter *adapter = data;
++      struct bonding_request_info *bonding;
++      bdaddr_t bdaddr;
++      int sk;
++ 
++      str2ba(address, &bdaddr);
++ 
++      /* check if there is a pending discover: requested by D-Bus/non clients */
++      if (adapter->discov_active || (adapter->pdiscov_active && !adapter->pinq_idle))
++              return in_progress(msg, "Discover in progress");
++ 
++      pending_remote_name_cancel(adapter);
++ 
++      if (adapter->bonding)
++              return in_progress(msg, "Bonding in progress");
++ 
++      if (adapter_find_auth_request(adapter, &bdaddr))
++              return in_progress(msg, "Bonding in progress");
++ 
++      /* check if a link key already exists */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "linkkeys");
++ 
++      str = textfile_caseget(filename, address);
++      if (str) {
++              free(str);
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Bonding already exists");
++      }
++ 
++      sk = l2raw_connect(adapter->address, &bdaddr);
++      if (sk < 0)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".ConnectionAttemptFailed",
++                              "Connection attempt failed");
++ 
++      bonding = bonding_request_new(conn, msg, adapter, address, agent_path,
++                                      capability);
++      if (!bonding) {
++              close(sk);
++              return NULL;
++      }
++ 
++      bonding->io = g_io_channel_unix_new(sk);
++      bonding->io_id = g_io_add_watch(bonding->io,
++                                      G_IO_OUT | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
++                                      (GIOFunc) create_bonding_conn_complete,
++                                      adapter);
++ 
++      bonding->listener_id = g_dbus_add_disconnect_watch(conn,
++                                              dbus_message_get_sender(msg),
++                                              create_bond_req_exit, adapter,
++                                              NULL);
++ 
++      adapter->bonding = bonding;
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *adapter_create_bonding(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      char *address;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &address,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      return create_bonding(conn, msg, address, NULL,
++                              IO_CAPABILITY_INVALID, data);
++ }
++ static DBusMessage *adapter_cancel_bonding(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *address;
++      bdaddr_t bdaddr;
++      struct bonding_request_info *bonding = adapter->bonding;
++      struct pending_auth_info *auth_req;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &address,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(address, &bdaddr);
++      if (!bonding || bacmp(&bonding->bdaddr, &bdaddr))
++              return not_in_progress(msg, "Bonding is not in progress");
++ 
++      if (strcmp(dbus_message_get_sender(adapter->bonding->msg),
++                              dbus_message_get_sender(msg)))
++              return not_authorized(msg);
++ 
++      adapter->bonding->cancel = 1;
++ 
++      auth_req = adapter_find_auth_request(adapter, &bdaddr);
++      if (auth_req) {
++              if (auth_req->replied) {
++                      /*
++                       * If disconnect can't be applied and the PIN code
++                       * request was already replied it doesn't make sense
++                       * cancel the remote passkey: return not authorized.
++                       */
++                      g_io_channel_close(adapter->bonding->io);
++                      return not_authorized(msg);
++              }
++ 
++              cancel_auth_request(auth_req, adapter->dev_id);
++              if (auth_req->agent)
++                      agent_cancel(auth_req->agent);
++              adapter_remove_auth_request(adapter, &bdaddr);
++      }
++ 
++      g_io_channel_close(adapter->bonding->io);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_remove_bonding(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      char *address;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &address,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      return remove_bonding(conn, msg, address, data);
++ }
++ 
++ static DBusMessage *adapter_has_bonding(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      char *addr_ptr, *str;
++      dbus_bool_t result;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "linkkeys");
++ 
++      str = textfile_caseget(filename, addr_ptr);
++      if (str) {
++              result = TRUE;
++              free(str);
++      } else
++              result = FALSE;
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &result,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void list_bondings_do_append(char *key, char *value, void *data)
++ {
++      DBusMessageIter *iter = data;
++      dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
++ }
++ 
++ static DBusMessage *adapter_list_bondings(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
++                      "linkkeys");
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      textfile_foreach(filename, list_bondings_do_append, &array_iter);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_pin_code_length(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      bdaddr_t local, peer;
++      char *addr_ptr;
++      uint8_t length;
++      int len;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &local);
++ 
++      str2ba(addr_ptr, &peer);
++ 
++      len = read_pin_length(&local, &peer);
++      if (len < 0)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Record does not exist");
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      length = len;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BYTE, &length,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_get_encryption_key_size(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      bdaddr_t bdaddr;
++      char *addr_ptr;
++      uint8_t size;
++      int val;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &addr_ptr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(addr_ptr) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(addr_ptr, &bdaddr);
++ 
++      val = get_encryption_key_size(adapter->dev_id, &bdaddr);
++      if (val < 0)
++              return failed_strerror(msg, -val);
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      size = val;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BYTE, &size,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void periodic_discover_req_exit(void *user_data)
++ {
++      struct adapter *adapter = user_data;
++ 
++      debug("PeriodicDiscovery requestor exited");
++ 
++      /* Cleanup the discovered devices list and send the cmd to exit from
++       * periodic inquiry or cancel remote name request. The return value can
++       * be ignored. */
++ 
++      cancel_periodic_discovery(adapter);
++ }
++ 
++ static DBusMessage *adapter_start_periodic(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      periodic_inquiry_cp cp;
++      struct hci_request rq;
++      struct adapter *adapter = data;
++      uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
++      uint8_t status;
++      int dd;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
++                              "StartPeriodicDiscovery")) {
++              if (!dbus_message_has_signature(msg,
++                                      DBUS_TYPE_INVALID_AS_STRING))
++                      return invalid_args(msg);
++      }
++ 
++      if (adapter->discov_active || adapter->pdiscov_active)
++              return in_progress(msg, "Discover in progress");
++ 
++      pending_remote_name_cancel(adapter);
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return no_such_adapter(msg);
++ 
++      memset(&cp, 0, sizeof(cp));
++      memcpy(&cp.lap, lap, 3);
++      cp.max_period = htobs(24);
++      cp.min_period = htobs(16);
++      cp.length  = 0x08;
++      cp.num_rsp = 0x00;
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_PERIODIC_INQUIRY;
++      rq.cparam = &cp;
++      rq.clen   = PERIODIC_INQUIRY_CP_SIZE;
++      rq.rparam = &status;
++      rq.rlen   = sizeof(status);
++      rq.event  = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, 1000) < 0) {
++              int err = errno;
++              error("Unable to start periodic inquiry: %s (%d)",
++                              strerror(errno), errno);
++              hci_close_dev(dd);
++              return failed_strerror(msg, err);
++      }
++ 
++      if (status) {
++              error("HCI_Periodic_Inquiry_Mode failed with status 0x%02x",
++                              status);
++              hci_close_dev(dd);
++              return failed_strerror(msg, bt_error(status));
++      }
++ 
++      adapter->pdiscov_requestor = g_strdup(dbus_message_get_sender(msg));
++ 
++      if (adapter->pdiscov_resolve_names)
++              adapter->discov_type = PERIODIC_INQUIRY | RESOLVE_NAME;
++      else
++              adapter->discov_type = PERIODIC_INQUIRY;
++ 
++      hci_close_dev(dd);
++ 
++      /* track the request owner to cancel it automatically if the owner
++       * exits */
++      adapter->pdiscov_listener = g_dbus_add_disconnect_watch(conn,
++                                              dbus_message_get_sender(msg),
++                                              periodic_discover_req_exit,
++                                              adapter, NULL);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_stop_periodic(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      int err;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
++                              "StopPeriodicDiscovery")) {
++              if (!dbus_message_has_signature(msg,
++                                      DBUS_TYPE_INVALID_AS_STRING))
++                      return invalid_args(msg);
++      }
++ 
++      if (!adapter->pdiscov_active)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".NotAuthorized",
++                              "Not authorized");
++      /*
++       * Cleanup the discovered devices list and send the cmd to exit
++       * from periodic inquiry mode or cancel remote name request.
++       */
++      err = cancel_periodic_discovery(adapter);
++      if (err < 0) {
++              if (err == -ENODEV)
++                      return no_such_adapter(msg);
++ 
++              else
++                      return failed_strerror(msg, -err);
++      }
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_is_periodic(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      struct adapter *adapter = data;
++      dbus_bool_t active = adapter->pdiscov_active;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &active,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_set_pdiscov_resolve(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      dbus_bool_t resolve;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_BOOLEAN, &resolve,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      debug("SetPeriodicDiscoveryNameResolving(%s)",
++                      resolve ? "TRUE" : "FALSE");
++ 
++      adapter->pdiscov_resolve_names = resolve;
++ 
++      if (adapter->pdiscov_active) {
++              if (resolve)
++                      adapter->discov_type |= RESOLVE_NAME;
++              else
++                      adapter->discov_type &= ~RESOLVE_NAME;
++      }
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_get_pdiscov_resolve(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      DBusMessage *reply;
++      struct adapter *adapter = data;
++      dbus_bool_t resolve = adapter->pdiscov_resolve_names;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &resolve,
++                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void discover_devices_req_exit(void *user_data)
++ {
++      struct adapter *adapter = user_data;
++ 
++      debug("DiscoverDevices requestor exited");
++ 
++      /* Cleanup the discovered devices list and send the command to cancel
++       * inquiry or cancel remote name request. The return can be ignored. */
++      cancel_discovery(adapter);
++ }
++ 
++ static DBusMessage *adapter_discover_devices(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      const char *method;
++      inquiry_cp cp;
++      evt_cmd_status rp;
++      struct hci_request rq;
++      struct adapter *adapter = data;
++      uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
++      int dd;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      if (adapter->discov_active)
++              return in_progress(msg, "Discover in progress");
++ 
++      pending_remote_name_cancel(adapter);
++ 
++      if (adapter->bonding)
++              return in_progress(msg, "Bonding in progress");
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return no_such_adapter(msg);
++ 
++      memset(&cp, 0, sizeof(cp));
++      memcpy(&cp.lap, lap, 3);
++      cp.length  = 0x08;
++      cp.num_rsp = 0x00;
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_INQUIRY;
++      rq.cparam = &cp;
++      rq.clen   = INQUIRY_CP_SIZE;
++      rq.rparam = &rp;
++      rq.rlen   = EVT_CMD_STATUS_SIZE;
++      rq.event  = EVT_CMD_STATUS;
++ 
++      if (hci_send_req(dd, &rq, 500) < 0) {
++              int err = errno;
++              error("Unable to start inquiry: %s (%d)",
++                              strerror(errno), errno);
++              hci_close_dev(dd);
++              return failed_strerror(msg, err);
++      }
++ 
++      if (rp.status) {
++              error("HCI_Inquiry command failed with status 0x%02x",
++                              rp.status);
++              hci_close_dev(dd);
++              return failed_strerror(msg, bt_error(rp.status));
++      }
++ 
++      method = dbus_message_get_member(msg);
++      if (strcmp("DiscoverDevicesWithoutNameResolving", method) == 0)
++              adapter->discov_type |= STD_INQUIRY;
++      else
++              adapter->discov_type |= (STD_INQUIRY | RESOLVE_NAME);
++ 
++      adapter->discov_requestor = g_strdup(dbus_message_get_sender(msg));
++ 
++ 
++      hci_close_dev(dd);
++ 
++      /* track the request owner to cancel it automatically if the owner
++       * exits */
++      adapter->discov_listener = g_dbus_add_disconnect_watch(conn,
++                                              dbus_message_get_sender(msg),
++                                              discover_devices_req_exit,
++                                              adapter, NULL);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_cancel_discovery(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      int err;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      /* is there discover pending? or discovery cancel was requested
++       * previously */
++      if (!adapter->discov_active || adapter->discovery_cancel)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".NotAuthorized",
++                              "Not Authorized");
++ 
++      /* only the discover requestor can cancel the inquiry process */
++      if (!adapter->discov_requestor ||
++                      strcmp(adapter->discov_requestor, dbus_message_get_sender(msg)))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".NotAuthorized",
++                              "Not Authorized");
++ 
++      /* Cleanup the discovered devices list and send the cmd to cancel
++       * inquiry or cancel remote name request */
++      err = cancel_discovery(adapter);
++      if (err < 0) {
++              if (err == -ENODEV)
++                      return no_such_adapter(msg);
++              else
++                      return failed_strerror(msg, -err);
++      }
++ 
++      /* Reply before send DiscoveryCompleted */
++      adapter->discovery_cancel = dbus_message_ref(msg);
++ 
++      return NULL;
++ }
++ 
++ struct remote_device_list_t {
++      GSList *list;
++      time_t time;
++ };
++ 
++ static void list_remote_devices_do_append(char *key, char *value, void *data)
++ {
++      struct remote_device_list_t *param = data;
++      char *address;
++      struct tm date;
++ 
++      if (g_slist_find_custom(param->list, key, (GCompareFunc) strcasecmp))
++              return;
++ 
++      if (param->time){
++              strptime(value, "%Y-%m-%d %H:%M:%S %Z", &date);
++              if (difftime(mktime(&date), param->time) < 0)
++                      return;
++      }
++ 
++      address = g_strdup(key);
++ 
++      param->list = g_slist_append(param->list, address);
++ }
++ 
++ static void remote_devices_do_append(void *data, void *user_data)
++ {
++      DBusMessageIter *iter = user_data;
++ 
++      dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data);
++ }
++ 
++ static DBusMessage *adapter_list_remote_devices(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      struct remote_device_list_t param = { NULL, 0 };
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      /* Add Bonded devices to the list */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "linkkeys");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      /* Add Trusted devices to the list */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      /* Add Last Used devices to the list */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      g_slist_foreach(param.list, remote_devices_do_append, &array_iter);
++ 
++      g_slist_foreach(param.list, (GFunc) free, NULL);
++      g_slist_free(param.list);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static void append_connected(struct active_conn_info *dev, GSList *list)
++ {
++      char address[18];
++ 
++      ba2str(&dev->bdaddr, address);
++      if (g_slist_find_custom(list, address, (GCompareFunc) strcasecmp))
++              return;
++ 
++      list = g_slist_append(list, g_strdup(address));
++ }
++ 
++ static DBusMessage *adapter_list_recent_remote_devices(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      struct tm date;
++      const char *string;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++      DBusMessage *reply;
++      char filename[PATH_MAX + 1];
++      struct remote_device_list_t param = { NULL, 0 };
++      int len;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &string,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      /* Date format is "YYYY-MM-DD HH:MM:SS GMT" */
++      len = strlen(string);
++      if (len && (strptime(string, "%Y-%m-%d %H:%M:%S", &date) == NULL))
++              return invalid_args(msg);
++ 
++      /* Bonded and trusted: mandatory entries(no matter the date/time) */
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "linkkeys");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      /* Last seen/used: append devices since the date informed */
++      if (len)
++              param.time = mktime(&date);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastseen");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
++      textfile_foreach(filename, list_remote_devices_do_append, &param);
++ 
++      /* connected: force appending connected devices, lastused might not match */
++      g_slist_foreach(adapter->active_conn, (GFunc) append_connected, param.list);
++ 
++      reply = dbus_message_new_method_return(msg);
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_STRING_AS_STRING, &array_iter);
++ 
++      g_slist_foreach(param.list, remote_devices_do_append, &array_iter);
++ 
++      g_slist_foreach(param.list, (GFunc) free, NULL);
++      g_slist_free(param.list);
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ 
++ static DBusMessage *adapter_set_trusted(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      bdaddr_t local;
++      const char *address;
++      char *old_path, *new_path;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &address,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &local);
++ 
++      write_trust(&local, address, GLOBAL_TRUST, TRUE);
++ 
++      resolve_paths(msg, &old_path, &new_path);
++ 
++      g_dbus_emit_signal(conn, old_path,
++                                      ADAPTER_INTERFACE, "TrustAdded",
++                                      DBUS_TYPE_STRING, &address,
++                                      DBUS_TYPE_INVALID);
++ 
++      if (new_path) {
++              struct device *device;
++              gboolean trust = TRUE;
++ 
++              device = adapter_find_device(adapter, address);
++              if (device) {
++                      dbus_connection_emit_property_changed(conn,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Trusted", DBUS_TYPE_BOOLEAN, &trust);
++              }
++      }
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_is_trusted(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      const char *address;
++      dbus_bool_t trusted;
++      bdaddr_t local;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &address,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &local);
++ 
++      trusted = read_trust(&local, address, GLOBAL_TRUST);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply,
++                              DBUS_TYPE_BOOLEAN, &trusted,
++                              DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *adapter_remove_trust(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *address;
++      bdaddr_t local;
++      char *old_path, *new_path;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &address,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &local);
++ 
++      write_trust(&local, address, GLOBAL_TRUST, FALSE);
++ 
++      resolve_paths(msg, &old_path, &new_path);
++ 
++      g_dbus_emit_signal(conn, old_path,
++                                      ADAPTER_INTERFACE, "TrustRemoved",
++                                      DBUS_TYPE_STRING, &address,
++                                      DBUS_TYPE_INVALID);
++ 
++      if (new_path) {
++              struct device *device;
++              gboolean trust = FALSE;
++ 
++              device = adapter_find_device(adapter, address);
++              if (device) {
++                      dbus_connection_emit_property_changed(conn,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Trusted", DBUS_TYPE_BOOLEAN, &trust);
++              }
++      }
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *adapter_list_trusts(DBusConnection *conn,
++                                              DBusMessage *msg,
++                                              void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      GSList *trusts, *l;
++      char **addrs;
++      bdaddr_t local;
++      int len;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      str2ba(adapter->address, &local);
++ 
++      trusts = list_trusts(&local, GLOBAL_TRUST);
++ 
++      addrs = g_new(char *, g_slist_length(trusts));
++ 
++      for (l = trusts, len = 0; l; l = l->next, len++)
++              addrs[len] = l->data;
++ 
++      dbus_message_append_args(reply,
++                      DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++                      &addrs, len,
++                      DBUS_TYPE_INVALID);
++ 
++      g_free(addrs);
++      g_slist_foreach(trusts, (GFunc) g_free, NULL);
++      g_slist_free(trusts);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *get_properties(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *property;
++      DBusMessage *reply;
++      DBusMessageIter iter;
++      DBusMessageIter dict;
++      bdaddr_t ba;
++      char str[249];
++ 
++      if (check_address(adapter->address) < 0)
++              return adapter_not_ready(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++ 
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
++                      DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
++                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
++ 
++      /* Address */
++      property = adapter->address;
++      dbus_message_iter_append_dict_entry(&dict, "Address",
++                      DBUS_TYPE_STRING, &property);
++ 
++      /* Name */
++      memset(str, 0, sizeof(str));
++      property = str;
++      str2ba(adapter->address, &ba);
++ 
++      if (!read_local_name(&ba, str))
++              dbus_message_iter_append_dict_entry(&dict, "Name",
++                      DBUS_TYPE_STRING, &property);
++ 
++      /* Mode */
++      property = mode2str(adapter->mode);
++ 
++      dbus_message_iter_append_dict_entry(&dict, "Mode",
++                      DBUS_TYPE_STRING, &property);
++ 
++      /* DiscoverableTimeout */
++      dbus_message_iter_append_dict_entry(&dict, "DiscoverableTimeout",
++                              DBUS_TYPE_UINT32, &adapter->discov_timeout);
++ 
++      /* PeriodicDiscovery */
++      dbus_message_iter_append_dict_entry(&dict, "PeriodicDiscovery",
++                              DBUS_TYPE_BOOLEAN, &adapter->pdiscov_active);
++ 
++      dbus_message_iter_close_container(&iter, &dict);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *set_property(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessageIter iter;
++      DBusMessageIter sub;
++      const char *property;
++ 
++      if (!dbus_message_iter_init(msg, &iter))
++              return invalid_args(msg);
++ 
++      if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
++              return invalid_args(msg);
++ 
++      dbus_message_iter_get_basic(&iter, &property);
++      dbus_message_iter_next(&iter);
++ 
++      if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
++              return invalid_args(msg);
++      dbus_message_iter_recurse(&iter, &sub);
++ 
++      if (g_str_equal("Name", property)) {
++              const char *name;
++ 
++              if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
++                      return invalid_args(msg);
++              dbus_message_iter_get_basic(&sub, &name);
++ 
++              return set_name(conn, msg, name, data);
++      } else if (g_str_equal("DiscoverableTimeout", property)) {
++              uint32_t timeout;
++ 
++              if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
++                      return invalid_args(msg);
++              dbus_message_iter_get_basic(&sub, &timeout);
++ 
++              return set_discoverable_timeout(conn, msg, timeout, data);
++      } else if (g_str_equal("PeriodicDiscovery", property)) {
++              dbus_bool_t value;
++ 
++              if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
++                      return invalid_args(msg);
++              dbus_message_iter_get_basic(&sub, &value);
++ 
++              if (value)
++                      return adapter_start_periodic(conn, msg, data);
++              else
++                      return adapter_stop_periodic(conn, msg, data);
++      } else if (g_str_equal("Mode", property)) {
++              const char *mode;
++ 
++              if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
++                      return invalid_args(msg);
++ 
++              dbus_message_iter_get_basic(&sub, &mode);
++ 
++              adapter->global_mode = str2mode(adapter->address, mode);
++ 
++              if (adapter->global_mode == adapter->mode)
++                      return dbus_message_new_method_return(msg);
++ 
++              if (adapter->sessions && adapter->global_mode < adapter->mode)
++                      return confirm_mode(conn, msg, mode, data);
++ 
++              return set_mode(conn, msg, str2mode(adapter->address, mode),
++                              data);
++      }
++ 
++      return invalid_args(msg);
++ }
++ 
++ static void session_exit(void *data)
++ {
++      struct mode_req *req = data;
++      struct adapter *adapter = req->adapter;
++ 
++      adapter->sessions = g_slist_remove(adapter->sessions, req);
++ 
++      if (!adapter->sessions) {
++              debug("Falling back to '%s' mode", mode2str(adapter->global_mode));
++              /* FIXME: fallback to previous mode
++              set_mode(req->conn, req->msg, adapter->global_mode, adapter);
++              */
++      }
++      dbus_connection_unref(req->conn);
++      dbus_message_unref(req->msg);
++      g_free(req);
++ }
++ 
++ static DBusMessage *request_mode(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const char *mode;
++      struct adapter *adapter = data;
++      struct mode_req *req;
++      uint8_t new_mode;
++      int ret;
++ 
++      if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &mode,
++                                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      new_mode = str2mode(adapter->address, mode);
++      if (new_mode != MODE_CONNECTABLE && new_mode != MODE_DISCOVERABLE)
++              return invalid_args(msg);
++ 
++      if (!adapter->agent)
++              return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
++                              "No agent registered");
++ 
++      if (g_slist_find_custom(adapter->sessions, msg,
++                      (GCompareFunc) find_session))
++              return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
++                              "Mode already requested");
++ 
++      req = g_new0(struct mode_req, 1);
++      req->adapter = adapter;
++      req->conn = dbus_connection_ref(conn);
++      req->msg = dbus_message_ref(msg);
++      req->mode = new_mode;
++      req->id = g_dbus_add_disconnect_watch(conn,
++                                      dbus_message_get_sender(msg),
++                                      session_exit, req, NULL);
++ 
++      if (!adapter->sessions)
++              adapter->global_mode = adapter->mode;
++      adapter->sessions = g_slist_append(adapter->sessions, req);
++ 
++      /* No need to change mode */
++      if (adapter->mode >= new_mode)
++              return dbus_message_new_method_return(msg);
++ 
++      ret = agent_confirm_mode_change(adapter->agent, mode, confirm_mode_cb,
++                                      req);
++      if (ret < 0) {
++              dbus_message_unref(req->msg);
++              g_dbus_remove_watch(req->conn, req->id);
++              dbus_connection_unref(req->conn);
++              g_free(req);
++              return invalid_args(msg);
++      }
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *release_mode(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      GSList *l;
++ 
++      l = g_slist_find_custom(adapter->sessions, msg,
++                      (GCompareFunc) find_session);
++      if (!l)
++              return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
++                              "No Mode to release");
++ 
++      session_exit(l->data);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *list_devices(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      GSList *l;
++      DBusMessageIter iter;
++      DBusMessageIter array_iter;
++ 
++      if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
++              return invalid_args(msg);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
++ 
++      for (l = adapter->devices; l; l = l->next) {
++              struct device *device = l->data;
++ 
++              if (device->temporary)
++                      continue;
++ 
++              dbus_message_iter_append_basic(&array_iter,
++                              DBUS_TYPE_OBJECT_PATH, &device->path);
++      }
++ 
++      dbus_message_iter_close_container(&iter, &array_iter);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *create_device(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      struct device *device;
++      const gchar *address;
++ 
++      if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
++                                              DBUS_TYPE_INVALID) == FALSE)
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      if (adapter_find_device(adapter, address))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Device already exists");
++ 
++      debug("create_device(%s)", address);
++ 
++      device = device_create(conn, adapter, address);
++      if (!device)
++              return NULL;
++ 
++      device->temporary = FALSE;
++ 
++      device_browse(device, conn, msg, NULL);
++ 
++      adapter->devices = g_slist_append(adapter->devices, device);
++ 
++      return NULL;
++ }
++ 
++ static uint8_t parse_io_capability(const char *capability)
++ {
++      if (g_str_equal(capability, ""))
++              return IO_CAPABILITY_DISPLAYYESNO;
++      if (g_str_equal(capability, "DisplayOnly"))
++              return IO_CAPABILITY_DISPLAYONLY;
++      if (g_str_equal(capability, "DisplayYesNo"))
++              return IO_CAPABILITY_DISPLAYYESNO;
++      if (g_str_equal(capability, "KeyboardOnly"))
++              return IO_CAPABILITY_KEYBOARDONLY;
++      if (g_str_equal(capability, "NoInputOutput"))
++              return IO_CAPABILITY_NOINPUTOUTPUT;
++      return IO_CAPABILITY_INVALID;
++ }
++ 
++ static DBusMessage *create_paired_device(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const gchar *address, *agent_path, *capability;
++      uint8_t cap;
++ 
++      if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
++                                      DBUS_TYPE_OBJECT_PATH, &agent_path,
++                                      DBUS_TYPE_STRING, &capability,
++                                              DBUS_TYPE_INVALID) == FALSE)
++              return invalid_args(msg);
++ 
++      if (check_address(address) < 0)
++              return invalid_args(msg);
++ 
++      cap = parse_io_capability(capability);
++      if (cap == IO_CAPABILITY_INVALID)
++              return invalid_args(msg);
++ 
++      return create_bonding(conn, msg, address, agent_path, cap, data);
++ }
++ 
++ static gint device_path_cmp(struct device *device, const gchar *path)
++ {
++      return strcasecmp(device->path, path);
++ }
++ 
++ static DBusMessage *remove_device(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      struct device *device;
++      const char *path;
++      GSList *l;
++ 
++      if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
++                                              DBUS_TYPE_INVALID) == FALSE)
++              return invalid_args(msg);
++ 
++      l = g_slist_find_custom(adapter->devices,
++                      path, (GCompareFunc) device_path_cmp);
++      if (!l)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Device does not exist");
++      device = l->data;
++ 
++      if (device->temporary || device->discov_active)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Device creation in progress");
++ 
++      adapter_remove_device(conn, adapter, device);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *find_device(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      struct device *device;
++      DBusMessage *reply;
++      const gchar *address;
++      GSList *l;
++ 
++      if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
++                                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      l = g_slist_find_custom(adapter->devices,
++                      address, (GCompareFunc) device_address_cmp);
++      if (!l)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Device does not exist");
++ 
++      device = l->data;
++ 
++      if (device->temporary)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Device creation in progress");
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply,
++                              DBUS_TYPE_OBJECT_PATH, &device->path,
++                              DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static void agent_removed(struct agent *agent, struct adapter *adapter)
++ {
++      struct pending_auth_info *auth;
++      GSList *l;
++ 
++      adapter->agent = NULL;
++ 
++      l = g_slist_find_custom(adapter->auth_reqs, agent,
++                                      auth_info_agent_cmp);
++      if (!l)
++              return;
++ 
++      auth = l->data;
++      auth->agent = NULL;
++ }
++ 
++ static DBusMessage *register_agent(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const char *path, *name, *capability;
++      struct agent *agent;
++      struct adapter *adapter = data;
++      uint8_t cap;
++ 
++      if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
++                      DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
++              return NULL;
++ 
++      if (adapter->agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Agent already exists");
++ 
++      cap = parse_io_capability(capability);
++      if (cap == IO_CAPABILITY_INVALID)
++              return invalid_args(msg);
++ 
++      name = dbus_message_get_sender(msg);
++ 
++      agent = agent_create(adapter, name, path, cap,
++                              (agent_remove_cb) agent_removed, adapter);
++      if (!agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".Failed",
++                              "Failed to create a new agent");
++ 
++      adapter->agent = agent;
++ 
++      debug("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
++                      path);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *unregister_agent(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      const char *path, *name;
++      struct adapter *adapter = data;
++ 
++      if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
++                                              DBUS_TYPE_INVALID))
++              return NULL;
++ 
++      name = dbus_message_get_sender(msg);
++ 
++      if (!adapter->agent || !agent_matches(adapter->agent, name, path))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "No such agent");
++ 
++      agent_destroy(adapter->agent, FALSE);
++      adapter->agent = NULL;
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *add_service_record(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      DBusMessage *reply;
++      const char *sender, *record;
++      dbus_uint32_t handle;
++      bdaddr_t src;
++      int err;
++ 
++      if (dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
++              return NULL;
++ 
++      sender = dbus_message_get_sender(msg);
++      str2ba(adapter->address, &src);
++      err = add_xml_record(conn, sender, &src, record, &handle);
++      if (err < 0)
++              return failed_strerror(msg, err);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply)
++              return NULL;
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
++                                                      DBUS_TYPE_INVALID);
++ 
++      return reply;
++ }
++ 
++ static DBusMessage *update_service_record(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      bdaddr_t src;
++ 
++      str2ba(adapter->address, &src);
++ 
++      return update_xml_record(conn, msg, &src);
++ }
++ 
++ static DBusMessage *remove_service_record(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      dbus_uint32_t handle;
++      const char *sender;
++ 
++      if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
++                                              DBUS_TYPE_INVALID) == FALSE)
++              return NULL;
++ 
++      sender = dbus_message_get_sender(msg);
++ 
++      if (remove_record(conn, sender, handle) < 0)
++              return not_available(msg);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *request_authorization(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      /* FIXME implement the request */
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *cancel_authorization(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      /* FIXME implement cancel request */
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ /* BlueZ 4.0 API */
++ static GDBusMethodTable adapter_methods[] = {
++      { "GetProperties",      "",     "a{sv}",get_properties          },
++      { "SetProperty",        "sv",   "",     set_property,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "RequestMode",        "s",    "",     request_mode,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "ReleaseMode",        "",     "",     release_mode            },
++      { "DiscoverDevices",    "",     "",     adapter_discover_devices},
++      { "CancelDiscovery",    "",     "",     adapter_cancel_discovery,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "ListDevices",        "",     "ao",   list_devices            },
++      { "CreateDevice",       "s",    "o",    create_device,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "CreatePairedDevice", "sos",  "o",    create_paired_device,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "RemoveDevice",       "o",    "",     remove_device           },
++      { "FindDevice",         "s",    "o",    find_device             },
++      { "RegisterAgent",      "os",   "",     register_agent          },
++      { "UnregisterAgent",    "o",    "",     unregister_agent        },
++      { "AddServiceRecord",   "s",    "u",    add_service_record      },
++      { "UpdateServiceRecord","us",   "",     update_service_record   },
++      { "RemoveServiceRecord","u",    "",     remove_service_record   },
++      { "RequestAuthorization","su",  "",     request_authorization,
++                                              G_DBUS_METHOD_FLAG_ASYNC},
++      { "CancelAuthorization","",     "",     cancel_authorization    },
++      { }
++ };
++ 
++ /* Deprecated */
++ static GDBusMethodTable old_adapter_methods[] = {
++      { "GetInfo",                    "",     "a{sv}",
++                                              adapter_get_info                },
++      { "GetAddress",                 "",     "s",
++                                              adapter_get_address             },
++      { "GetVersion",                 "",     "s",
++                                              adapter_get_version             },
++      { "GetRevision",                "",     "s",
++                                              adapter_get_revision            },
++      { "GetManufacturer",            "",     "s",
++                                              adapter_get_manufacturer        },
++      { "GetCompany",                 "",     "s",
++                                              adapter_get_company             },
++      { "ListAvailableModes",         "",     "as",
++                                              adapter_list_modes              },
++      { "GetMode",                    "",     "s",
++                                              adapter_get_mode                },
++      { "SetMode",                    "s",    "",
++                                              adapter_set_mode                },
++      { "GetDiscoverableTimeout",     "",     "u",
++                                              adapter_get_discoverable_to     },
++      { "SetDiscoverableTimeout",     "u",    "",
++                                              adapter_set_discoverable_to     },
++      { "IsConnectable",              "",     "b",
++                                              adapter_is_connectable          },
++      { "IsDiscoverable",             "",     "b",
++                                              adapter_is_discoverable         },
++      { "IsConnected",                "s",    "b",
++                                              adapter_is_connected            },
++      { "ListConnections",            "",     "as",
++                                              adapter_list_connections        },
++      { "GetMajorClass",              "",     "s",
++                                              adapter_get_major_class         },
++      { "ListAvailableMinorClasses",  "",     "as",
++                                              adapter_list_minor_classes      },
++      { "GetMinorClass",              "",     "s",
++                                              adapter_get_minor_class         },
++      { "SetMinorClass",              "s",    "",
++                                              adapter_set_minor_class         },
++      { "GetServiceClasses",          "",     "as",
++                                              adapter_get_service_classes     },
++      { "GetName",                    "",     "s",
++                                              adapter_get_name                },
++      { "SetName",                    "s",    "",
++                                              adapter_set_name                },
++ 
++      { "GetRemoteInfo",                      "s",    "a{sv}",
++                                              adapter_get_remote_info         },
++      { "GetRemoteServiceRecord",             "su",   "ay",
++              adapter_get_remote_svc,         G_DBUS_METHOD_FLAG_ASYNC        },
++      { "GetRemoteServiceRecordAsXML",        "su",   "s",
++              adapter_get_remote_svc_xml,     G_DBUS_METHOD_FLAG_ASYNC        },
++      { "GetRemoteServiceHandles",            "ss",   "au",
++              adapter_get_remote_svc_handles, G_DBUS_METHOD_FLAG_ASYNC        },
++      { "GetRemoteServiceIdentifiers",        "s",    "as",
++              adapter_get_remote_svc_identifiers, G_DBUS_METHOD_FLAG_ASYNC    },
+++     { "GetRemoteServiceChannel",            "sq",   "i",
+++             adapter_get_remote_svc_channel,         G_DBUS_METHOD_FLAG_ASYNC        },
++      { "FinishRemoteServiceTransaction",     "s",    "",
++                                              adapter_finish_sdp_transact     },
++      { "GetRemoteVersion",                   "s",    "s",
++                                              adapter_get_remote_version      },
++      { "GetRemoteRevision",                  "s",    "s",
++                                              adapter_get_remote_revision     },
++      { "GetRemoteManufacturer",              "s",    "s",
++                                              adapter_get_remote_manufacturer },
++      { "GetRemoteCompany",                   "s",    "s",
++                                              adapter_get_remote_company      },
++      { "GetRemoteMajorClass",                "s",    "s",
++                                              adapter_get_remote_major_class  },
++      { "GetRemoteMinorClass",                "s",    "s",
++                                              adapter_get_remote_minor_class  },
++      { "GetRemoteServiceClasses",            "s",    "as",
++                                              adapter_get_remote_service_cls  },
++      { "GetRemoteClass",                     "s",    "u",
++                                              adapter_get_remote_class        },
++      { "GetRemoteFeatures",                  "s",    "ay",
++                                              adapter_get_remote_features     },
++      { "GetRemoteName",                      "s",    "s",
++                                              adapter_get_remote_name         },
++      { "GetRemoteAlias",                     "s",    "s",
++                                              adapter_get_remote_alias        },
++      { "SetRemoteAlias",                     "ss",   "",
++                                              adapter_set_remote_alias        },
++      { "ClearRemoteAlias",                   "s",    "",
++                                              adapter_clear_remote_alias      },
++ 
++      { "LastSeen",           "s",    "s",
++                                              adapter_last_seen               },
++      { "LastUsed",           "s",    "s",
++                                              adapter_last_used               },
++ 
++      { "DisconnectRemoteDevice",     "s",    "",
++                      adapter_dc_remote_device,       G_DBUS_METHOD_FLAG_ASYNC},
++ 
++      { "CreateBonding",              "s",    "",
++                      adapter_create_bonding,         G_DBUS_METHOD_FLAG_ASYNC},
++      { "CancelBondingProcess",       "s",    "",
++                                              adapter_cancel_bonding          },
++      { "RemoveBonding",              "s",    "",
++                                              adapter_remove_bonding          },
++      { "HasBonding",                 "s",    "b",
++                                              adapter_has_bonding             },
++      { "ListBondings",               "",     "as",
++                                              adapter_list_bondings           },
++      { "GetPinCodeLength",           "s",    "y",
++                                              adapter_get_pin_code_length     },
++      { "GetEncryptionKeySize",       "s",    "y",
++                                              adapter_get_encryption_key_size },
++ 
++      { "StartPeriodicDiscovery",     "",     "",
++                                              adapter_start_periodic          },
++      { "StopPeriodicDiscovery",      "",     "",
++                                              adapter_stop_periodic           },
++      { "IsPeriodicDiscovery",        "",     "b",
++                                              adapter_is_periodic             },
++      { "SetPeriodicDiscoveryNameResolving",  "b",    "",
++                                              adapter_set_pdiscov_resolve     },
++      { "GetPeriodicDiscoveryNameResolving",  "",     "b",
++                                              adapter_get_pdiscov_resolve     },
++      { "DiscoverDevices",                    "",     "",
++                                              adapter_discover_devices        },
++      { "CancelDiscovery",                    "",     "",
++                      adapter_cancel_discovery, G_DBUS_METHOD_FLAG_ASYNC      },
++      { "DiscoverDevicesWithoutNameResolving","",     "",
++                                              adapter_discover_devices        },
++      { "ListRemoteDevices",                  "",     "as",
++                                              adapter_list_remote_devices     },
++      { "ListRecentRemoteDevices",            "s",    "as",
++                                              adapter_list_recent_remote_devices},
++ 
++      { "SetTrusted",         "s",    "",
++                                              adapter_set_trusted             },
++      { "IsTrusted",          "s",    "b",
++                                              adapter_is_trusted              },
++      { "RemoveTrust",        "s",    "",
++                                              adapter_remove_trust            },
++      { "ListTrusts",         "",     "as",
++                                              adapter_list_trusts             },
++ 
++      { }
++ };
++ 
++ /* BlueZ 4.X */
++ static GDBusSignalTable adapter_signals[] = {
++      { "DiscoveryStarted",           ""              },
++      { "DiscoveryCompleted",         ""              },
++      { "DeviceCreated",              "o"             },
++      { "DeviceRemoved",              "o"             },
++      { "DeviceFound",                "sa{sv}"        },
++      { "PropertyChanged",            "sv"            },
++      { "DeviceDisappeared",          "s"             },
++      { }
++ };
++ 
++ /* Deprecated */
++ static GDBusSignalTable old_adapter_signals[] = {
++      { "DiscoveryStarted",                   ""      },
++      { "DiscoveryCompleted",                 ""      },
++      { "ModeChanged",                        "s"     },
++      { "DiscoverableTimeoutChanged",         "u"     },
++      { "MinorClassChanged",                  "s"     },
++      { "NameChanged",                        "s"     },
++      { "PeriodicDiscoveryStarted",           ""      },
++      { "PeriodicDiscoveryStopped",           ""      },
++      { "RemoteDeviceFound",                  "sun"   },
++      { "RemoteDeviceDisappeared",            "s"     },
++      { "RemoteClassUpdated",                 "su"    },
++      { "RemoteNameUpdated",                  "ss"    },
++      { "RemoteNameFailed",                   "s"     },
++      { "RemoteNameRequested",                "s"     },
++      { "RemoteAliasChanged",                 "ss"    },
++      { "RemoteAliasCleared",                 "s"     },
++      { "RemoteDeviceConnected",              "s"     },
++      { "RemoteDeviceDisconnectRequested",    "s"     },
++      { "RemoteDeviceDisconnected",           "s"     },
++      { "RemoteIdentifiersUpdated",           "sas"   },
++      { "BondingCreated",                     "s"     },
++      { "BondingRemoved",                     "s"     },
++      { "TrustAdded",                         "s"     },
++      { "TrustRemoved",                       "s"     },
++      { }
++ };
++ 
++ dbus_bool_t adapter_init(DBusConnection *conn,
++              const char *path, struct adapter *adapter)
++ {
++      if (hcid_dbus_use_experimental())
++              g_dbus_register_interface(conn, path + ADAPTER_PATH_INDEX,
++                              ADAPTER_INTERFACE, adapter_methods,
++                              adapter_signals, NULL, adapter, NULL);
++ 
++      return g_dbus_register_interface(conn,
++                      path, ADAPTER_INTERFACE,
++                      old_adapter_methods, old_adapter_signals,
++                      NULL, adapter, NULL);
++ }
++ 
++ dbus_bool_t adapter_cleanup(DBusConnection *conn, const char *path)
++ {
++      return g_dbus_unregister_interface(conn, path, ADAPTER_INTERFACE);
++ }
++ 
++ const char *major_class_str(uint32_t class)
++ {
++      uint8_t index = (class >> 8) & 0x1F;
++ 
++      if (index > 8)
++              return major_cls[9]; /* uncategorized */
++ 
++      return major_cls[index];
++ }
++ 
++ const char *minor_class_str(uint32_t class)
++ {
++      uint8_t major_index = (class >> 8) & 0x1F;
++      uint8_t minor_index;
++ 
++      switch (major_index) {
++      case 1: /* computer */
++              minor_index = (class >> 2) & 0x3F;
++              if (minor_index < NUM_ELEMENTS(computer_minor_cls))
++                      return computer_minor_cls[minor_index];
++              else
++                      return "";
++      case 2: /* phone */
++              minor_index = (class >> 2) & 0x3F;
++              if (minor_index < NUM_ELEMENTS(phone_minor_cls))
++                      return phone_minor_cls[minor_index];
++              return "";
++      case 3: /* access point */
++              minor_index = (class >> 5) & 0x07;
++              if (minor_index < NUM_ELEMENTS(access_point_minor_cls))
++                      return access_point_minor_cls[minor_index];
++              else
++                      return "";
++      case 4: /* audio/video */
++              minor_index = (class >> 2) & 0x3F;
++              if (minor_index < NUM_ELEMENTS(audio_video_minor_cls))
++                      return audio_video_minor_cls[minor_index];
++              else
++                      return "";
++      case 5: /* peripheral */
++              minor_index = (class >> 6) & 0x03;
++              if (minor_index < NUM_ELEMENTS(peripheral_minor_cls))
++                      return peripheral_minor_cls[minor_index];
++              else
++                      return "";
++      case 6: /* imaging */
++              {
++                      uint8_t shift_minor = 0;
++ 
++                      minor_index = (class >> 4) & 0x0F;
++                      while (shift_minor < (sizeof(imaging_minor_cls) / sizeof(*imaging_minor_cls))) {
++                              if (((minor_index >> shift_minor) & 0x01) == 0x01)
++                                      return imaging_minor_cls[shift_minor];
++                              shift_minor++;
++                      }
++              }
++              break;
++      case 7: /* wearable */
++              minor_index = (class >> 2) & 0x3F;
++              if (minor_index < NUM_ELEMENTS(wearable_minor_cls))
++                      return wearable_minor_cls[minor_index];
++              else
++                      return "";
++      case 8: /* toy */
++              minor_index = (class >> 2) & 0x3F;
++              if (minor_index < NUM_ELEMENTS(toy_minor_cls))
++                      return toy_minor_cls[minor_index];
++              else
++                      return "";
++      }
++ 
++      return "";
++ }
++ 
++ GSList *service_classes_str(uint32_t class)
++ {
++      uint8_t services = class >> 16;
++      GSList *l = NULL;
++      int i;
++ 
++      for (i = 0; i < (sizeof(service_cls) / sizeof(*service_cls)); i++) {
++              if (!(services & (1 << i)))
++                      continue;
++ 
++              l = g_slist_append(l, (void *) service_cls[i]);
++      }
++ 
++      return l;
++ }
index 0000000,0000000,617ded2..f69ab29
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,2713 +1,2712 @@@@
  -
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2006-2007  Nokia Corporation
++  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #define _GNU_SOURCE
++ #include <stdio.h>
++ #include <errno.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <sys/param.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/sdp.h>
++ 
++ #include <glib.h>
++ #include <dbus/dbus.h>
++ #include <gdbus.h>
++ 
++ #include "hcid.h"
++ #include "textfile.h"
++ #include "manager.h"
++ #include "adapter.h"
++ #include "device.h"
++ #include "error.h"
++ #include "glib-helper.h"
++ #include "dbus-common.h"
++ #include "dbus-error.h"
++ #include "dbus-service.h"
++ #include "dbus-security.h"
++ #include "agent.h"
++ #include "dbus-hci.h"
++ 
++ static DBusConnection *connection = NULL;
++ 
++ void bonding_request_free(struct bonding_request_info *bonding)
++ {
++      struct device *device;
++      char address[18];
++ 
++      if (!bonding)
++              return;
++ 
++      if (bonding->msg)
++              dbus_message_unref(bonding->msg);
++ 
++      if (bonding->conn)
++              dbus_connection_unref(bonding->conn);
++ 
++      if (bonding->io)
++              g_io_channel_unref(bonding->io);
++ 
++      ba2str(&bonding->bdaddr, address);
++ 
++      device = adapter_find_device(bonding->adapter, address);
++      if (device && device->agent) {
++              agent_destroy(device->agent, FALSE);
++              device->agent = NULL;
++      }
++ 
++      g_free(bonding);
++ }
++ 
++ int found_device_cmp(const struct remote_dev_info *d1,
++                      const struct remote_dev_info *d2)
++ {
++      int ret;
++ 
++      if (bacmp(&d2->bdaddr, BDADDR_ANY)) {
++              ret = bacmp(&d1->bdaddr, &d2->bdaddr);
++              if (ret)
++                      return ret;
++      }
++ 
++      if (d2->name_status != NAME_ANY) {
++              ret = (d1->name_status - d2->name_status);
++              if (ret)
++                      return ret;
++      }
++ 
++      return 0;
++ }
++ 
++ int dev_rssi_cmp(struct remote_dev_info *d1, struct remote_dev_info *d2)
++ {
++      int rssi1, rssi2;
++ 
++      rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
++      rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
++ 
++      return rssi1 - rssi2;
++ }
++ 
++ int found_device_add(GSList **list, bdaddr_t *bdaddr, int8_t rssi,
++                      name_status_t name_status)
++ {
++      struct remote_dev_info *dev, match;
++      GSList *l;
++ 
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, bdaddr);
++      match.name_status = NAME_ANY;
++ 
++      /* ignore repeated entries */
++      l = g_slist_find_custom(*list, &match, (GCompareFunc) found_device_cmp);
++      if (l) {
++              /* device found, update the attributes */
++              dev = l->data;
++ 
++              if (rssi != 0)
++                      dev->rssi = rssi;
++ 
++               /* Get remote name can be received while inquiring.
++                * Keep in mind that multiple inquiry result events can
++                * be received from the same remote device.
++                */
++              if (name_status != NAME_NOT_REQUIRED)
++                      dev->name_status = name_status;
++ 
++              *list = g_slist_sort(*list, (GCompareFunc) dev_rssi_cmp);
++ 
++              return -EALREADY;
++      }
++ 
++      dev = g_new0(struct remote_dev_info, 1);
++ 
++      bacpy(&dev->bdaddr, bdaddr);
++      dev->rssi = rssi;
++      dev->name_status = name_status;
++ 
++      *list = g_slist_insert_sorted(*list, dev, (GCompareFunc) dev_rssi_cmp);
++ 
++      return 0;
++ }
++ 
++ static int found_device_remove(GSList **list, bdaddr_t *bdaddr)
++ {
++      struct remote_dev_info *dev, match;
++      GSList *l;
++ 
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, bdaddr);
++ 
++      l = g_slist_find_custom(*list, &match, (GCompareFunc) found_device_cmp);
++      if (!l)
++              return -1;
++ 
++      dev = l->data;
++      *list = g_slist_remove(*list, dev);
++      g_free(dev);
++ 
++      return 0;
++ }
++ 
++ int active_conn_find_by_bdaddr(const void *data, const void *user_data)
++ {
++      const struct active_conn_info *con = data;
++      const bdaddr_t *bdaddr = user_data;
++ 
++      return bacmp(&con->bdaddr, bdaddr);
++ }
++ 
++ static int active_conn_find_by_handle(const void *data, const void *user_data)
++ {
++      const struct active_conn_info *dev = data;
++      const uint16_t *handle = user_data;
++ 
++      if (dev->handle == *handle)
++              return 0;
++ 
++      return -1;
++ }
++ 
++ static int active_conn_append(GSList **list, bdaddr_t *bdaddr,
++                              uint16_t handle)
++ {
++      struct active_conn_info *dev;
++ 
++      dev = g_new0(struct active_conn_info, 1);
++ 
++      bacpy(&dev->bdaddr, bdaddr);
++      dev->handle = handle;
++ 
++      *list = g_slist_append(*list, dev);
++      return 0;
++ }
++ 
++ DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
++ {
++      switch (status) {
++      case 0x00: /* success */
++              return dbus_message_new_method_return(msg);
++ 
++      case 0x04: /* page timeout */
++      case 0x08: /* connection timeout */
++      case 0x10: /* connection accept timeout */
++      case 0x22: /* LMP response timeout */
++      case 0x28: /* instant passed - is this a timeout? */
++              return dbus_message_new_error(msg,
++                                      ERROR_INTERFACE ".AuthenticationTimeout",
++                                      "Authentication Timeout");
++      case 0x17: /* too frequent pairing attempts */
++              return dbus_message_new_error(msg,
++                                      ERROR_INTERFACE ".RepeatedAttempts",
++                                      "Repeated Attempts");
++ 
++      case 0x06:
++      case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */
++              return dbus_message_new_error(msg,
++                                      ERROR_INTERFACE ".AuthenticationRejected",
++                                      "Authentication Rejected");
++ 
++      case 0x07: /* memory capacity */
++      case 0x09: /* connection limit */
++      case 0x0a: /* synchronous connection limit */
++      case 0x0d: /* limited resources */
++      case 0x14: /* terminated due to low resources */
++              return dbus_message_new_error(msg,
++                                      ERROR_INTERFACE ".AuthenticationCanceled",
++                                      "Authentication Canceled");
++ 
++      case 0x05: /* authentication failure */
++      case 0x0E: /* rejected due to security reasons - is this auth failure? */
++      case 0x25: /* encryption mode not acceptable - is this auth failure? */
++      case 0x26: /* link key cannot be changed - is this auth failure? */
++      case 0x29: /* pairing with unit key unsupported - is this auth failure? */
++      case 0x2f: /* insufficient security - is this auth failure? */
++      default:
++              return dbus_message_new_error(msg,
++                                      ERROR_INTERFACE ".AuthenticationFailed",
++                                      "Authentication Failed");
++      }
++ }
++ 
++ static dbus_bool_t send_adapter_signal(DBusConnection *conn, int devid,
++                                      const char *name, int first, ...)
++ {
++      va_list var_args;
++      dbus_bool_t ret;
++      char path[MAX_PATH_LENGTH];
++ 
++      snprintf(path, sizeof(path)-1, "%s/hci%d", BASE_PATH, devid);
++ 
++      va_start(var_args, first);
++      ret = g_dbus_emit_signal_valist(conn, path, ADAPTER_INTERFACE,
++                                                      name, first, var_args);
++      va_end(var_args);
++ 
++      return ret;
++ }
++ 
++ static void adapter_mode_changed(struct adapter *adapter, uint8_t scan_enable)
++ {
++      const char *mode;
++ 
++      adapter->scan_enable = scan_enable;
++ 
++      switch (scan_enable) {
++      case SCAN_DISABLED:
++              mode = "off";
++              adapter->mode = MODE_OFF;
++              break;
++      case SCAN_PAGE:
++              mode = "connectable";
++              adapter->mode = MODE_CONNECTABLE;
++              break;
++      case (SCAN_PAGE | SCAN_INQUIRY):
++ 
++              if (adapter->discov_timeout != 0)
++                      adapter->timeout_id = g_timeout_add(adapter->discov_timeout * 1000,
++                                      discov_timeout_handler, adapter);
++ 
++              if (adapter->mode == MODE_LIMITED) {
++                      mode = "limited";
++              } else {
++                      adapter->mode = MODE_DISCOVERABLE;
++                      mode = "discoverable";
++              }
++              break;
++      case SCAN_INQUIRY:
++              /* Address the scenario where another app changed the scan mode */
++              if (adapter->discov_timeout != 0)
++                      adapter->timeout_id = g_timeout_add(adapter->discov_timeout * 1000,
++                                      discov_timeout_handler, adapter);
++              /* ignore, this event should not be sent*/
++      default:
++              /* ignore, reserved */
++              return;
++      }
++ 
++      g_dbus_emit_signal(connection, adapter->path, ADAPTER_INTERFACE,
++                                      "ModeChanged",
++                                      DBUS_TYPE_STRING, &mode,
++                                      DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental()) {
++              const char *ptr = adapter->path + ADAPTER_PATH_INDEX;
++              dbus_connection_emit_property_changed(connection, ptr,
++                                              ADAPTER_INTERFACE, "Mode",
++                                              DBUS_TYPE_STRING, &mode);
++      }
++ }
++ 
++ /*
++  * HCI D-Bus services
++  */
++ static void reply_pending_requests(const char *path, struct adapter *adapter)
++ {
++      if (!path || !adapter)
++              return;
++ 
++      /* pending bonding */
++      if (adapter->bonding) {
++              error_authentication_canceled(connection, adapter->bonding->msg);
++ 
++              remove_pending_device(adapter);
++ 
++              g_dbus_remove_watch(adapter->bonding->conn,
++                                      adapter->bonding->listener_id);
++ 
++              if (adapter->bonding->io_id)
++                      g_source_remove(adapter->bonding->io_id);
++              g_io_channel_close(adapter->bonding->io);
++              bonding_request_free(adapter->bonding);
++              adapter->bonding = NULL;
++      }
++ 
++      /* If there is a pending reply for discovery cancel */
++      if (adapter->discovery_cancel) {
++              DBusMessage *reply;
++              reply = dbus_message_new_method_return(adapter->discovery_cancel);
++              dbus_connection_send(connection, reply, NULL);
++              dbus_message_unref(reply);
++              dbus_message_unref(adapter->discovery_cancel);
++              adapter->discovery_cancel = NULL;
++      }
++ 
++      if (adapter->discov_active) {
++              /* Send discovery completed signal if there isn't name
++               * to resolve */
++              if (hcid_dbus_use_experimental()) {
++                      const char *ptr = path + ADAPTER_PATH_INDEX;
++ 
++                      g_dbus_emit_signal(connection, ptr,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++ 
++              }
++ 
++              g_dbus_emit_signal(connection, path,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++ 
++              /* Cancel inquiry initiated by D-Bus client */
++              if (adapter->discov_requestor)
++                      cancel_discovery(adapter);
++      }
++ 
++      if (adapter->pdiscov_active) {
++              /* Send periodic discovery stopped signal exit or stop
++               * the device */
++              g_dbus_emit_signal(connection, path,
++                                              ADAPTER_INTERFACE,
++                                              "PeriodicDiscoveryStopped",
++                                              DBUS_TYPE_INVALID);
++ 
++              /* Stop periodic inquiry initiated by D-Bus client */
++              if (adapter->pdiscov_requestor)
++                      cancel_periodic_discovery(adapter);
++      }
++ }
++ 
++ static void do_unregister(gpointer data, gpointer user_data)
++ {
++      DBusConnection *conn = user_data;
++      struct device *device = data;
++ 
++      device_remove(conn, device);
++ }
++ 
++ int unregister_adapter_path(const char *path)
++ {
++      struct adapter *adapter;
++ 
++      info("Unregister path: %s", path);
++ 
++      __remove_servers(path);
++ 
++      adapter = manager_find_adapter_by_path(path);
++      if (!adapter)
++              goto unreg;
++ 
++      /* check pending requests */
++      reply_pending_requests(path, adapter);
++ 
++      cancel_passkey_agent_requests(adapter->passkey_agents, path, NULL);
++ 
++      release_passkey_agents(adapter, NULL);
++ 
++      if (adapter->agent) {
++              agent_destroy(adapter->agent, FALSE);
++              adapter->agent = NULL;
++      }
++ 
++      if (adapter->discov_requestor) {
++              g_dbus_remove_watch(connection, adapter->discov_listener);
++              adapter->discov_listener = 0;
++              g_free(adapter->discov_requestor);
++              adapter->discov_requestor = NULL;
++      }
++ 
++      if (adapter->pdiscov_requestor) {
++              g_dbus_remove_watch(connection, adapter->pdiscov_listener);
++              adapter->pdiscov_listener = 0;
++              g_free(adapter->pdiscov_requestor);
++              adapter->pdiscov_requestor = NULL;
++      }
++ 
++      if (adapter->found_devices) {
++              g_slist_foreach(adapter->found_devices,
++                              (GFunc) g_free, NULL);
++              g_slist_free(adapter->found_devices);
++              adapter->found_devices = NULL;
++      }
++ 
++      if (adapter->oor_devices) {
++              g_slist_foreach(adapter->oor_devices,
++                              (GFunc) free, NULL);
++              g_slist_free(adapter->oor_devices);
++              adapter->oor_devices = NULL;
++      }
++ 
++      if (adapter->auth_reqs) {
++              g_slist_foreach(adapter->auth_reqs,
++                              (GFunc) g_free, NULL);
++              g_slist_free(adapter->auth_reqs);
++              adapter->auth_reqs = NULL;
++      }
++ 
++      if (adapter->active_conn) {
++              g_slist_foreach(adapter->active_conn,
++                              (GFunc) free, NULL);
++              g_slist_free(adapter->active_conn);
++              adapter->active_conn = NULL;
++      }
++ 
++      /* Check if there is a pending RemoteDeviceDisconnect request */
++      if (adapter->pending_dc) {
++              error_no_such_adapter(adapter->pending_dc->conn,
++                                    adapter->pending_dc->msg);
++              g_source_remove(adapter->pending_dc->timeout_id);
++              dc_pending_timeout_cleanup(adapter);
++      }
++ 
++      if (adapter->devices) {
++              g_slist_foreach(adapter->devices, do_unregister,
++                                                      connection);
++              g_slist_free(adapter->devices);
++      }
++ 
++      manager_remove_adapter(adapter);
++ 
++      g_free(adapter->path);
++      g_free(adapter);
++ 
++ unreg:
++      if (!adapter_cleanup(connection, path)) {
++              error("Failed to unregister adapter interface on %s object",
++                      path);
++              return -1;
++      }
++ 
++      if (!security_cleanup(connection, path)) {
++              error("Failed to unregister security interface on %s object",
++                      path);
++              return -1;
++      }
++ 
++      if (hcid_dbus_use_experimental()) {
++              const char *ptr = path + ADAPTER_PATH_INDEX;
++ 
++              adapter_cleanup(connection, ptr);
++      }
++ 
++      return 0;
++ }
++ 
++ /*****************************************************************
++  *
++  *  Section reserved to HCI commands confirmation handling and low
++  *  level events(eg: device attached/dettached.
++  *
++  *****************************************************************/
++ 
++ int hcid_dbus_register_device(uint16_t id)
++ {
++      char path[MAX_PATH_LENGTH];
++      char *ptr = path + ADAPTER_PATH_INDEX;
++      struct adapter *adapter;
++ 
++      snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
++ 
++      adapter = g_try_new0(struct adapter, 1);
++      if (!adapter) {
++              error("Failed to alloc memory to D-Bus path register data (%s)",
++                              path);
++              return -1;
++      }
++ 
++      adapter->dev_id = id;
++      adapter->pdiscov_resolve_names = 1;
++ 
++      if (!adapter_init(connection, path, adapter)) {
++              error("Adapter interface init failed on path %s", path);
++              g_free(adapter);
++              return -1;
++      }
++ 
++      adapter->path = g_strdup(path);
++ 
++      if (!security_init(connection, path)) {
++              error("Security interface init failed on path %s", path);
++              goto failed;
++      }
++ 
++      __probe_servers(path);
++ 
++      manager_add_adapter(adapter);
++ 
++      return 0;
++ 
++ failed:
++      if (hcid_dbus_use_experimental())
++              g_dbus_unregister_interface(connection, ptr, ADAPTER_INTERFACE);
++ 
++      g_dbus_unregister_interface(connection, path, ADAPTER_INTERFACE);
++ 
++      g_free(adapter->path);
++      g_free(adapter);
++ 
++      return -1;
++ }
++ 
++ int hcid_dbus_unregister_device(uint16_t id)
++ {
++      char path[MAX_PATH_LENGTH];
++ 
++      snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
++ 
++      return unregister_adapter_path(path);
++ }
++ 
++ static void create_stored_device_from_profiles(char *key, char *value,
++                                              void *user_data)
++ {
++      struct adapter *adapter = user_data;
++      GSList *uuids = bt_string2list(value);
++      struct device *device;
++ 
++      device = device_create(connection, adapter, key);
++      if (device) {
++              device->temporary = FALSE;
++              adapter->devices = g_slist_append(adapter->devices, device);
++              device_probe_drivers(device, uuids);
++              g_slist_free(uuids);
++      }
++ }
++ 
++ static void create_stored_device_from_linkkeys(char *key, char *value,
++                                              void *user_data)
++ {
++      struct adapter *adapter = user_data;
++      struct device *device;
++ 
++      if (g_slist_find_custom(adapter->devices,
++                              key, (GCompareFunc) device_address_cmp))
++              return;
++ 
++      device = device_create(connection, adapter, key);
++      if (device) {
++              device->temporary = FALSE;
++              adapter->devices = g_slist_append(adapter->devices, device);
++      }
++ }
++ 
++ static void register_devices(bdaddr_t *src, struct adapter *adapter)
++ {
++      char filename[PATH_MAX + 1];
++      char addr[18];
++ 
++      ba2str(src, addr);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, addr, "profiles");
++      textfile_foreach(filename, create_stored_device_from_profiles, adapter);
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, addr, "linkkeys");
++      textfile_foreach(filename, create_stored_device_from_linkkeys, adapter);
++ }
++ 
++ int hcid_dbus_start_device(uint16_t id)
++ {
++      char path[MAX_PATH_LENGTH];
++      char *ptr = path + ADAPTER_PATH_INDEX;
++      struct hci_dev_info di;
++      struct adapter* adapter;
++      struct hci_conn_list_req *cl = NULL;
++      struct hci_conn_info *ci;
++      const char *mode;
++      int i, err, dd = -1, ret = -1;
++ 
++      snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
++ 
++      if (hci_devinfo(id, &di) < 0) {
++              error("Getting device info failed: hci%d", id);
++              return -1;
++      }
++ 
++      if (hci_test_bit(HCI_RAW, &di.flags))
++              return -1;
++ 
++      adapter = manager_find_adapter_by_path(path);
++      if (!adapter) {
++              error("Getting %s path data failed!", path);
++              return -1;
++      }
++ 
++      if (hci_test_bit(HCI_INQUIRY, &di.flags))
++              adapter->discov_active = 1;
++      else
++              adapter->discov_active = 0;
++ 
++      adapter->up = 1;
++      adapter->discov_timeout = get_discoverable_timeout(id);
++      adapter->discov_type = DISCOVER_TYPE_NONE;
++ 
++      dd = hci_open_dev(id);
++      if (dd < 0)
++              goto failed;
++ 
++      adapter->scan_enable = get_startup_scan(id);
++      hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE,
++                                      1, &adapter->scan_enable);
++      /*
++       * Get the adapter Bluetooth address
++       */
++      err = get_device_address(adapter->dev_id, adapter->address,
++                                      sizeof(adapter->address));
++      if (err < 0)
++              goto failed;
++ 
++      err = get_device_class(adapter->dev_id, adapter->class);
++      if (err < 0)
++              goto failed;
++ 
++      adapter->mode = get_startup_mode(id);
++      if (adapter->mode == MODE_LIMITED)
++              set_limited_discoverable(dd, adapter->class, TRUE);
++ 
++      /*
++       * retrieve the active connections: address the scenario where
++       * the are active connections before the daemon've started
++       */
++ 
++      cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
++ 
++      cl->dev_id = id;
++      cl->conn_num = 10;
++      ci = cl->conn_info;
++ 
++      if (ioctl(dd, HCIGETCONNLIST, cl) < 0)
++              goto failed;
++ 
++      for (i = 0; i < cl->conn_num; i++, ci++)
++              active_conn_append(&adapter->active_conn,
++                                      &ci->bdaddr, ci->handle);
++ 
++      ret = 0;
++ 
++      mode = mode2str(adapter->mode);
++      g_dbus_emit_signal(connection, path, ADAPTER_INTERFACE,
++                                      "ModeChanged",
++                                      DBUS_TYPE_STRING, &mode,
++                                      DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental()) {
++              dbus_connection_emit_property_changed(connection, ptr,
++                                              ADAPTER_INTERFACE, "Mode",
++                                              DBUS_TYPE_STRING, &mode);
++      }
++ 
++      if (manager_get_default_adapter() < 0)
++              manager_set_default_adapter(id);
++ 
++      if (hcid_dbus_use_experimental())
++              register_devices(&di.bdaddr, adapter);
++ 
++ failed:
++      if (dd >= 0)
++              hci_close_dev(dd);
++ 
++      g_free(cl);
++ 
++      return ret;
++ }
++ 
++ static void send_dc_signal(struct active_conn_info *dev, const char *path)
++ {
++      char addr[18];
++      const char *paddr = addr;
++ 
++      ba2str(&dev->bdaddr, addr);
++ 
++      g_dbus_emit_signal(connection, path, ADAPTER_INTERFACE,
++                                      "RemoteDeviceDisconnected",
++                                      DBUS_TYPE_STRING, &paddr,
++                                      DBUS_TYPE_INVALID);
++ }
++ 
++ int hcid_dbus_stop_device(uint16_t id)
++ {
++      char path[MAX_PATH_LENGTH];
++      struct adapter *adapter;
++      const char *mode = "off";
++ 
++      snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
++ 
++      adapter = manager_find_adapter_by_path(path);
++      if (!adapter) {
++              error("Getting %s path data failed!", path);
++              return -1;
++      }
++ 
++      /* cancel pending timeout */
++      if (adapter->timeout_id) {
++              g_source_remove(adapter->timeout_id);
++              adapter->timeout_id = 0;
++      }
++ 
++      /* check pending requests */
++      reply_pending_requests(path, adapter);
++ 
++      cancel_passkey_agent_requests(adapter->passkey_agents, path, NULL);
++ 
++      release_passkey_agents(adapter, NULL);
++ 
++      if (adapter->discov_requestor) {
++              g_dbus_remove_watch(connection, adapter->discov_listener);
++              adapter->discov_listener = 0;
++              g_free(adapter->discov_requestor);
++              adapter->discov_requestor = NULL;
++      }
++ 
++      if (adapter->pdiscov_requestor) {
++              g_dbus_remove_watch(connection, adapter->pdiscov_listener);
++              adapter->pdiscov_listener = 0;
++              g_free(adapter->pdiscov_requestor);
++              adapter->pdiscov_requestor = NULL;
++      }
++ 
++      if (adapter->found_devices) {
++              g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++              g_slist_free(adapter->found_devices);
++              adapter->found_devices = NULL;
++      }
++ 
++      if (adapter->oor_devices) {
++              g_slist_foreach(adapter->oor_devices, (GFunc) free, NULL);
++              g_slist_free(adapter->oor_devices);
++              adapter->oor_devices = NULL;
++      }
++ 
++      if (adapter->auth_reqs) {
++              g_slist_foreach(adapter->auth_reqs, (GFunc) g_free, NULL);
++              g_slist_free(adapter->auth_reqs);
++              adapter->auth_reqs = NULL;
++      }
++ 
++      if (adapter->active_conn) {
++              g_slist_foreach(adapter->active_conn, (GFunc) send_dc_signal, path);
++              g_slist_foreach(adapter->active_conn, (GFunc) g_free, NULL);
++              g_slist_free(adapter->active_conn);
++              adapter->active_conn = NULL;
++      }
++ 
++      send_adapter_signal(connection, adapter->dev_id, "ModeChanged",
++                              DBUS_TYPE_STRING, &mode,
++                              DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental()) {
++              const char *ptr = path + ADAPTER_PATH_INDEX;
++              dbus_connection_emit_property_changed(connection, ptr,
++                                              ADAPTER_INTERFACE, "Mode",
++                                              DBUS_TYPE_STRING, &mode);
++      }
++ 
++      adapter->up = 0;
++      adapter->scan_enable = SCAN_DISABLED;
++      adapter->mode = MODE_OFF;
++      adapter->discov_active = 0;
++      adapter->pdiscov_active = 0;
++      adapter->pinq_idle = 0;
++      adapter->discov_type = DISCOVER_TYPE_NONE;
++ 
++      return 0;
++ }
++ 
++ static void pincode_cb(struct agent *agent, DBusError *err, const char *pincode,
++                      struct device *device)
++ {
++      struct adapter *adapter = device->adapter;
++      pin_code_reply_cp pr;
++      bdaddr_t sba, dba;
++      size_t len;
++      int dev;
++      struct pending_auth_info *auth;
++ 
++      /* No need to reply anything if the authentication already failed */
++      if (adapter->bonding && adapter->bonding->hci_status)
++              return;
++ 
++      dev = hci_open_dev(adapter->dev_id);
++      if (dev < 0) {
++              error("hci_open_dev(%d): %s (%d)", adapter->dev_id,
++                              strerror(errno), errno);
++              return;
++      }
++ 
++      str2ba(adapter->address, &sba);
++      str2ba(device->address, &dba);
++ 
++      auth = adapter_find_auth_request(adapter, &dba);
++ 
++      if (err) {
++              hci_send_cmd(dev, OGF_LINK_CTL,
++                              OCF_PIN_CODE_NEG_REPLY, 6, &dba);
++              goto done;
++      }
++ 
++      len = strlen(pincode);
++ 
++      set_pin_length(&sba, len);
++ 
++      memset(&pr, 0, sizeof(pr));
++      bacpy(&pr.bdaddr, &dba);
++      memcpy(pr.pin_code, pincode, len);
++      pr.pin_len = len;
++      hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY, PIN_CODE_REPLY_CP_SIZE, &pr);
++ 
++ done:
++      if (auth) {
++              auth->replied = TRUE;
++              auth->agent = NULL;
++      }
++      hci_close_dev(dev);
++ }
++ 
++ int hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci)
++ {
++      char addr[18];
++      struct adapter *adapter;
++      struct device *device;
++      struct agent *agent;
++      int ret;
++ 
++      adapter = manager_find_adapter(sba);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      if (!hcid_dbus_use_experimental())
++              goto old_fallback;
++ 
++      ba2str(&ci->bdaddr, addr);
++ 
++      device = adapter_find_device(adapter, addr);
++      agent = device && device->agent ? device->agent : adapter->agent;
++      if (!agent)
++              goto old_fallback;
++ 
++      if (!device) {
++              device = adapter_create_device(connection, adapter, addr);
++              if (!device)
++                      return -ENODEV;
++      }
++ 
++      ret = agent_request_pincode(agent, device,
++                                      (agent_pincode_cb) pincode_cb,
++                                      device);
++      if (ret == 0) {
++              struct pending_auth_info *auth;
++              auth = adapter_new_auth_request(adapter, &ci->bdaddr,
++                                              AUTH_TYPE_PINCODE);
++              auth->agent = agent;
++      }
++ 
++ 
++      return ret;
++ 
++ old_fallback:
++      ret = handle_passkey_request_old(connection, dev, adapter, sba,
++                                              &ci->bdaddr);
++      if (ret == 0)
++              adapter_new_auth_request(adapter, &ci->bdaddr,
++                                              AUTH_TYPE_PINCODE);
++      return ret;
++ }
++ 
++ static void confirm_cb(struct agent *agent, DBusError *err, void *user_data)
++ {
++      struct device *device = user_data;
++      struct adapter *adapter = device->adapter;
++      user_confirm_reply_cp cp;
++      int dd;
++      struct pending_auth_info *auth;
++ 
++      /* No need to reply anything if the authentication already failed */
++      if (adapter->bonding && adapter->bonding->hci_status)
++              return;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error("Unable to open hci%d", adapter->dev_id);
++              return;
++      }
++ 
++      memset(&cp, 0, sizeof(cp));
++      str2ba(device->address, &cp.bdaddr);
++ 
++      auth = adapter_find_auth_request(adapter, &cp.bdaddr);
++ 
++      if (err)
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
++                                      USER_CONFIRM_REPLY_CP_SIZE, &cp);
++      else
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_REPLY,
++                                      USER_CONFIRM_REPLY_CP_SIZE, &cp);
++ 
++      if (auth) {
++              auth->replied = TRUE;
++              auth->agent = FALSE;
++      }
++ 
++      hci_close_dev(dd);
++ }
++ 
++ static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey,
++                      void *user_data)
++ {
++      struct device *device = user_data;
++      struct adapter *adapter = device->adapter;
++      user_passkey_reply_cp cp;
++      bdaddr_t dba;
++      int dd;
++      struct pending_auth_info *auth;
++ 
++      /* No need to reply anything if the authentication already failed */
++      if (adapter->bonding && adapter->bonding->hci_status)
++              return;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error("Unable to open hci%d", adapter->dev_id);
++              return;
++      }
++ 
++      str2ba(device->address, &dba);
++ 
++      memset(&cp, 0, sizeof(cp));
++      bacpy(&cp.bdaddr, &dba);
++      cp.passkey = passkey;
++ 
++      auth = adapter_find_auth_request(adapter, &dba);
++ 
++      if (err)
++              hci_send_cmd(dd, OGF_LINK_CTL,
++                              OCF_USER_PASSKEY_NEG_REPLY, 6, &dba);
++      else
++              hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_PASSKEY_REPLY,
++                                      USER_PASSKEY_REPLY_CP_SIZE, &cp);
++ 
++      if (auth) {
++              auth->replied = TRUE;
++              auth->agent = NULL;
++      }
++ 
++      hci_close_dev(dd);
++ }
++ 
++ static int get_auth_requirements(bdaddr_t *local, bdaddr_t *remote,
++                                                      uint8_t *auth)
++ {
++      struct hci_auth_info_req req;
++      char addr[18];
++      int err, dd, dev_id;
++ 
++      ba2str(local, addr);
++ 
++      dev_id = hci_devid(addr);
++      if (dev_id < 0)
++              return dev_id;
++ 
++      dd = hci_open_dev(dev_id);
++      if (dd < 0)
++              return dd;
++ 
++      memset(&req, 0, sizeof(req));
++      bacpy(&req.bdaddr, remote);
++ 
++      err = ioctl(dd, HCIGETAUTHINFO, (unsigned long) &req);
++      if (err < 0) {
++              debug("HCIGETAUTHINFO failed: %s (%d)",
++                                      strerror(errno), errno);
++              hci_close_dev(dd);
++              return err;
++      }
++ 
++      hci_close_dev(dd);
++ 
++      if (auth)
++              *auth = req.type;
++ 
++      return 0;
++ }
++ 
++ int hcid_dbus_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
++ {
++      struct adapter *adapter;
++      struct device *device;
++      struct agent *agent;
++      char addr[18];
++      uint8_t type;
++      struct pending_auth_info *auth;
++ 
++      adapter = manager_find_adapter(sba);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      if (get_auth_requirements(sba, dba, &type) < 0) {
++              int dd;
++ 
++              dd = hci_open_dev(adapter->dev_id);
++              if (dd < 0) {
++                      error("Unable to open hci%d", adapter->dev_id);
++                      return -1;
++              }
++ 
++              hci_send_cmd(dd, OGF_LINK_CTL,
++                                      OCF_USER_CONFIRM_NEG_REPLY, 6, dba);
++ 
++              hci_close_dev(dd);
++ 
++              return 0;
++      }
++ 
++      ba2str(dba, addr);
++ 
++      device = adapter_get_device(connection, adapter, addr);
++      if (!device) {
++              error("Device creation failed");
++              return -1;
++      }
++ 
++      /* If no MITM protection required, auto-accept */
++      if (!(device->auth & 0x01) && !(type & 0x01)) {
++              int dd;
++ 
++              dd = hci_open_dev(adapter->dev_id);
++              if (dd < 0) {
++                      error("Unable to open hci%d", adapter->dev_id);
++                      return -1;
++              }
++ 
++              hci_send_cmd(dd, OGF_LINK_CTL,
++                                      OCF_USER_CONFIRM_REPLY, 6, dba);
++ 
++              hci_close_dev(dd);
++ 
++              return 0;
++      }
++ 
++      if (device->agent)
++              agent = device->agent;
++      else
++              agent = adapter->agent;
++ 
++      if (!agent) {
++              error("No agent available for user confirm request");
++              return -1;
++      }
++ 
++      if (agent_request_confirmation(agent, device, passkey,
++                                              confirm_cb, device) < 0) {
++              error("Requesting passkey failed");
++              return -1;
++      }
++ 
++      auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_CONFIRM);
++      auth->agent = agent;
++ 
++      return 0;
++ }
++ 
++ int hcid_dbus_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
++ {
++      struct adapter *adapter;
++      struct device *device;
++      struct agent *agent;
++      char addr[18];
++      struct pending_auth_info *auth;
++ 
++      adapter = manager_find_adapter(sba);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      ba2str(dba, addr);
++ 
++      device = adapter_get_device(connection, adapter, addr);
++      if (device && device->agent)
++              agent = device->agent;
++      else
++              agent = adapter->agent;
++ 
++      if (!agent) {
++              error("No agent available for user confirm request");
++              return -1;
++      }
++ 
++      if (agent_request_passkey(agent, device, passkey_cb, device) < 0) {
++              error("Requesting passkey failed");
++              return -1;
++      }
++ 
++      auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_PASSKEY);
++      auth->agent = agent;
++ 
++      return 0;
++ }
++ 
++ int hcid_dbus_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
++ {
++      struct adapter *adapter;
++      struct device *device;
++      struct agent *agent;
++      char addr[18];
++      struct pending_auth_info *auth;
++ 
++      adapter = manager_find_adapter(sba);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      ba2str(dba, addr);
++ 
++      device = adapter_get_device(connection, adapter, addr);
++      if (device && device->agent)
++              agent = device->agent;
++      else
++              agent = adapter->agent;
++ 
++      if (!agent) {
++              error("No agent available for user confirm request");
++              return -1;
++      }
++ 
++      if (agent_display_passkey(agent, device, passkey) < 0) {
++              error("Displaying passkey failed");
++              return -1;
++      }
++ 
++      auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_NOTIFY);
++      auth->agent = agent;
++ 
++      return 0;
++ }
++ 
++ void hcid_dbus_bonding_process_complete(bdaddr_t *local, bdaddr_t *peer,
++                                      uint8_t status)
++ {
++      struct adapter *adapter;
++      char peer_addr[18];
++      const char *paddr = peer_addr;
++      DBusMessage *reply;
++      struct device *device;
++      struct bonding_request_info *bonding;
++      gboolean paired = TRUE;
++      struct pending_auth_info *auth;
++ 
++      debug("hcid_dbus_bonding_process_complete: status=%02x", status);
++ 
++      ba2str(peer, peer_addr);
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("Unable to find matching adapter");
++              return;
++      }
++ 
++      if (status) {
++              if (adapter->bonding)
++                      adapter->bonding->hci_status = status;
++              cancel_passkey_agent_requests(adapter->passkey_agents,
++                                              adapter->path, peer);
++      }
++ 
++      auth = adapter_find_auth_request(adapter, peer);
++      if (!auth) {
++              debug("hcid_dbus_bonding_process_complete: no pending auth request");
++              goto proceed;
++      }
++ 
++      if (auth->agent)
++              agent_cancel(auth->agent);
++ 
++      adapter_remove_auth_request(adapter, peer);
++ 
++      if (status)
++              goto proceed;
++ 
++      send_adapter_signal(connection, adapter->dev_id, "BondingCreated",
++                              DBUS_TYPE_STRING, &paddr, DBUS_TYPE_INVALID);
++ 
++      device = adapter_get_device(connection, adapter, paddr);
++      if (device) {
++              char *ptr = adapter->path + ADAPTER_PATH_INDEX;
++ 
++              debug("hcid_dbus_bonding_process_complete: removing temporary flag");
++ 
++              device->temporary = FALSE;
++ 
++              g_dbus_emit_signal(connection, ptr,
++                                      ADAPTER_INTERFACE,
++                                      "DeviceCreated",
++                                      DBUS_TYPE_OBJECT_PATH,
++                                      &device->path,
++                                      DBUS_TYPE_INVALID);
++ 
++              dbus_connection_emit_property_changed(connection, device->path,
++                                      DEVICE_INTERFACE, "Paired",
++                                      DBUS_TYPE_BOOLEAN, &paired);
++      }
++ 
++ proceed:
++ 
++      release_passkey_agents(adapter, peer);
++ 
++      bonding = adapter->bonding;
++      if (!bonding || bacmp(&bonding->bdaddr, peer))
++              return; /* skip: no bonding req pending */
++ 
++      if (bonding->cancel) {
++              /* reply authentication canceled */
++              error_authentication_canceled(connection, bonding->msg);
++              goto cleanup;
++      }
++ 
++      /* reply authentication success or an error */
++      if (dbus_message_is_method_call(bonding->msg, ADAPTER_INTERFACE,
++                                      "CreateBonding")) {
++              reply = new_authentication_return(bonding->msg, status);
++              dbus_connection_send(connection, reply, NULL);
++              dbus_message_unref(reply);
++      } else if ((device = adapter_find_device(adapter, paddr))) {
++              if (status) {
++                      reply = new_authentication_return(bonding->msg, status);
++                      dbus_connection_send(connection, reply, NULL);
++                      dbus_message_unref(reply);
++              } else {
++                      device->temporary = FALSE;
++                      device_browse(device, bonding->conn,
++                                      bonding->msg, NULL);
++              }
++      }
++ 
++ cleanup:
++      g_dbus_remove_watch(connection, adapter->bonding->listener_id);
++ 
++      if (adapter->bonding->io_id)
++              g_source_remove(adapter->bonding->io_id);
++      g_io_channel_close(adapter->bonding->io);
++      bonding_request_free(adapter->bonding);
++      adapter->bonding = NULL;
++ }
++ 
++ void hcid_dbus_inquiry_start(bdaddr_t *local)
++ {
++      struct adapter *adapter;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("Unable to find matching adapter");
++              return;
++      }
++ 
++      adapter->discov_active = 1;
++      /*
++       * Cancel pending remote name request and clean the device list
++       * when inquiry is supported in periodic inquiry idle state.
++       */
++      if (adapter->pdiscov_active)
++              pending_remote_name_cancel(adapter);
++ 
++      /* Disable name resolution for non D-Bus clients */
++      if (!adapter->discov_requestor)
++              adapter->discov_type &= ~RESOLVE_NAME;
++ 
++      if (hcid_dbus_use_experimental())
++              dbus_connection_emit_property_changed(connection,
++                              adapter->path + ADAPTER_PATH_INDEX,
++                              ADAPTER_INTERFACE, "PeriodicDiscovery",
++                              DBUS_TYPE_BOOLEAN, &adapter->discov_active);
++ 
++      send_adapter_signal(connection, adapter->dev_id, "DiscoveryStarted",
++                              DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental())
++              g_dbus_emit_signal(connection,
++                                              adapter->path + ADAPTER_PATH_INDEX,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryStarted",
++                                              DBUS_TYPE_INVALID);
++ }
++ 
++ int found_device_req_name(struct adapter *adapter)
++ {
++      struct hci_request rq;
++      evt_cmd_status rp;
++      remote_name_req_cp cp;
++      struct remote_dev_info match;
++      GSList *l;
++      int dd, req_sent = 0;
++ 
++      /* get the next remote address */
++      if (!adapter->found_devices)
++              return -ENODATA;
++ 
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, BDADDR_ANY);
++      match.name_status = NAME_REQUIRED;
++ 
++      l = g_slist_find_custom(adapter->found_devices, &match,
++                                      (GCompareFunc) found_device_cmp);
++      if (!l)
++              return -ENODATA;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0)
++              return -errno;
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_REMOTE_NAME_REQ;
++      rq.cparam = &cp;
++      rq.clen   = REMOTE_NAME_REQ_CP_SIZE;
++      rq.rparam = &rp;
++      rq.rlen   = EVT_CMD_STATUS_SIZE;
++      rq.event  = EVT_CMD_STATUS;
++ 
++      /* send at least one request or return failed if the list is empty */
++      do {
++              struct remote_dev_info *dev = l->data;
++              char peer_addr[18];
++              const char *signal = NULL, *paddr = peer_addr;
++ 
++               /* flag to indicate the current remote name requested */
++              dev->name_status = NAME_REQUESTED;
++ 
++              memset(&rp, 0, sizeof(rp));
++              memset(&cp, 0, sizeof(cp));
++              bacpy(&cp.bdaddr, &dev->bdaddr);
++              cp.pscan_rep_mode = 0x02;
++ 
++              ba2str(&dev->bdaddr, peer_addr);
++ 
++              if (hci_send_req(dd, &rq, 500) < 0) {
++                      error("Unable to send the HCI remote name request: %s (%d)",
++                                              strerror(errno), errno);
++                      signal = "RemoteNameFailed";
++              }
++ 
++              if (rp.status) {
++                      error("Remote name request failed with status 0x%02x",
++                                      rp.status);
++                      signal = "RemoteNameFailed";
++              }
++ 
++              if (!signal) {
++                      req_sent = 1;
++                      /* if we are in discovery, inform application of getting name */
++                      if (adapter->discov_type & (STD_INQUIRY | PERIODIC_INQUIRY))
++                              signal = "RemoteNameRequested";
++              }
++ 
++              if (signal)
++                      send_adapter_signal(connection, adapter->dev_id, signal,
++                                              DBUS_TYPE_STRING, &paddr,
++                                              DBUS_TYPE_INVALID);
++ 
++              if (req_sent)
++                      break;
++ 
++              /* if failed, request the next element */
++              /* remove the element from the list */
++              adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
++              g_free(dev);
++ 
++              /* get the next element */
++              l = g_slist_find_custom(adapter->found_devices, &match,
++                                      (GCompareFunc) found_device_cmp);
++ 
++      } while (l);
++ 
++      hci_close_dev(dd);
++ 
++      if (!req_sent)
++              return -ENODATA;
++ 
++      return 0;
++ }
++ 
++ static void send_out_of_range(const char *path, GSList *l)
++ {
++      while (l) {
++              const char *peer_addr = l->data;
++ 
++              g_dbus_emit_signal(connection, path,
++                                              ADAPTER_INTERFACE,
++                                              "RemoteDeviceDisappeared",
++                                              DBUS_TYPE_STRING, &peer_addr,
++                                              DBUS_TYPE_INVALID);
++ 
++              if (hcid_dbus_use_experimental()) {
++                      const char *ptr = path + ADAPTER_PATH_INDEX;
++                      g_dbus_emit_signal(connection, ptr,
++                                              ADAPTER_INTERFACE,
++                                              "DeviceDisappeared",
++                                              DBUS_TYPE_STRING,
++                                              &peer_addr,
++                                              DBUS_TYPE_INVALID);
++              }
++ 
++              l = l->next;
++      }
++ }
++ 
++ void hcid_dbus_inquiry_complete(bdaddr_t *local)
++ {
++      struct adapter *adapter;
++      struct remote_dev_info *dev;
++      bdaddr_t tmp;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("Unable to find matching adapter");
++              return;
++      }
++ 
++      /* Out of range verification */
++      if (adapter->pdiscov_active && !adapter->discov_active) {
++              GSList *l;
++ 
++              send_out_of_range(adapter->path, adapter->oor_devices);
++ 
++              g_slist_foreach(adapter->oor_devices, (GFunc) free, NULL);
++              g_slist_free(adapter->oor_devices);
++              adapter->oor_devices = NULL;
++ 
++              l = adapter->found_devices;
++              while (l) {
++                      dev = l->data;
++                      baswap(&tmp, &dev->bdaddr);
++                      adapter->oor_devices = g_slist_append(adapter->oor_devices,
++                                                              batostr(&tmp));
++                      l = l->next;
++              }
++      }
++ 
++      adapter->pinq_idle = 1;
++ 
++      /*
++       * Enable resolution again: standard inquiry can be
++       * received in the periodic inquiry idle state.
++       */
++      if (adapter->pdiscov_requestor && adapter->pdiscov_resolve_names)
++              adapter->discov_type |= RESOLVE_NAME;
++ 
++      /*
++       * The following scenarios can happen:
++       * 1. standard inquiry: always send discovery completed signal
++       * 2. standard inquiry + name resolving: send discovery completed
++       *    after name resolving
++       * 3. periodic inquiry: skip discovery completed signal
++       * 4. periodic inquiry + standard inquiry: always send discovery
++       *    completed signal
++       *
++       * Keep in mind that non D-Bus requests can arrive.
++       */
++ 
++      if (!found_device_req_name(adapter))
++              return;         /* skip - there is name to resolve */
++ 
++      if (adapter->discov_active) {
++              if (hcid_dbus_use_experimental()) {
++                      const char *ptr = adapter->path + ADAPTER_PATH_INDEX;
++                      g_dbus_emit_signal(connection, ptr,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++ 
++              }
++ 
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++              adapter->discov_active = 0;
++      }
++ 
++      /* free discovered devices list */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      if (adapter->discov_requestor) {
++              g_dbus_remove_watch(connection, adapter->discov_listener);
++              adapter->discov_listener = 0;
++              g_free(adapter->discov_requestor);
++              adapter->discov_requestor = NULL;
++ 
++              /* If there is a pending reply for discovery cancel */
++              if (adapter->discovery_cancel) {
++                      DBusMessage *reply;
++                      reply = dbus_message_new_method_return(adapter->discovery_cancel);
++                      dbus_connection_send(connection, reply, NULL);
++                      dbus_message_unref(reply);
++                      dbus_message_unref(adapter->discovery_cancel);
++                      adapter->discovery_cancel = NULL;
++              }
++ 
++              /* reset the discover type for standard inquiry only */
++              adapter->discov_type &= ~STD_INQUIRY;
++      }
++ }
++ 
++ void hcid_dbus_periodic_inquiry_start(bdaddr_t *local, uint8_t status)
++ {
++      struct adapter *adapter;
++ 
++      /* Don't send the signal if the cmd failed */
++      if (status)
++              return;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      adapter->pdiscov_active = 1;
++ 
++      /* Disable name resolution for non D-Bus clients */
++      if (!adapter->pdiscov_requestor)
++              adapter->discov_type &= ~RESOLVE_NAME;
++ 
++      if (hcid_dbus_use_experimental())
++              dbus_connection_emit_property_changed(connection,
++                                              adapter->path + ADAPTER_PATH_INDEX,
++                                              ADAPTER_INTERFACE,
++                                              "PeriodicDiscovery",
++                                              DBUS_TYPE_BOOLEAN,
++                                              &adapter->pdiscov_active);
++ 
++      g_dbus_emit_signal(connection, adapter->path, ADAPTER_INTERFACE,
++                                      "PeriodicDiscoveryStarted",
++                                      DBUS_TYPE_INVALID);
++ }
++ 
++ void hcid_dbus_periodic_inquiry_exit(bdaddr_t *local, uint8_t status)
++ {
++      struct adapter *adapter;
++      char *ptr;
++ 
++      /* Don't send the signal if the cmd failed */
++      if (status)
++              return;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      ptr = adapter->path + ADAPTER_PATH_INDEX;
++ 
++      /* reset the discover type to be able to handle D-Bus and non D-Bus
++       * requests */
++      adapter->pdiscov_active = 0;
++      adapter->discov_type &= ~(PERIODIC_INQUIRY | RESOLVE_NAME);
++ 
++      /* free discovered devices list */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      /* free out of range devices list */
++      g_slist_foreach(adapter->oor_devices, (GFunc) free, NULL);
++      g_slist_free(adapter->oor_devices);
++      adapter->oor_devices = NULL;
++ 
++      if (adapter->pdiscov_requestor) {
++              g_dbus_remove_watch(connection, adapter->pdiscov_listener);
++              adapter->pdiscov_listener = 0;
++              g_free(adapter->pdiscov_requestor);
++              adapter->pdiscov_requestor = NULL;
++      }
++ 
++       /* workaround: inquiry completed is not sent when exiting from
++        * periodic inquiry */
++      if (adapter->discov_active) {
++              if (hcid_dbus_use_experimental())
++                      g_dbus_emit_signal(connection, ptr,
++                                      ADAPTER_INTERFACE,
++                                      "DiscoveryCompleted",
++                                      DBUS_TYPE_INVALID);
++ 
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++              adapter->discov_active = 0;
++      }
++ 
++      /* Send discovery completed signal if there isn't name to resolve */
++      g_dbus_emit_signal(connection, adapter->path,
++                                      ADAPTER_INTERFACE,
++                                      "PeriodicDiscoveryStopped",
++                                      DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental())
++              dbus_connection_emit_property_changed(connection, ptr,
++                                              ADAPTER_INTERFACE,
++                                              "PeriodicDiscovery",
++                                              DBUS_TYPE_BOOLEAN,
++                                              &adapter->discov_active);
++ }
++ 
++ static char *extract_eir_name(uint8_t *data, uint8_t *type)
++ {
++      if (!data || !type)
++              return NULL;
++ 
++      if (data[0] == 0)
++              return NULL;
++ 
++      *type = data[1];
++ 
++      switch (*type) {
++      case 0x08:
++      case 0x09:
++              return strndup((char *) (data + 2), data[0] - 1);
++      }
++ 
++      return NULL;
++ }
++ 
++ static void append_dict_valist(DBusMessageIter *iter,
++                                      const char *first_key,
++                                      va_list var_args)
++ {
++      DBusMessageIter dict;
++      const char *key;
++      int type;
++      void *val;
++ 
++      dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
++                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
++                      DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
++                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
++ 
++      key = first_key;
++      while (key) {
++              type = va_arg(var_args, int);
++              val = va_arg(var_args, void *);
++              dbus_message_iter_append_dict_entry(&dict, key, type, val);
++              key = va_arg(var_args, char *);
++      }
++ 
++      dbus_message_iter_close_container(iter, &dict);
++ }
++ 
++ static void emit_device_found(const char *path, const char *address,
++                              const char *first_key, ...)
++ {
++      DBusMessage *signal;
++      DBusMessageIter iter;
++      va_list var_args;
++ 
++      signal = dbus_message_new_signal(path, ADAPTER_INTERFACE,
++                                      "DeviceFound");
++      if (!signal) {
++              error("Unable to allocate new %s.DeviceFound signal",
++                              ADAPTER_INTERFACE);
++              return;
++      }
++      dbus_message_iter_init_append(signal, &iter);
++      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &address);
++ 
++      va_start(var_args, first_key);
++      append_dict_valist(&iter, first_key, var_args);
++      va_end(var_args);
++ 
++      dbus_connection_send(connection, signal, NULL);
++ 
++      dbus_message_unref(signal);
++ }
++ 
++ void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class,
++                              int8_t rssi, uint8_t *data)
++ {
++      char filename[PATH_MAX + 1];
++      struct adapter *adapter;
++      GSList *l;
++      char local_addr[18], peer_addr[18], *name, *tmp_name;
++      const char *paddr = peer_addr;
++      struct remote_dev_info match;
++      dbus_int16_t tmp_rssi = rssi;
++      uint8_t name_type = 0x00;
++      name_status_t name_status;
++ 
++      ba2str(local, local_addr);
++      ba2str(peer, peer_addr);
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      write_remote_class(local, peer, class);
++ 
++      if (data)
++              write_remote_eir(local, peer, data);
++ 
++      /*
++       * workaround to identify situation when the daemon started and
++       * a standard inquiry or periodic inquiry was already running
++       */
++      if (!adapter->discov_active && !adapter->pdiscov_active)
++              adapter->pdiscov_active = 1;
++ 
++      /* reset the idle flag when the inquiry complete event arrives */
++      if (adapter->pdiscov_active) {
++              adapter->pinq_idle = 0;
++ 
++              /* Out of range list update */
++              l = g_slist_find_custom(adapter->oor_devices, peer_addr,
++                              (GCompareFunc) strcmp);
++              if (l) {
++                      char *dev = l->data;
++                      adapter->oor_devices = g_slist_remove(adapter->oor_devices,
++                                                              dev);
++                      g_free(dev);
++              }
++      }
++ 
++      /* send the device found signal */
++      g_dbus_emit_signal(connection, adapter->path,
++                                      ADAPTER_INTERFACE,
++                                      "RemoteDeviceFound",
++                                      DBUS_TYPE_STRING, &paddr,
++                                      DBUS_TYPE_UINT32, &class,
++                                      DBUS_TYPE_INT16, &tmp_rssi,
++                                      DBUS_TYPE_INVALID);
++ 
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, peer);
++      match.name_status = NAME_SENT;
++      /* if found: don't send the name again */
++      l = g_slist_find_custom(adapter->found_devices, &match,
++                      (GCompareFunc) found_device_cmp);
++      if (l)
++              return;
++ 
++      /* the inquiry result can be triggered by NON D-Bus client */
++      if (adapter->discov_type & RESOLVE_NAME)
++              name_status = NAME_REQUIRED;
++      else
++              name_status = NAME_NOT_REQUIRED;
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, local_addr, "names");
++      name = textfile_get(filename, peer_addr);
++ 
++      tmp_name = extract_eir_name(data, &name_type);
++      if (tmp_name) {
++              if (name_type == 0x09) {
++                      write_device_name(local, peer, tmp_name);
++                      name_status = NAME_NOT_REQUIRED;
++ 
++                      if (name)
++                              g_free(name);
++ 
++                      name = tmp_name;
++              } else {
++                      if (name)
++                              free(tmp_name);
++                      else
++                              name = tmp_name;
++              }
++      }
++ 
++      if (name) {
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "RemoteNameUpdated",
++                                              DBUS_TYPE_STRING, &paddr,
++                                              DBUS_TYPE_STRING, &name,
++                                              DBUS_TYPE_INVALID);
++              if (name_type != 0x08)
++                      name_status = NAME_SENT;
++ 
++              if (hcid_dbus_use_experimental()) {
++                      emit_device_found(adapter->path + ADAPTER_PATH_INDEX,
++                                      paddr,
++                                      "Address", DBUS_TYPE_STRING, &paddr,
++                                      "Class", DBUS_TYPE_UINT32, &class,
++                                      "RSSI", DBUS_TYPE_INT16, &tmp_rssi,
++                                      "Name", DBUS_TYPE_STRING, &name,
++                                      NULL);
++              }
++ 
++              g_free(name);
++      } else if (hcid_dbus_use_experimental()) {
++              emit_device_found(adapter->path + ADAPTER_PATH_INDEX,
++                              paddr,
++                              "Address", DBUS_TYPE_STRING, &paddr,
++                              "Class", DBUS_TYPE_UINT32, &class,
++                              "RSSI", DBUS_TYPE_INT16, &tmp_rssi,
++                              NULL);
++      }
++ 
++      /* add in the list to track name sent/pending */
++      found_device_add(&adapter->found_devices, peer, rssi, name_status);
++ }
++ 
++ void hcid_dbus_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
++ {
++      char peer_addr[18];
++      const char *paddr = peer_addr;
++      uint32_t old_class = 0;
++      struct adapter *adapter;
++ 
++      read_remote_class(local, peer, &old_class);
++ 
++      if (old_class == class)
++              return;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      ba2str(peer, peer_addr);
++ 
++      send_adapter_signal(connection, adapter->dev_id,
++                              "RemoteClassUpdated",
++                              DBUS_TYPE_STRING, &paddr,
++                              DBUS_TYPE_UINT32, &class,
++                              DBUS_TYPE_INVALID);
++ 
++      if (hcid_dbus_use_experimental()) {
++              GSList *l;
++              struct device *device;
++ 
++              l = g_slist_find_custom(adapter->devices, paddr,
++                              (GCompareFunc) device_address_cmp);
++              if (!l)
++                      return;
++ 
++              device = l->data;
++              dbus_connection_emit_property_changed(connection,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Class", DBUS_TYPE_UINT32, &class);
++      }
++ }
++ 
++ void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status,
++                              char *name)
++ {
++      struct adapter *adapter;
++      char peer_addr[18];
++      const char *paddr = peer_addr;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      ba2str(peer, peer_addr);
++ 
++      if (status)
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "RemoteNameFailed",
++                                              DBUS_TYPE_STRING, &paddr,
++                                              DBUS_TYPE_INVALID);
++      else {
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "RemoteNameUpdated",
++                                              DBUS_TYPE_STRING, &paddr,
++                                              DBUS_TYPE_STRING, &name,
++                                              DBUS_TYPE_INVALID);
++ 
++              if (hcid_dbus_use_experimental()) {
++                      struct device *device;
++ 
++                      device = adapter_find_device(adapter, paddr);
++                      if (device) {
++                              dbus_connection_emit_property_changed(connection,
++                                              device->path, DEVICE_INTERFACE,
++                                              "Name", DBUS_TYPE_STRING, &name);
++                      }
++              }
++      }
++ 
++      /* remove from remote name request list */
++      found_device_remove(&adapter->found_devices, peer);
++ 
++      /* check if there is more devices to request names */
++      if (!found_device_req_name(adapter))
++              return; /* skip if a new request has been sent */
++ 
++      /* free discovered devices list */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      /* The discovery completed signal must be sent only for discover
++       * devices request WITH name resolving */
++      if (adapter->discov_requestor) {
++              g_dbus_remove_watch(connection, adapter->discov_listener);
++              adapter->discov_listener = 0;
++              g_free(adapter->discov_requestor);
++              adapter->discov_requestor = NULL;
++ 
++              /* If there is a pending reply for discovery cancel */
++              if (adapter->discovery_cancel) {
++                      DBusMessage *reply;
++                      reply = dbus_message_new_method_return(adapter->discovery_cancel);
++                      dbus_connection_send(connection, reply, NULL);
++                      dbus_message_unref(reply);
++                      dbus_message_unref(adapter->discovery_cancel);
++                      adapter->discovery_cancel = NULL;
++              }
++ 
++              /* Disable name resolution for non D-Bus clients */
++              if (!adapter->pdiscov_requestor)
++                      adapter->discov_type &= ~RESOLVE_NAME;
++      }
++ 
++      if (adapter->discov_active) {
++              if (hcid_dbus_use_experimental())
++                      g_dbus_emit_signal(connection,
++                                      adapter->path + ADAPTER_PATH_INDEX,
++                                      ADAPTER_INTERFACE,
++                                      "DiscoveryCompleted",
++                                      DBUS_TYPE_INVALID);
++ 
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "DiscoveryCompleted",
++                                              DBUS_TYPE_INVALID);
++              adapter->discov_active = 0;
++      }
++ }
++ 
++ void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle,
++                              bdaddr_t *peer)
++ {
++      char peer_addr[18];
++      const char *paddr = peer_addr;
++      struct adapter *adapter;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      ba2str(peer, peer_addr);
++ 
++      if (status) {
++              struct pending_auth_info *auth;
++ 
++              cancel_passkey_agent_requests(adapter->passkey_agents,
++                                              adapter->path, peer);
++              release_passkey_agents(adapter, peer);
++ 
++              auth = adapter_find_auth_request(adapter, peer);
++              if (auth && auth->agent)
++                      agent_cancel(auth->agent);
++ 
++              adapter_remove_auth_request(adapter, peer);
++ 
++              if (adapter->bonding)
++                      adapter->bonding->hci_status = status;
++      } else {
++              /* Send the remote device connected signal */
++              g_dbus_emit_signal(connection, adapter->path,
++                                              ADAPTER_INTERFACE,
++                                              "RemoteDeviceConnected",
++                                              DBUS_TYPE_STRING, &paddr,
++                                              DBUS_TYPE_INVALID);
++ 
++              if (hcid_dbus_use_experimental()) {
++                      struct device *device;
++                      gboolean connected = TRUE;
++ 
++                      device = adapter_find_device(adapter, paddr);
++                      if (device) {
++                              dbus_connection_emit_property_changed(connection,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Connected", DBUS_TYPE_BOOLEAN,
++                                      &connected);
++                      }
++              }
++ 
++              /* add in the active connetions list */
++              active_conn_append(&adapter->active_conn, peer, handle);
++      }
++ }
++ 
++ void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status,
++                              uint16_t handle, uint8_t reason)
++ {
++      DBusMessage *reply;
++      char peer_addr[18];
++      const char *paddr = peer_addr;
++      struct adapter *adapter;
++      struct device *device;
++      struct active_conn_info *dev;
++      GSList *l;
++      gboolean connected = FALSE;
++      struct pending_auth_info *auth;
++ 
++      if (status) {
++              error("Disconnection failed: 0x%02x", status);
++              return;
++      }
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      l = g_slist_find_custom(adapter->active_conn, &handle,
++                              active_conn_find_by_handle);
++ 
++      if (!l)
++              return;
++ 
++      dev = l->data;
++ 
++      ba2str(&dev->bdaddr, peer_addr);
++ 
++      /* clean pending HCI cmds */
++      hci_req_queue_remove(adapter->dev_id, &dev->bdaddr);
++ 
++      /* Cancel D-Bus/non D-Bus requests */
++      cancel_passkey_agent_requests(adapter->passkey_agents, adapter->path,
++                                      &dev->bdaddr);
++      release_passkey_agents(adapter, &dev->bdaddr);
++ 
++      auth = adapter_find_auth_request(adapter, &dev->bdaddr);
++      if (auth && auth->agent)
++              agent_cancel(auth->agent);
++ 
++      adapter_remove_auth_request(adapter, &dev->bdaddr);
++ 
++      /* Check if there is a pending CreateBonding request */
++      if (adapter->bonding && (bacmp(&adapter->bonding->bdaddr, &dev->bdaddr) == 0)) {
++              if (adapter->bonding->cancel) {
++                      /* reply authentication canceled */
++                      error_authentication_canceled(connection,
++                                                      adapter->bonding->msg);
++              } else {
++                      reply = new_authentication_return(adapter->bonding->msg,
++                                                      HCI_AUTHENTICATION_FAILURE);
++                      dbus_connection_send(connection, reply, NULL);
++                      dbus_message_unref(reply);
++              }
++ 
++              g_dbus_remove_watch(adapter->bonding->conn,
++                                      adapter->bonding->listener_id);
++ 
++              if (adapter->bonding->io_id)
++                      g_source_remove(adapter->bonding->io_id);
++              g_io_channel_close(adapter->bonding->io);
++              bonding_request_free(adapter->bonding);
++              adapter->bonding = NULL;
++      }
++ 
++      /* Check if there is a pending RemoteDeviceDisconnect request */
++      if (adapter->pending_dc) {
++              reply = dbus_message_new_method_return(adapter->pending_dc->msg);
++              if (reply) {
++                      dbus_connection_send(connection, reply, NULL);
++                      dbus_message_unref(reply);
++              } else
++                      error("Failed to allocate disconnect reply");
++ 
++              g_source_remove(adapter->pending_dc->timeout_id);
++              dc_pending_timeout_cleanup(adapter);
++      }
++ 
++      /* Send the remote device disconnected signal */
++      g_dbus_emit_signal(connection, adapter->path,
++                                      ADAPTER_INTERFACE,
++                                      "RemoteDeviceDisconnected",
++                                      DBUS_TYPE_STRING, &paddr,
++                                      DBUS_TYPE_INVALID);
++ 
++      adapter->active_conn = g_slist_remove(adapter->active_conn, dev);
++      g_free(dev);
++ 
++      device = adapter_find_device(adapter, paddr);
++      if (device) {
++              dbus_connection_emit_property_changed(connection,
++                                      device->path, DEVICE_INTERFACE,
++                                      "Connected", DBUS_TYPE_BOOLEAN,
++                                      &connected);
++              if (device->temporary) {
++                      debug("Removing temporary device %s", device->address);
++                      adapter_remove_device(connection, adapter, device);
++              }
++      }
++ }
++ 
++ int set_limited_discoverable(int dd, const uint8_t *cls, gboolean limited)
++ {
++      uint32_t dev_class;
++      int err;
++      int num = (limited ? 2 : 1);
++      uint8_t lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
++      /*
++       * 1: giac
++       * 2: giac + liac
++       */
++      if (hci_write_current_iac_lap(dd, num, lap, 1000) < 0) {
++              err = errno;
++              error("Can't write current IAC LAP: %s(%d)",
++                              strerror(err), err);
++              return -err;
++      }
++ 
++      if (limited) {
++              if (cls[1] & 0x20)
++                      return 0; /* Already limited */
++ 
++              dev_class = (cls[2] << 16) | ((cls[1] | 0x20) << 8) | cls[0];
++      } else {
++              if (!(cls[1] & 0x20))
++                      return 0; /* Already clear */
++ 
++              dev_class = (cls[2] << 16) | ((cls[1] & 0xdf) << 8) | cls[0];
++      }
++ 
++      if (hci_write_class_of_dev(dd, dev_class, 1000) < 0) {
++              err = errno;
++              error("Can't write class of device: %s (%d)",
++                                                      strerror(err), err);
++              return -err;
++      }
++ 
++      return 0;
++ }
++ 
++ int set_service_classes(int dd, const uint8_t *cls, uint8_t value)
++ {
++      uint32_t dev_class;
++      int err;
++ 
++      if (cls[2] == value)
++              return 0; /* Already set */
++ 
++      dev_class = (value << 16) | (cls[1] << 8) | cls[0];
++ 
++      if (hci_write_class_of_dev(dd, dev_class, 1000) < 0) {
++              err = errno;
++              error("Can't write class of device: %s (%d)",
++                                                      strerror(err), err);
++              return -err;
++      }
++ 
++      return 0;
++ }
++ 
++ gboolean discov_timeout_handler(void *data)
++ {
++      struct adapter *adapter = data;
++      struct hci_request rq;
++      int dd;
++      uint8_t scan_enable = adapter->scan_enable;
++      uint8_t status = 0;
++      gboolean retval = TRUE;
++ 
++      scan_enable &= ~SCAN_INQUIRY;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error("HCI device open failed: hci%d", adapter->dev_id);
++              return TRUE;
++      }
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_HOST_CTL;
++      rq.ocf    = OCF_WRITE_SCAN_ENABLE;
++      rq.cparam = &scan_enable;
++      rq.clen   = sizeof(scan_enable);
++      rq.rparam = &status;
++      rq.rlen   = sizeof(status);
++      rq.event  = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, 1000) < 0) {
++              error("Sending write scan enable command to hci%d failed: %s (%d)",
++                              adapter->dev_id, strerror(errno), errno);
++              goto failed;
++      }
++      if (status) {
++              error("Setting scan enable failed with status 0x%02x", status);
++              goto failed;
++      }
++ 
++      set_limited_discoverable(dd, adapter->class, FALSE);
++ 
++      adapter->timeout_id = 0;
++      retval = FALSE;
++ 
++ failed:
++      if (dd >= 0)
++              hci_close_dev(dd);
++ 
++      return retval;
++ }
++ 
++ /* Section reserved to device HCI callbacks */
++ 
++ void hcid_dbus_setname_complete(bdaddr_t *local)
++ {
++      int id, dd = -1;
++      read_local_name_rp rp;
++      struct hci_request rq;
++      const char *pname = (char *) rp.name;
++      char local_addr[18], name[249];
++ 
++      ba2str(local, local_addr);
++ 
++      id = hci_devid(local_addr);
++      if (id < 0) {
++              error("No matching device id for %s", local_addr);
++              return;
++      }
++ 
++      dd = hci_open_dev(id);
++      if (dd < 0) {
++              error("HCI device open failed: hci%d", id);
++              memset(&rp, 0, sizeof(rp));
++      } else {
++              memset(&rq, 0, sizeof(rq));
++              rq.ogf    = OGF_HOST_CTL;
++              rq.ocf    = OCF_READ_LOCAL_NAME;
++              rq.rparam = &rp;
++              rq.rlen   = READ_LOCAL_NAME_RP_SIZE;
++              rq.event  = EVT_CMD_COMPLETE;
++ 
++              if (hci_send_req(dd, &rq, 1000) < 0) {
++                      error("Sending getting name command failed: %s (%d)",
++                                              strerror(errno), errno);
++                      rp.name[0] = '\0';
++              } else if (rp.status) {
++                      error("Getting name failed with status 0x%02x",
++                                      rp.status);
++                      rp.name[0] = '\0';
++              }
++              hci_close_dev(dd);
++      }
++ 
++      strncpy(name, pname, sizeof(name) - 1);
++      name[248] = '\0';
++      pname = name;
++ 
++      send_adapter_signal(connection, id, "NameChanged",
++                              DBUS_TYPE_STRING, &pname, DBUS_TYPE_INVALID);
++ }
++ 
++ void hcid_dbus_setscan_enable_complete(bdaddr_t *local)
++ {
++      struct adapter *adapter;
++      read_scan_enable_rp rp;
++      struct hci_request rq;
++      int dd = -1;
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error("HCI device open failed: hci%d", adapter->dev_id);
++              return;
++      }
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_HOST_CTL;
++      rq.ocf    = OCF_READ_SCAN_ENABLE;
++      rq.rparam = &rp;
++      rq.rlen   = READ_SCAN_ENABLE_RP_SIZE;
++      rq.event  = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, 1000) < 0) {
++              error("Sending read scan enable command failed: %s (%d)",
++                              strerror(errno), errno);
++              goto failed;
++      }
++ 
++      if (rp.status) {
++              error("Getting scan enable failed with status 0x%02x",
++                              rp.status);
++              goto failed;
++      }
++ 
++      if (adapter->timeout_id) {
++              g_source_remove(adapter->timeout_id);
++              adapter->timeout_id = 0;
++      }
++ 
++      if (adapter->scan_enable != rp.enable)
++              adapter_mode_changed(adapter, rp.enable);
++ 
++ failed:
++      if (dd >= 0)
++              hci_close_dev(dd);
++ }
++ 
++ void hcid_dbus_write_class_complete(bdaddr_t *local)
++ {
++      struct adapter *adapter;
++      int dd;
++      uint8_t cls[3];
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return;
++      }
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              error("HCI device open failed: hci%d", adapter->dev_id);
++              return;
++      }
++ 
++      if (hci_read_class_of_dev(dd, cls, 1000) < 0) {
++              error("Can't read class of device on hci%d: %s (%d)",
++                      adapter->dev_id, strerror(errno), errno);
++              hci_close_dev(dd);
++              return;
++      }
++ 
++      write_local_class(local, cls);
++      set_device_class(adapter->dev_id, cls);
++      memcpy(adapter->class, cls, 3);
++ 
++      hci_close_dev(dd);
++ }
++ 
++ void hcid_dbus_write_simple_pairing_mode_complete(bdaddr_t *local)
++ {
++      char addr[18];
++      int dev_id, dd;
++      uint8_t mode;
++ 
++      ba2str(local, addr);
++ 
++      dev_id = hci_devid(addr);
++      if (dev_id < 0) {
++              error("No matching device id for %s", addr);
++              return;
++      }
++ 
++      dd = hci_open_dev(dev_id);
++      if (dd < 0) {
++              error("HCI device open failed: hci%d", dev_id);
++              return;
++      }
++ 
++      if (hci_read_simple_pairing_mode(dd, &mode, 1000) < 0) {
++              error("Can't read class of device on hci%d: %s(%d)",
++                                      dev_id, strerror(errno), errno);
++              hci_close_dev(dd);
++              return;
++      }
++ 
++      set_simple_pairing_mode(dev_id, mode);
++ 
++      hci_close_dev(dd);
++ }
++ 
++ int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
++                                              uint8_t *cap, uint8_t *auth)
++ {
++      struct adapter *adapter;
++      struct device *device;
++      struct agent *agent;
++      char addr[18];
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      if (get_auth_requirements(local, remote, auth) < 0)
++              return -1;
++ 
++      ba2str(remote, addr);
++ 
++      device = adapter_find_device(adapter, addr);
++      if (device && device->agent) {
++              agent = device->agent;
++              *auth = 0x03;
++      } else
++              agent = adapter->agent;
++ 
++      if (!agent) {
++              if (!(*auth & 0x01)) {
++                      /* No input, no output */
++                      *cap = 0x03;
++                      return 0;
++              }
++              error("No agent available for IO capability");
++              return -1;
++      }
++ 
++      *cap = agent_get_io_capability(agent);
++ 
++      return 0;
++ }
++ 
++ int hcid_dbus_set_io_cap(bdaddr_t *local, bdaddr_t *remote,
++                                                 uint8_t cap, uint8_t auth)
++ {
++      struct adapter *adapter;
++      struct device *device;
++      char addr[18];
++ 
++      adapter = manager_find_adapter(local);
++      if (!adapter) {
++              error("No matching adapter found");
++              return -1;
++      }
++ 
++      ba2str(remote, addr);
++ 
++      device = adapter_get_device(connection, adapter, addr);
++      if (device) {
++              device->cap = cap;
++              device->auth = auth;
++      }
++ 
++      return 0;
++ }
++ 
++ static int inquiry_cancel(int dd, int to)
++ {
++      struct hci_request rq;
++      uint8_t status;
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_INQUIRY_CANCEL;
++      rq.rparam = &status;
++      rq.rlen   = sizeof(status);
++      rq.event = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, to) < 0)
++              return -1;
++ 
++      if (status) {
++              errno = bt_error(status);
++              return -1;
++      }
++ 
++      return 0;
++ }
++ 
++ static int remote_name_cancel(int dd, bdaddr_t *dba, int to)
++ {
++      remote_name_req_cancel_cp cp;
++      struct hci_request rq;
++      uint8_t status;
++ 
++      memset(&rq, 0, sizeof(rq));
++      memset(&cp, 0, sizeof(cp));
++ 
++      bacpy(&cp.bdaddr, dba);
++ 
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_REMOTE_NAME_REQ_CANCEL;
++      rq.cparam = &cp;
++      rq.clen   = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
++      rq.rparam = &status;
++      rq.rlen = sizeof(status);
++      rq.event = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, to) < 0)
++              return -1;
++ 
++      if (status) {
++              errno = bt_error(status);
++              return -1;
++      }
++ 
++      return 0;
++ }
++ 
++ int cancel_discovery(struct adapter *adapter)
++ {
++      struct remote_dev_info *dev, match;
++      GSList *l;
++      int dd, err = 0;
++ 
++      if (!adapter->discov_active)
++              goto cleanup;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              err = -ENODEV;
++              goto cleanup;
++      }
++ 
++      /*
++       * If there is a pending read remote name request means
++       * that the inquiry complete event was already received
++       */
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, BDADDR_ANY);
++      match.name_status = NAME_REQUESTED;
++ 
++      l = g_slist_find_custom(adapter->found_devices, &match,
++                              (GCompareFunc) found_device_cmp);
++      if (l) {
++              dev = l->data;
++              if (remote_name_cancel(dd, &dev->bdaddr, 1000) < 0) {
++                      error("Read remote name cancel failed: %s, (%d)",
++                                      strerror(errno), errno);
++                      err = -errno;
++              }
++      } else {
++              if (inquiry_cancel(dd, 1000) < 0) {
++                      error("Inquiry cancel failed:%s (%d)",
++                                      strerror(errno), errno);
++                      err = -errno;
++              }
++      }
++ 
++      hci_close_dev(dd);
++ 
++ cleanup:
++      /*
++       * Reset discov_requestor and discover_state in the remote name
++       * request event handler or in the inquiry complete handler.
++       */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      /* Disable name resolution for non D-Bus clients */
++      if (!adapter->pdiscov_requestor)
++              adapter->discov_type &= ~RESOLVE_NAME;
++ 
++      return err;
++ }
++ 
++ static int periodic_inquiry_exit(int dd, int to)
++ {
++      struct hci_request rq;
++      uint8_t status;
++ 
++      memset(&rq, 0, sizeof(rq));
++      rq.ogf    = OGF_LINK_CTL;
++      rq.ocf    = OCF_EXIT_PERIODIC_INQUIRY;
++      rq.rparam = &status;
++      rq.rlen   = sizeof(status);
++      rq.event = EVT_CMD_COMPLETE;
++ 
++      if (hci_send_req(dd, &rq, to) < 0)
++              return -1;
++ 
++      if (status) {
++              errno = status;
++              return -1;
++      }
++ 
++      return 0;
++ }
++ 
++ int cancel_periodic_discovery(struct adapter *adapter)
++ {
++      struct remote_dev_info *dev, match;
++      GSList *l;
++      int dd, err = 0;
++ 
++      if (!adapter->pdiscov_active)
++              goto cleanup;
++ 
++      dd = hci_open_dev(adapter->dev_id);
++      if (dd < 0) {
++              err = -ENODEV;
++              goto cleanup;
++      }
++      /* find the pending remote name request */
++      memset(&match, 0, sizeof(struct remote_dev_info));
++      bacpy(&match.bdaddr, BDADDR_ANY);
++      match.name_status = NAME_REQUESTED;
++ 
++      l = g_slist_find_custom(adapter->found_devices, &match,
++                      (GCompareFunc) found_device_cmp);
++      if (l) {
++              dev = l->data;
++              if (remote_name_cancel(dd, &dev->bdaddr, 1000) < 0) {
++                      error("Read remote name cancel failed: %s, (%d)",
++                                      strerror(errno), errno);
++                      err = -errno;
++              }
++      }
++ 
++      /* ovewrite err if necessary: stop periodic inquiry has higher
++       * priority */
++      if (periodic_inquiry_exit(dd, 1000) < 0) {
++              error("Periodic Inquiry exit failed:%s (%d)",
++                              strerror(errno), errno);
++              err = -errno;
++      }
++ 
++      hci_close_dev(dd);
++ 
++ cleanup:
++      /*
++       * Reset pdiscov_requestor and pdiscov_active is done when the
++       * cmd complete event for exit periodic inquiry mode cmd arrives.
++       */
++      g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
++      g_slist_free(adapter->found_devices);
++      adapter->found_devices = NULL;
++ 
++      return err;
++ }
++ 
++ /* Most of the functions in this module require easy access to a connection so
++  * we keep it global here and provide these access functions the other (few)
++  * modules that require access to it */
++ 
++ void set_dbus_connection(DBusConnection *conn)
++ {
++      connection = conn;
++ }
++ 
++ DBusConnection *get_dbus_connection(void)
++ {
++      return connection;
++ }
index 0000000,0000000,02f9e31..b62c70a
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,1125 +1,1327 @@@@
  -static const char *extract_service_class(sdp_data_t *d)
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2006-2007  Nokia Corporation
++  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <sys/stat.h>
++ #include <sys/types.h>
++ #include <sys/param.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/l2cap.h>
++ #include <bluetooth/sdp.h>
++ #include <bluetooth/sdp_lib.h>
++ 
++ #include <netinet/in.h>
++ 
++ #include <glib.h>
++ #include <dbus/dbus.h>
++ #include <gdbus.h>
++ 
++ #include "hcid.h"
++ #include "textfile.h"
++ #include "adapter.h"
++ #include "dbus-hci.h"
++ #include "dbus-common.h"
++ #include "dbus-error.h"
++ #include "error.h"
++ #include "dbus-sdp.h"
++ #include "sdp-xml.h"
++ #include "glib-helper.h"
++ 
++ #define SESSION_TIMEOUT 2000
++ #define DEFAULT_XML_BUF_SIZE 1024
++ 
++ struct transaction_context {
++      char            *src;
++      char            *dst;
++      DBusConnection  *conn;
++      DBusMessage     *rq;
++      sdp_session_t   *session;
++      GIOChannel      *io;
++      guint           io_id;
++      uuid_t          uuid;
++      GSList          *identifiers;
++ };
++ 
++ typedef int connect_cb_t(struct transaction_context *t);
++ 
++ struct pending_connect {
++      DBusConnection  *conn;
++      DBusMessage     *rq;
++      char            *src;
++      char            *dst;
++      sdp_session_t   *session;
++      connect_cb_t    *conn_cb;
++ };
++ 
++ struct cached_session {
++      sdp_session_t *session;
++      guint timeout_id;
++      guint io_id;
++ };
++ 
++ static GSList *cached_sessions = NULL;
++ 
++ static inline DBusMessage *invalid_args(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
++                      "Invalid arguments in method call");
++ }
++ 
++ static inline DBusMessage *in_progress(DBusMessage *msg, const char *str)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", str);
++ }
++ 
++ static inline DBusMessage *adapter_not_ready(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady",
++                      "Adapter is not ready");
++ }
++ 
++ static inline DBusMessage *failed_strerror(DBusMessage *msg, int err)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
++                      strerror(err));
++ }
++ 
++ static gboolean session_timeout(gpointer user_data)
++ {
++      struct cached_session *s = user_data;
++ 
++      debug("sdp session timed out. closing");
++ 
++      cached_sessions = g_slist_remove(cached_sessions, s);
++ 
++      g_source_remove(s->io_id);
++      sdp_close(s->session);
++      g_free(s);
++ 
++      return FALSE;
++ }
++ 
++ gboolean idle_callback(GIOChannel *io, GIOCondition cond, gpointer user_data)
++ {
++      struct cached_session *s = user_data;
++ 
++      if (cond & G_IO_NVAL)
++              return FALSE;
++ 
++      if (cond & (G_IO_ERR | G_IO_HUP))
++              debug("idle_callback: session got disconnected");
++ 
++      if (cond & G_IO_IN)
++              debug("got unexpected input on idle SDP socket");
++ 
++      cached_sessions = g_slist_remove(cached_sessions, s);
++ 
++      g_source_remove(s->timeout_id);
++      sdp_close(s->session);
++      g_free(s);
++ 
++      return FALSE;
++ }
++ 
++ static void cache_sdp_session(sdp_session_t *sess, GIOChannel *io)
++ {
++      struct cached_session *s;
++ 
++      s = g_new0(struct cached_session, 1);
++ 
++      s->session = sess;
++      s->timeout_id = g_timeout_add(SESSION_TIMEOUT, session_timeout, s);
++      s->io_id = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
++                                      idle_callback, s);
++ 
++      cached_sessions = g_slist_append(cached_sessions, s);
++ 
++      debug("sdp session added to cache");
++ }
++ 
++ static int get_bdaddrs(int sock, bdaddr_t *sba, bdaddr_t *dba)
++ {
++      struct sockaddr_l2 a;
++      socklen_t len;
++ 
++      len = sizeof(a);
++      if (getsockname(sock, (struct sockaddr *) &a, &len) < 0) {
++              error("getsockname: %s (%d)", strerror(errno), errno);
++              return -1;
++      }
++ 
++      bacpy(sba, &a.l2_bdaddr);
++ 
++      len = sizeof(a);
++      if (getpeername(sock, (struct sockaddr *) &a, &len) < 0) {
++              error("getpeername: %s (%d)", strerror(errno), errno);
++              return -1;
++      }
++ 
++      bacpy(dba, &a.l2_bdaddr);
++ 
++      return 0;
++ }
++ 
++ static struct cached_session *get_cached_session(bdaddr_t *src, bdaddr_t *dst)
++ {
++      GSList *l;
++ 
++      for (l = cached_sessions; l != NULL; l = l->next) {
++              struct cached_session *s = l->data;
++              int sock = sdp_get_socket(s->session);
++              bdaddr_t sba, dba;
++ 
++              if (get_bdaddrs(sock, &sba, &dba) < 0)
++                      continue;
++ 
++              if (bacmp(&sba, src) || bacmp(&dba, dst))
++                      continue;
++ 
++              debug("found matching session, removing from list");
++ 
++              cached_sessions = g_slist_remove(cached_sessions, s);
++ 
++              return s;
++      }
++ 
++      return NULL;
++ }
++ 
++ static sdp_session_t *get_sdp_session(bdaddr_t *src, bdaddr_t *dst)
++ {
++      struct cached_session *s;
++      sdp_session_t *session;
++ 
++      s = get_cached_session(src, dst);
++      if (!s) {
++              debug("no matching session found. creating a new one");
++              return sdp_connect(src, dst, SDP_NON_BLOCKING);
++      }
++ 
++      session = s->session;
++ 
++      g_source_remove(s->timeout_id);
++      g_source_remove(s->io_id);
++      g_free(s);
++ 
++      return session;
++ }
++ 
++ void append_and_grow_string(void *data, const char *str)
++ {
++      sdp_buf_t *buff = data;
++      int len;
++ 
++      len = strlen(str);
++ 
++      if (!buff->data) {
++              buff->data = malloc(DEFAULT_XML_BUF_SIZE);
++              if (!buff->data)
++                      return;
++              buff->buf_size = DEFAULT_XML_BUF_SIZE;
++      }
++ 
++      /* Grow string */
++      while (buff->buf_size < (buff->data_size + len + 1)) {
++              void *tmp;
++              uint32_t new_size;
++ 
++              /* Grow buffer by a factor of 2 */
++              new_size = (buff->buf_size << 1);
++ 
++              tmp = realloc(buff->data, new_size);
++              if (!tmp)
++                      return;
++ 
++              buff->data = tmp;
++              buff->buf_size = new_size;
++      }
++ 
++      /* Include the NULL character */
++      memcpy(buff->data + buff->data_size, str, len + 1);
++      buff->data_size += len;
++ }
++ 
++ /* list of remote and local service records */
++ static GSList *pending_connects  = NULL;
++ 
++ static struct pending_connect *pending_connect_new(DBusConnection *conn,
++                                      DBusMessage *msg, const char *src,
++                                      const char *dst, connect_cb_t *cb)
++ {
++      struct pending_connect *c;
++ 
++      if (!dst)
++              return NULL;
++ 
++      c = g_new0(struct pending_connect, 1);
++      c->src = g_strdup(src);
++      c->dst = g_strdup(dst);
++      c->conn = dbus_connection_ref(conn);
++      c->rq = dbus_message_ref(msg);
++      c->conn_cb = cb;
++ 
++      return c;
++ }
++ 
++ static void pending_connect_free(struct pending_connect *c)
++ {
++      if (!c)
++              return;
++ 
++      g_free(c->src);
++      g_free(c->dst);
++ 
++      if (c->rq)
++              dbus_message_unref(c->rq);
++ 
++      if (c->conn)
++              dbus_connection_unref(c->conn);
++ 
++      g_free(c);
++ }
++ 
++ static struct pending_connect *find_pending_connect(const char *dst)
++ {
++      GSList *l;
++ 
++      for (l = pending_connects; l != NULL; l = l->next) {
++              struct pending_connect *pending = l->data;
++              if (!strcmp(dst, pending->dst))
++                      return pending;
++      }
++ 
++      return NULL;
++ }
++ 
++ static int sdp_store_record(const char *src, const char *dst, uint32_t handle, uint8_t *buf, size_t size)
++ {
++      char filename[PATH_MAX + 1], key[28], *value;
++      int i, err;
++ 
++      create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
++ 
++      create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
++ 
++      snprintf(key, sizeof(key), "%17s#%08X", dst, handle);
++ 
++      value = g_malloc0(size * 2 + 1);
++ 
++      for (i = 0; i < size; i++)
++              sprintf(value + (i * 2), "%02X", buf[i]);
++ 
++      err = textfile_put(filename, key, value);
++ 
++      g_free(value);
++ 
++      return err;
++ }
++ 
++ static void transaction_context_free(void *udata, gboolean cache)
++ {
++      struct transaction_context *ctxt = udata;
++ 
++      if (!ctxt)
++              return;
++ 
++      g_free(ctxt->src);
++      g_free(ctxt->dst);
++ 
++      if (ctxt->conn)
++              dbus_connection_unref(ctxt->conn);
++ 
++      if (ctxt->rq)
++              dbus_message_unref(ctxt->rq);
++ 
++      if (ctxt->session && !ctxt->io)
++              sdp_close(ctxt->session);
++ 
++      if (ctxt->session && ctxt->io) {
++              g_source_remove(ctxt->io_id);
++ 
++              if (cache)
++                      cache_sdp_session(ctxt->session, ctxt->io);
++              else
++                      sdp_close(ctxt->session);
++ 
++              g_io_channel_unref(ctxt->io);
++      }
++ 
++      if (ctxt->identifiers) {
++              g_slist_foreach(ctxt->identifiers, (GFunc) g_free, NULL);
++              g_slist_free(ctxt->identifiers);
++      }
++ 
++      g_free(ctxt);
++ }
++ 
++ static gboolean search_process_cb(GIOChannel *chan,
++                              GIOCondition cond, void *udata)
++ {
++      struct transaction_context *ctxt = udata;
++      int err = 0;
++ 
++      if (cond & G_IO_NVAL) {
++              g_io_channel_unref(chan);
++              return FALSE;
++      }
++ 
++      if (cond & (G_IO_ERR | G_IO_HUP)) {
++              err = EIO;
++              goto failed;
++      }
++ 
++      if (sdp_process(ctxt->session) < 0)
++              goto failed;
++ 
++      return TRUE;
++ 
++ failed:
++      if (err) {
++              error_failed_errno(ctxt->conn, ctxt->rq, err);
++              transaction_context_free(ctxt, FALSE);
++      }
++ 
++      return TRUE;
++ }
++ 
++ static void remote_svc_rec_completed_cb(uint8_t type, uint16_t err,
++                      uint8_t *rsp, size_t size, void *udata)
++ {
++      struct transaction_context *ctxt = udata;
++      sdp_record_t *rec;
++      DBusMessage *reply;
++      DBusMessageIter iter, array_iter;
++      int scanned;
++ 
++      if (!ctxt)
++              return;
++ 
++      if (err == 0xffff) {
++              /* Check for protocol error or I/O error */
++              int sdp_err = sdp_get_error(ctxt->session);
++              if (sdp_err < 0) {
++                      error("search failed: Invalid session!");
++                      error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);
++                      goto failed;
++              }
++ 
++              error("search failed: %s (%d)", strerror(sdp_err), sdp_err);
++              error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);
++              goto failed;
++      }
++ 
++      if (type == SDP_ERROR_RSP) {
++              error_sdp_failed(ctxt->conn, ctxt->rq, err);
++              goto failed;
++      }
++ 
++      /* check response PDU ID */
++      if (type != SDP_SVC_ATTR_RSP) {
++              error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);
++              error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);
++              goto failed;
++      }
++ 
++      reply = dbus_message_new_method_return(ctxt->rq);
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                      DBUS_TYPE_BYTE_AS_STRING, &array_iter);
++ 
++      rec = sdp_extract_pdu_safe(rsp, size, &scanned);
++      if (rec == NULL || size != scanned) {
++              error("Invalid service record!");
++              goto done;
++      }
++ 
++      sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, size);
++ 
++      sdp_record_free(rec);
++ 
++      dbus_message_iter_append_fixed_array(&array_iter,
++                      DBUS_TYPE_BYTE, &rsp, size);
++ 
++ done:
++      dbus_message_iter_close_container(&iter, &array_iter);
++      dbus_connection_send(ctxt->conn, reply, NULL);
++      dbus_message_unref(reply);
++ 
++ failed:
++      transaction_context_free(ctxt, TRUE);
++ }
++ 
++ static void remote_svc_rec_completed_xml_cb(uint8_t type, uint16_t err,
++                                              uint8_t *rsp, size_t size,
++                                              void *udata)
++ {
++      struct transaction_context *ctxt = udata;
++      sdp_record_t *rec;
++      DBusMessage *reply;
++      int scanned;
++      sdp_buf_t result;
++ 
++      if (!ctxt)
++              return;
++ 
++      if (err == 0xffff) {
++              /* Check for protocol error or I/O error */
++              int sdp_err = sdp_get_error(ctxt->session);
++              if (sdp_err < 0) {
++                      error("search failed: Invalid session!");
++                      error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);
++                      goto failed;
++              }
++ 
++              error("search failed: %s (%d)", strerror(sdp_err), sdp_err);
++              error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);
++              goto failed;
++      }
++ 
++      if (type == SDP_ERROR_RSP) {
++              error_sdp_failed(ctxt->conn, ctxt->rq, err);
++              goto failed;
++      }
++ 
++      /* check response PDU ID */
++      if (type != SDP_SVC_ATTR_RSP) {
++              error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);
++              error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);
++              goto failed;
++      }
++ 
++      reply = dbus_message_new_method_return(ctxt->rq);
++ 
++      rec = sdp_extract_pdu_safe(rsp, size, &scanned);
++      if (rec == NULL || size != scanned) {
++              error("Invalid service record!");
++              goto done;
++      }
++ 
++      sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, size);
++ 
++      memset(&result, 0, sizeof(sdp_buf_t));
++ 
++      convert_sdp_record_to_xml(rec, &result, append_and_grow_string);
++ 
++      sdp_record_free(rec);
++ 
++      if (result.data) {
++              dbus_message_append_args(reply,
++                              DBUS_TYPE_STRING, &result.data,
++                              DBUS_TYPE_INVALID);
++ 
++              free(result.data);
++      }
++ done:
++      dbus_connection_send(ctxt->conn, reply, NULL);
++      dbus_message_unref(reply);
++ 
++ failed:
++      transaction_context_free(ctxt, TRUE);
++ }
++ 
++ static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err,
++                      uint8_t *rsp, size_t size, void *udata)
++ {
++      struct transaction_context *ctxt = udata;
++      DBusMessage *reply;
++      DBusMessageIter iter, array_iter;
++      uint8_t *pdata;
++      int csrc, tsrc;
++ 
++      if (!ctxt)
++              return;
++ 
++      if (err == 0xffff) {
++              /* Check for protocol error or I/O error */
++              int sdp_err = sdp_get_error(ctxt->session);
++              if (sdp_err < 0) {
++                      error("search failed: Invalid session!");
++                      error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);
++                      goto failed;
++              }
++ 
++              error("search failed: %s (%d)", strerror(sdp_err), sdp_err);
++              error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);
++              goto failed;
++      }
++ 
++      if (type == SDP_ERROR_RSP) {
++              error_sdp_failed(ctxt->conn, ctxt->rq, err);
++              goto failed;
++      }
++ 
++      /* check response PDU ID */
++      if (type != SDP_SVC_SEARCH_RSP) {
++              error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);
++              error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);
++              goto failed;
++      }
++ 
++      reply = dbus_message_new_method_return(ctxt->rq);
++      dbus_message_iter_init_append(reply, &iter);
++      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
++                              DBUS_TYPE_UINT32_AS_STRING, &array_iter);
++ 
++      pdata = rsp;
++ 
++      tsrc = ntohs(bt_get_unaligned((uint16_t *) pdata));
++      if (tsrc <= 0)
++              goto done;
++ 
++      pdata += sizeof(uint16_t);
++ 
++      csrc = ntohs(bt_get_unaligned((uint16_t *) pdata));
++      if (csrc <= 0)
++              goto done;
++ 
++      pdata += sizeof(uint16_t);
++ 
++      do {
++              uint32_t handle = ntohl(bt_get_unaligned((uint32_t*)pdata));
++              pdata += sizeof(uint32_t);
++ 
++              dbus_message_iter_append_basic(&array_iter,
++                                              DBUS_TYPE_UINT32, &handle);
++      } while (--tsrc);
++ 
++ 
++ done:
++      dbus_message_iter_close_container(&iter, &array_iter);
++      dbus_connection_send(ctxt->conn, reply, NULL);
++      dbus_message_unref(reply);
++ 
++ failed:
++      transaction_context_free(ctxt, TRUE);
++ }
++ 
  -     static char uuid_str[37];
+++/* Extract 16 or 32 bit uuid */
+++static uuid_t *extract_service_class_uuid(sdp_data_t *d)
++ {
++      sdp_data_t *seq;
++      uuid_t *uuid;
  -     if (uuid->type != SDP_UUID16)
++ 
++      /* Expected sequence of UUID16 */
++      if (d->attrId != SDP_ATTR_SVCLASS_ID_LIST)
++              return NULL;
++ 
++      if (d->dtd != SDP_SEQ8 && d->dtd != SDP_SEQ16 && d->dtd != SDP_SEQ32)
++              return NULL;
++ 
++      if (!d->val.dataseq)
++              return NULL;
++ 
++      seq = d->val.dataseq;
++      if (!SDP_IS_UUID(seq->dtd))
++              return NULL;
++ 
++      uuid = &seq->val.uuid;
  -     sprintf(uuid_str, "0000%04x-0000-1000-8000-00805f9b34fb",
+++     if (uuid->type != SDP_UUID16 && uuid->type != SDP_UUID32)
++              return NULL;
++ 
+++     return uuid;
+++}
+++
+++static const char *extract_service_class(sdp_data_t *d)
+++{
+++     uuid_t *uuid;
+++     static char uuid_str[37];
+++
+++     uuid = extract_service_class_uuid(d);
+++     if (uuid->type == SDP_UUID16)
+++             sprintf(uuid_str, "0000%04x-0000-1000-8000-00805f9b34fb",
++                                                      uuid->value.uuid16);
+++     else if (uuid->type = SDP_UUID32)
+++             sprintf(uuid_str, "%08x-0000-1000-8000-00805f9b34fb",
+++                                                     uuid->value.uuid32);
++ 
++      return uuid_str;
++ }
++ 
++ static int service_search_attr(struct transaction_context *ctxt, uint16_t uuid)
++ {
++      sdp_list_t *attrids, *search;
++      uint32_t range = 0x0000ffff;
++      int ret = 0;
++ 
++      sdp_uuid16_create(&ctxt->uuid, uuid);
++ 
++      search = sdp_list_append(0, &ctxt->uuid);
++      attrids = sdp_list_append(NULL, &range);
++ 
++      /*
++       * Create/send the search request and set the
++       * callback to indicate the request completion
++       */
++      if (sdp_service_search_attr_async(ctxt->session, search,
++                              SDP_ATTR_REQ_RANGE, attrids) < 0)
++              ret = -sdp_get_error(ctxt->session);
++ 
++      sdp_list_free(search, NULL);
++      sdp_list_free(attrids, NULL);
++ 
++      return ret;
++ }
++ 
++ static void remote_svc_identifiers_completed_cb(uint8_t type, uint16_t err,
++                      uint8_t *rsp, size_t size, void *udata)
++ {
++      struct transaction_context *ctxt = udata;
++      const char *puuid;
++      const char *devid_uuid = "00001200-0000-1000-8000-00805f9b34fb";
++      char **identifiers;
++      DBusMessage *reply;
++      GSList *l = NULL;
++      int scanned, extracted = 0, len = 0, recsize = 0, bytesleft = size;
++      uint8_t dtd = 0;
++ 
++      if (!ctxt)
++              return;
++ 
++      if (err == 0xffff) {
++              /* Check for protocol error or I/O error */
++              int sdp_err = sdp_get_error(ctxt->session);
++              if (sdp_err < 0) {
++                      error("search failed: Invalid session!");
++                      error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);
++                      goto failed;
++              }
++ 
++              error("search failed: %s (%d)", strerror(sdp_err), sdp_err);
++              error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);
++              goto failed;
++      }
++ 
++      if (type == SDP_ERROR_RSP) {
++              error_sdp_failed(ctxt->conn, ctxt->rq, err);
++              goto failed;
++      }
++ 
++      /* Check response PDU ID */
++      if (type != SDP_SVC_SEARCH_ATTR_RSP) {
++              error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);
++              error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);
++              goto failed;
++      }
++ 
++      scanned = sdp_extract_seqtype_safe(rsp, bytesleft, &dtd, &len);
++      rsp += scanned;
++      bytesleft -= scanned;
++      for (; extracted < len; rsp += recsize, extracted += recsize, bytesleft -= recsize) {
++              sdp_record_t *rec;
++              sdp_data_t *d;
++ 
++              recsize = 0;
++              rec = sdp_extract_pdu_safe(rsp, bytesleft, &recsize);
++              if (!rec)
++                      break;
++ 
++              sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, recsize);
++ 
++              d = sdp_data_get(rec, SDP_ATTR_SVCLASS_ID_LIST);
++              if (!d) {
++                      sdp_record_free(rec);
++                      continue;
++              }
++ 
++              puuid = extract_service_class(d);
++              sdp_record_free(rec);
++              if (!puuid)
++                      continue;
++ 
++              /* Ignore repeated identifiers */
++              l = g_slist_find_custom(ctxt->identifiers,
++                              puuid, (GCompareFunc) strcmp);
++              if (l)
++                      continue;
++ 
++              ctxt->identifiers = g_slist_append(ctxt->identifiers,
++                                                      g_strdup(puuid));
++      }
++ 
++      /* If public browse response is empty: search for L2CAP */
++      if (!ctxt->identifiers && ctxt->uuid.value.uuid16 == PUBLIC_BROWSE_GROUP)
++              if (service_search_attr(ctxt, L2CAP_UUID) == 0)
++                      return; /* Wait the response */
++ 
++      /* Request DeviceID if it was not returned previously */
++      l = g_slist_find_custom(ctxt->identifiers,
++                      devid_uuid, (GCompareFunc) strcmp);
++      if (!l && ctxt->uuid.value.uuid16 != PNP_INFO_SVCLASS_ID)
++              if (service_search_attr(ctxt, PNP_INFO_SVCLASS_ID) == 0)
++                      return; /* Wait the response */
++ 
++      reply = dbus_message_new_method_return(ctxt->rq);
++ 
++      identifiers = g_new(char *, g_slist_length(ctxt->identifiers));
++ 
++      for (l = ctxt->identifiers, len = 0; l; l = l->next, len++)
++              identifiers[len] = l->data;
++ 
++      dbus_message_append_args(reply,
++                      DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++                      &identifiers, len,
++                      DBUS_TYPE_INVALID);
++ 
++      dbus_connection_send(ctxt->conn, reply, NULL);
++      dbus_message_unref(reply);
++ 
++      if (len)
++              g_dbus_emit_signal(ctxt->conn,
++                              dbus_message_get_path(ctxt->rq),
++                              ADAPTER_INTERFACE,
++                              "RemoteIdentifiersUpdated",
++                              DBUS_TYPE_STRING, &ctxt->dst,
++                              DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
++                              &identifiers, len,
++                              DBUS_TYPE_INVALID);
++ 
++      if (identifiers)
++              g_free(identifiers);
++ 
++ failed:
++      transaction_context_free(ctxt, TRUE);
++ }
++ 
++ static gboolean sdp_client_connect_cb(GIOChannel *chan,
++                                      GIOCondition cond, void *udata)
++ {
++      struct pending_connect *c = udata;
++      struct transaction_context *ctxt = NULL;
++      int sdp_err, err = 0, sk;
++      socklen_t len;
++ 
++      sk = g_io_channel_unix_get_fd(chan);
++ 
++      len = sizeof(err);
++      if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
++              error("getsockopt(): %s (%d)", strerror(errno), errno);
++              err = errno;
++              goto failed;
++      }
++      if (err != 0) {
++              error("connect(): %s (%d)", strerror(err), err);
++              goto failed;
++      }
++ 
++      ctxt = g_new0(struct transaction_context, 1);
++      ctxt->src = g_strdup(c->src);
++      ctxt->dst = g_strdup(c->dst);
++      ctxt->conn = dbus_connection_ref(c->conn);
++      ctxt->rq = dbus_message_ref(c->rq);
++      ctxt->session = c->session;
++ 
++      /* set the complete transaction callback and send the search request */
++      sdp_err = c->conn_cb(ctxt);
++      if (sdp_err < 0) {
++              err = -sdp_err;
++              error("search failed: %s (%d)", strerror(err), err);
++              goto failed;
++      }
++ 
++      /* set the callback responsible for update the transaction data */
++      ctxt->io_id = g_io_add_watch(chan,
++                              G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
++                              search_process_cb, ctxt);
++      ctxt->io = g_io_channel_ref(chan);
++ 
++      goto done;
++ 
++ failed:
++      error_connection_attempt_failed(c->conn, c->rq, err);
++ 
++      if (ctxt)
++              transaction_context_free(ctxt, FALSE);
++      else
++              sdp_close(c->session);
++ 
++ done:
++      pending_connects = g_slist_remove(pending_connects, c);
++      pending_connect_free(c);
++ 
++      return FALSE;
++ }
++ 
++ static struct pending_connect *connect_request(DBusConnection *conn,
++                                      DBusMessage *msg,
++                                      const char *src,
++                                      const char *dst,
++                                      connect_cb_t *cb, int *err)
++ {
++      struct pending_connect *c;
++      bdaddr_t srcba, dstba;
++      GIOChannel *chan;
++ 
++      c = pending_connect_new(conn, msg, src, dst, cb);
++      if (!c) {
++              if (err)
++                      *err = ENOMEM;
++              return NULL;
++      }
++ 
++      str2ba(src, &srcba);
++      str2ba(dst, &dstba);
++      c->session = get_sdp_session(&srcba, &dstba);
++      if (!c->session) {
++              if (err)
++                      *err = errno;
++              error("sdp_connect() failed: %s (%d)", strerror(errno), errno);
++              pending_connect_free(c);
++              return NULL;
++      }
++ 
++      chan = g_io_channel_unix_new(sdp_get_socket(c->session));
++      g_io_add_watch(chan, G_IO_OUT, sdp_client_connect_cb, c);
++      g_io_channel_unref(chan);
++      pending_connects = g_slist_append(pending_connects, c);
++ 
++      return c;
++ }
++ 
++ static int remote_svc_rec_conn_cb(struct transaction_context *ctxt)
++ {
++      sdp_list_t *attrids;
++      uint32_t range = 0x0000ffff;
++      const char *dst;
++      uint32_t handle;
++ 
++      if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_cb, ctxt) < 0)
++              return -EINVAL;
++ 
++      dbus_message_get_args(ctxt->rq, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_UINT32, &handle,
++                      DBUS_TYPE_INVALID);
++ 
++      attrids = sdp_list_append(NULL, &range);
++      /*
++       * Create/send the search request and set the
++       * callback to indicate the request completion
++       */
++      if (sdp_service_attr_async(ctxt->session, handle,
++                              SDP_ATTR_REQ_RANGE, attrids) < 0) {
++              sdp_list_free(attrids, NULL);
++              return -sdp_get_error(ctxt->session);
++      }
++ 
++      sdp_list_free(attrids, NULL);
++ 
++      return 0;
++ }
++ 
++ static int remote_svc_rec_conn_xml_cb(struct transaction_context *ctxt)
++ {
++      sdp_list_t *attrids;
++      uint32_t range = 0x0000ffff;
++      const char *dst;
++      uint32_t handle;
++ 
++      if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_xml_cb, ctxt) < 0)
++              return -EINVAL;
++ 
++      dbus_message_get_args(ctxt->rq, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_UINT32, &handle,
++                      DBUS_TYPE_INVALID);
++ 
++      attrids = sdp_list_append(NULL, &range);
++      /* 
++       * Create/send the search request and set the
++       * callback to indicate the request completion
++       */
++      if (sdp_service_attr_async(ctxt->session, handle,
++                              SDP_ATTR_REQ_RANGE, attrids) < 0) {
++              sdp_list_free(attrids, NULL);
++              return -sdp_get_error(ctxt->session);
++      }
++ 
++      sdp_list_free(attrids, NULL);
++ 
++      return 0;
++ }
++ 
+++static void service_search_channel_completed_cb(uint8_t type, uint16_t err,
+++                                             uint8_t *rsp, size_t size,
+++                                             void *udata)
+++{
+++     struct transaction_context *ctxt = udata;
+++     sdp_record_t *rec;
+++     DBusMessage *reply;
+++     uint8_t dtd = 0;
+++     int channel = 0;
+++     int scanned, extracted = 0, len = 0, recsize = 0, bytesleft = size;
+++
+++     if (!ctxt)
+++             return;
+++
+++     if (err == 0xffff) {
+++             /* Check for protocol error or I/O error */
+++             int sdp_err = sdp_get_error(ctxt->session);
+++             if (sdp_err < 0) {
+++                     error("search failed: Invalid session!");
+++                     error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);
+++                     goto failed;
+++             }
+++
+++             error("search failed: %s (%d)", strerror(sdp_err), sdp_err);
+++             error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);
+++             goto failed;
+++     }
+++
+++     if (type == SDP_ERROR_RSP) {
+++             error_sdp_failed(ctxt->conn, ctxt->rq, err);
+++             goto failed;
+++     }
+++
+++     /* Check response PDU ID */
+++     if (type != SDP_SVC_SEARCH_ATTR_RSP) {
+++             error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);
+++             error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);
+++             goto failed;
+++     }
+++
+++     scanned = sdp_extract_seqtype_safe(rsp, bytesleft, &dtd, &len);
+++     rsp += scanned;
+++     bytesleft -= scanned;
+++     for (; extracted < len; rsp += recsize, extracted += recsize, bytesleft -= recsize) {
+++             sdp_record_t *rec;
+++             sdp_data_t *d;
+++             sdp_list_t *protos;
+++             uuid_t* recv_uuid;
+++             int chan;
+++
+++             recsize = 0;
+++             rec = sdp_extract_pdu_safe(rsp, bytesleft, &recsize);
+++             if (!rec)
+++                     break;
+++
+++             sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, recsize);
+++
+++             d = sdp_data_get(rec, SDP_ATTR_SVCLASS_ID_LIST);
+++             if (!d) {
+++                     sdp_record_free(rec);
+++                     continue;
+++             }
+++
+++             recv_uuid = extract_service_class_uuid(d);
+++             if (recv_uuid == NULL) {
+++                     debug("recv_uuid is null\n");
+++                     sdp_record_free(rec);
+++                     continue;
+++             }
+++
+++             debug("recv_uuid = %#X\n", recv_uuid->value);
+++
+++             if (recv_uuid->type == SDP_UUID32) {
+++                     /* should probably use a sdp_xxx_cmp function for this,
+++                      * but there isn't a simple one handy */
+++                     uint16_t recv_uuid16 = (uint16_t)recv_uuid->value.uuid32;;
+++                     if (recv_uuid16 != ctxt->uuid.value.uuid16) {
+++                             debug("uuid's don't match\n");
+++                             sdp_record_free(rec);
+++                             continue;
+++                     }
+++             } else if (sdp_uuid16_cmp(&ctxt->uuid, recv_uuid)) {
+++                     debug("uuid's don't match\n");
+++                     sdp_record_free(rec);
+++                     continue;
+++             }
+++
+++             if (sdp_get_access_protos(rec, &protos)) {
+++                     sdp_record_free(rec);
+++                     continue;
+++             }
+++
+++             channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+++
+++             debug("channel = %d\n", channel);
+++
+++             sdp_record_free(rec);
+++
+++             if (channel > 0) {
+++                     break;
+++             }
+++     }
+++
+++     if (channel <= 0) {
+++             channel = -1;
+++     }
+++
+++     reply = dbus_message_new_method_return(ctxt->rq);
+++     dbus_message_append_args(reply, DBUS_TYPE_INT32, &channel,
+++                                                      DBUS_TYPE_INVALID);
+++
+++done:
+++     dbus_connection_send(ctxt->conn, reply, NULL);
+++     dbus_message_unref(reply);
+++
+++failed:
+++     transaction_context_free(ctxt, TRUE);
+++}
+++
+++static int service_search_channel_cb(struct transaction_context *ctxt)
+++{
+++     const char *dst;
+++     sdp_list_t *attrids, *search;
+++     uint16_t uuid16;
+++     uint16_t attr_svclass = SDP_ATTR_SVCLASS_ID_LIST;
+++     uint16_t attr_proto = SDP_ATTR_PROTO_DESC_LIST;
+++     int ret = 0;
+++
+++    debug("%s\n", __FUNCTION__);
+++
+++     dbus_message_get_args(ctxt->rq, NULL,
+++                     DBUS_TYPE_STRING, &dst,
+++                     DBUS_TYPE_UINT16, &uuid16,
+++                     DBUS_TYPE_INVALID);
+++
+++     sdp_uuid16_create(&ctxt->uuid, uuid16);
+++
+++     search = sdp_list_append(0, &ctxt->uuid);
+++     attrids = sdp_list_append(NULL, &attr_svclass);
+++     attrids = sdp_list_append(attrids, &attr_proto);
+++
+++     if (sdp_set_notify(ctxt->session, service_search_channel_completed_cb,
+++                                        ctxt) < 0)
+++             return -EINVAL;
+++     /*
+++      * Create/send the search request and set the
+++      * callback to indicate the request completion
+++      */
+++     if (sdp_service_search_attr_async(ctxt->session, search,
+++                             SDP_ATTR_REQ_INDIVIDUAL, attrids) < 0)
+++             ret = -sdp_get_error(ctxt->session);
+++
+++     sdp_list_free(search, NULL);
+++     sdp_list_free(attrids, NULL);
+++
+++     return ret;
+++}
+++
+++DBusMessage *get_remote_svc_channel(DBusConnection *conn, DBusMessage *msg, void *data)
+++{
+++     struct adapter *adapter = data;
+++     const char *dst;
+++     uint16_t uuid16;
+++     int err;
+++     connect_cb_t *cb;
+++
+++     if (!adapter->up)
+++             return adapter_not_ready(msg);
+++
+++     if (!dbus_message_get_args(msg, NULL,
+++                     DBUS_TYPE_STRING, &dst,
+++                     DBUS_TYPE_UINT16, &uuid16,
+++                     DBUS_TYPE_INVALID))
+++             return invalid_args(msg);
+++
+++     if (find_pending_connect(dst))
+++             return in_progress(msg, "Service search in progress");
+++
+++     cb = service_search_channel_cb;
+++
+++     if (!connect_request(conn, msg, adapter->address,
+++                             dst, cb, &err)) {
+++             error("Search request failed: %s (%d)", strerror(err), err);
+++             return failed_strerror(msg, err);
+++     }
+++
+++     return NULL;
+++}
+++
++ DBusMessage *get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg,
++                              void *data, sdp_format_t format)
++ {
++      struct adapter *adapter = data;
++      const char *dst;
++      uint32_t handle;
++      int err;
++      connect_cb_t *cb;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_UINT32, &handle,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (find_pending_connect(dst))
++              return in_progress(msg, "Service search in progress");
++ 
++      cb = remote_svc_rec_conn_cb;
++      if (format == SDP_FORMAT_XML)
++              cb = remote_svc_rec_conn_xml_cb;
++ 
++      if (!connect_request(conn, msg, adapter->address,
++                              dst, cb, &err)) {
++              error("Search request failed: %s (%d)", strerror(err), err);
++              return failed_strerror(msg, err);
++      }
++ 
++      return NULL;
++ }
++ 
++ static int remote_svc_handles_conn_cb(struct transaction_context *ctxt)
++ {
++      sdp_list_t *search = NULL;
++      const char *dst, *svc;
++ 
++      if (sdp_set_notify(ctxt->session, remote_svc_handles_completed_cb, ctxt) < 0)
++              return -EINVAL;
++ 
++      dbus_message_get_args(ctxt->rq, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_STRING, &svc,
++                      DBUS_TYPE_INVALID);
++ 
++      if (strlen(svc) > 0)
++              bt_string2uuid(&ctxt->uuid, svc);
++      else
++              sdp_uuid16_create(&ctxt->uuid, PUBLIC_BROWSE_GROUP);
++ 
++      search = sdp_list_append(0, &ctxt->uuid);
++ 
++      /* Create/send the search request and set the callback to indicate the request completion */
++      if (sdp_service_search_async(ctxt->session, search, 64) < 0) {
++              error("send request failed: %s (%d)", strerror(errno), errno);
++              sdp_list_free(search, NULL);
++              return -sdp_get_error(ctxt->session);
++      }
++ 
++      sdp_list_free(search, NULL);
++ 
++      return 0;
++ }
++ 
++ static int remote_svc_identifiers_conn_cb(struct transaction_context *ctxt)
++ {
++      if (sdp_set_notify(ctxt->session,
++                      remote_svc_identifiers_completed_cb, ctxt) < 0)
++              return -EINVAL;
++ 
++      return service_search_attr(ctxt, PUBLIC_BROWSE_GROUP);
++ }
++ 
++ DBusMessage *get_remote_svc_handles(DBusConnection *conn,
++                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *dst, *svc;
++      int err;
++      uuid_t uuid;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_STRING, &svc,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (strlen(svc) > 0) {
++              /* Check if it is a service name string */
++              if (bt_string2uuid(&uuid, svc) < 0) {
++                      error("Invalid service class name");
++                      return invalid_args(msg);
++              }
++      }
++ 
++      if (find_pending_connect(dst))
++              return in_progress(msg, "Service search in progress");
++ 
++      if (!connect_request(conn, msg, adapter->address,
++                              dst, remote_svc_handles_conn_cb, &err)) {
++              error("Search request failed: %s (%d)", strerror(err), err);
++              return failed_strerror(msg, err);
++      }
++ 
++      return NULL;
++ }
++ 
++ DBusMessage *get_remote_svc_identifiers(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter = data;
++      const char *dst;
++      int err;
++ 
++      if (!adapter->up)
++              return adapter_not_ready(msg);
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &dst,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if (find_pending_connect(dst))
++              return in_progress(msg, "Service search in progress");
++ 
++      if (!connect_request(conn, msg, adapter->address,
++                              dst, remote_svc_identifiers_conn_cb, &err)) {
++              error("Search request failed: %s (%d)", strerror(err), err);
++              return failed_strerror(msg, err);
++      }
++ 
++      return NULL;
++ }
++ 
++ DBusMessage *finish_remote_svc_transact(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct cached_session *s;
++      const char *address;
++      struct adapter *adapter = data;
++      bdaddr_t sba, dba;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &address,
++                      DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      str2ba(adapter->address, &sba);
++      str2ba(address, &dba);
++ 
++      while ((s = get_cached_session(&sba, &dba))) {
++              sdp_close(s->session);
++              g_source_remove(s->timeout_id);
++              g_source_remove(s->io_id);
++              g_free(s);
++      }
++ 
++      return dbus_message_new_method_return(msg);
++ }
index 0000000,0000000,19ba626..1550f96
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,1112 +1,1113 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2006-2007  Nokia Corporation
++  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
++  *  Copyright (C) 2005-2007  Johan Hedberg <johan.hedberg@nokia.com>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <stdlib.h>
++ #include <sys/param.h>
++ #include <sys/socket.h>
++ #include <sys/ioctl.h>
+++#include <limits.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/sdp.h>
++ 
++ #include <glib.h>
++ #include <dbus/dbus.h>
++ #include <gdbus.h>
++ 
++ #include "adapter.h"
++ #include "manager.h"
++ #include "hcid.h"
++ #include "dbus-common.h"
++ #include "dbus-service.h"
++ #include "error.h"
++ #include "dbus-security.h"
++ #include "dbus-hci.h"
++ 
++ #define REQUEST_TIMEOUT (60 * 1000)          /* 60 seconds */
++ #define AGENT_TIMEOUT (10 * 60 * 1000)               /* 10 minutes */
++ 
++ struct passkey_agent {
++      struct adapter *adapter;
++      DBusConnection *conn;
++      char *addr;
++      char *name;
++      char *path;
++      GSList *pending_requests;
++      int exited;
++      guint timeout;
++      guint listener_id;
++ };
++ 
++ struct pending_agent_request {
++      struct passkey_agent *agent;
++      int dev;
++      bdaddr_t sba;
++      bdaddr_t bda;
++      char *path;
++      DBusPendingCall *call;
++      int old_if;
++      char *pin;
++ };
++ 
++ struct authorization_agent {
++      DBusConnection *conn;
++      char *name;
++      char *path;
++      GSList *pending_requests;
++      guint listener_id;
++ };
++ 
++ struct auth_agent_req {
++      struct authorization_agent *agent;
++      char *adapter_path;
++      char *address;
++      char *service_path;
++      char *uuid;
++      service_auth_cb cb;
++      void *user_data;
++      DBusPendingCall *call;
++ };
++ 
++ static struct passkey_agent *default_agent = NULL;
++ static struct authorization_agent *default_auth_agent = NULL;
++ 
++ static void release_agent(struct passkey_agent *agent);
++ static void send_cancel_request(struct pending_agent_request *req);
++ 
++ static void passkey_agent_free(struct passkey_agent *agent)
++ {
++      GSList *l;
++ 
++      if (!agent)
++              return;
++ 
++      for (l = agent->pending_requests; l != NULL; l = l->next) {
++              struct pending_agent_request *req = l->data;
++              struct adapter *adapter = manager_find_adapter(&req->sba);
++ 
++              hci_send_cmd(req->dev, OGF_LINK_CTL,
++                              OCF_PIN_CODE_NEG_REPLY, 6, &req->bda);
++ 
++              if (adapter)
++                      adapter_auth_request_replied(adapter, &req->bda);
++ 
++              send_cancel_request(req);
++      }
++ 
++      if (agent->timeout)
++              g_source_remove(agent->timeout);
++ 
++      if (!agent->exited)
++              release_agent(agent);
++ 
++      g_free(agent->name);
++      g_free(agent->path);
++      g_free(agent->addr);
++ 
++      if (agent->conn)
++              dbus_connection_unref(agent->conn);
++ 
++      g_slist_free(agent->pending_requests);
++ 
++      g_free(agent);
++ }
++ 
++ static void agent_exited(void *user_data)
++ {
++      struct passkey_agent *agent = user_data;
++      struct adapter *adapter = agent->adapter;
++ 
++      debug("Passkey agent exited without calling Unregister");
++ 
++      agent->exited = 1;
++ 
++      adapter->passkey_agents = g_slist_remove(adapter->passkey_agents, agent);
++      passkey_agent_free(agent);
++ }
++ 
++ static gboolean agent_timeout(struct passkey_agent *agent)
++ {
++      struct adapter *adapter = agent->adapter;
++ 
++      debug("Passkey Agent at %s, %s timed out", agent->name, agent->path);
++ 
++      if (adapter)
++              adapter->passkey_agents = g_slist_remove(adapter->passkey_agents, agent);
++ 
++      agent->timeout = 0;
++ 
++      passkey_agent_free(agent);
++ 
++      return FALSE;
++ }
++ 
++ static void default_agent_exited(void *data)
++ {
++      debug("D-Bus client exited without unregistering the"
++                      " default passkey agent");
++ 
++      default_agent->exited = 1;
++ 
++      passkey_agent_free(default_agent);
++      default_agent = NULL;
++ }
++ 
++ static struct passkey_agent *passkey_agent_new(struct adapter *adapter, DBusConnection *conn,
++                                              const char *name, const char *path,
++                                              const char *addr)
++ {
++      struct passkey_agent *agent;
++ 
++      agent = g_new0(struct passkey_agent, 1);
++ 
++      agent->adapter = adapter;
++ 
++      agent->name = g_strdup(name);
++      agent->path = g_strdup(path);
++ 
++      if (addr)
++              agent->addr = g_strdup(addr);
++ 
++      agent->conn = dbus_connection_ref(conn);
++ 
++      return agent;
++ }
++ 
++ static int agent_cmp(const struct passkey_agent *a, const struct passkey_agent *b)
++ {
++      int ret;
++ 
++      if (b->name) {
++              if (!a->name)
++                      return -1;
++              ret = strcmp(a->name, b->name);
++              if (ret)
++                      return ret;
++      }
++ 
++      if (b->path) {
++              if (!a->path)
++                      return -1;
++              ret = strcmp(a->path, b->path);
++              if (ret)
++                      return ret;
++      }
++ 
++      if (b->addr) {
++              if (!a->addr)
++                      return -1;
++              ret = strcmp(a->addr, b->addr);
++              if (ret)
++                      return ret;
++      }
++ 
++      return 0;
++ }
++ 
++ static inline DBusMessage *invalid_args(DBusMessage *msg)
++ {
++      return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
++                      "Invalid arguments in method call");
++ }
++ 
++ static DBusMessage *register_passkey_agent(DBusConnection *conn,
++                                      DBusMessage *msg, void *data)
++ {
++      struct passkey_agent *agent, ref;
++      struct adapter *adapter;
++      const char *path, *addr;
++ 
++      if (!data) {
++              error("register_passkey_agent called without any adapter info!");
++              return NULL;
++      }
++ 
++      adapter = data;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_STRING, &addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      if ((check_address(addr) < 0) || (path[0] != '/'))
++              return invalid_args(msg);
++ 
++      memset(&ref, 0, sizeof(ref));
++ 
++      ref.name = (char *) dbus_message_get_sender(msg);
++      ref.addr = (char *) addr;
++      ref.path = (char *) path;
++ 
++      if (g_slist_find_custom(adapter->passkey_agents, &ref, (GCompareFunc) agent_cmp))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Passkey agent already exists");
++ 
++      agent = passkey_agent_new(adapter, conn, ref.name, path, addr);
++      if (!agent)
++              return NULL;
++ 
++      /* Only add a name listener if there isn't one already for this name */
++      ref.addr = NULL;
++      ref.path = NULL;
++      if (!g_slist_find_custom(adapter->passkey_agents, &ref,
++                                              (GCompareFunc) agent_cmp))
++              agent->listener_id = g_dbus_add_disconnect_watch(conn, ref.name,
++                                              agent_exited, agent, NULL);
++ 
++      agent->timeout = g_timeout_add(AGENT_TIMEOUT,
++                                      (GSourceFunc) agent_timeout, agent);
++ 
++      adapter->passkey_agents = g_slist_append(adapter->passkey_agents,                                                                               agent);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *unregister_passkey_agent(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      struct adapter *adapter;
++      GSList *match;
++      struct passkey_agent ref, *agent;
++      const char *path, *addr;
++ 
++      if (!data) {
++              error("unregister_passkey_agent called without any adapter info!");
++              return NULL;
++      }
++ 
++      adapter = data;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_STRING, &addr,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      memset(&ref, 0, sizeof(ref));
++ 
++      ref.name = (char *) dbus_message_get_sender(msg);
++      ref.path = (char *) path;
++      ref.addr = (char *) addr;
++ 
++      match = g_slist_find_custom(adapter->passkey_agents, &ref, (GCompareFunc) agent_cmp);
++      if (!match)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Passkey agent does not exist");
++ 
++      agent = match->data;
++ 
++      g_dbus_remove_watch(agent->conn, agent->listener_id);
++ 
++      adapter->passkey_agents = g_slist_remove(adapter->passkey_agents, agent);
++      agent->exited = 1;
++      passkey_agent_free(agent);
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static DBusMessage *register_default_passkey_agent(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      const char *path;
++ 
++      if (default_agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Passkey agent already exists");
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      default_agent = passkey_agent_new(NULL, conn, dbus_message_get_sender(msg),
++                                              path, NULL);
++      if (!default_agent)
++              goto need_memory;
++ 
++      default_agent->listener_id = g_dbus_add_disconnect_watch(conn,
++                                                      default_agent->name,
++                                                      default_agent_exited,
++                                                              NULL, NULL);
++ 
++      info("Default passkey agent (%s, %s) registered",
++                      default_agent->name, default_agent->path);
++ 
++      return dbus_message_new_method_return(msg);
++ 
++ need_memory:
++      if (default_agent) {
++              default_agent->exited = 1;
++              passkey_agent_free(default_agent);
++              default_agent = NULL;
++      }
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *unregister_default_passkey_agent(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      const char *path, *name;
++ 
++      if (!default_agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Passkey agent does not exist");
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      name = dbus_message_get_sender(msg);
++ 
++      if (strcmp(name, default_agent->name) || strcmp(path, default_agent->path))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Passkey agent does not exist");
++ 
++      g_dbus_remove_watch(default_agent->conn, default_agent->listener_id);
++ 
++      info("Default passkey agent (%s, %s) unregistered",
++                      default_agent->name, default_agent->path);
++ 
++      default_agent->exited = 1;
++      passkey_agent_free(default_agent);
++      default_agent = NULL;
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static struct auth_agent_req *auth_agent_req_new(struct authorization_agent *agent,
++                                              const char *adapter_path,
++                                              const char *address,
++                                              const char *service_path,
++                                              const char *uuid,
++                                              service_auth_cb cb,
++                                              void *user_data)
++ {
++      struct auth_agent_req *req;
++ 
++      req = g_new0(struct auth_agent_req, 1);
++ 
++      req->agent = agent;
++      req->adapter_path = g_strdup(adapter_path);
++      req->address = g_strdup(address);
++      req->service_path = g_strdup(service_path);
++      req->uuid = g_strdup(uuid);
++      req->cb = cb;
++      req->user_data = user_data;
++ 
++      return req;
++ }
++ 
++ static void auth_agent_req_free(struct auth_agent_req *req)
++ {
++      g_free(req->adapter_path);
++      g_free(req->address);
++      g_free(req->service_path);
++      g_free(req->uuid);
++      if (req->call)
++              dbus_pending_call_unref(req->call);
++      g_free(req);
++ }
++ 
++ static void auth_agent_req_cancel(struct auth_agent_req *req)
++ {
++      dbus_pending_call_cancel(req->call);
++ }
++ 
++ static void auth_agent_cancel_requests(struct authorization_agent *agent)
++ {
++      GSList *l;
++ 
++      for (l = agent->pending_requests; l != NULL; l = l->next) {
++              struct auth_agent_req *req = l->data;
++              auth_agent_req_cancel(req);
++              auth_agent_req_free(req);
++      }
++ }
++ 
++ static void auth_agent_call_cancel(struct auth_agent_req *req)
++ {
++      struct authorization_agent *agent = req->agent;
++      DBusMessage *message;
++ 
++      message = dbus_message_new_method_call(agent->name, agent->path,
++                              "org.bluez.AuthorizationAgent", "Cancel");
++      if (!message) {
++              error("Couldn't allocate D-Bus message");
++              return;
++      }
++ 
++      dbus_message_append_args(message,
++                              DBUS_TYPE_STRING, &req->adapter_path,
++                              DBUS_TYPE_STRING, &req->address,
++                              DBUS_TYPE_STRING, &req->service_path,
++                              DBUS_TYPE_STRING, &req->uuid,
++                              DBUS_TYPE_INVALID);
++ 
++      dbus_message_set_no_reply(message, TRUE);
++ 
++      dbus_connection_send(agent->conn, message, NULL);
++ 
++      dbus_message_unref(message);
++ }
++ 
++ static void auth_agent_free(struct authorization_agent *agent)
++ {
++      g_free(agent->name);
++      g_free(agent->path);
++      dbus_connection_unref(agent->conn);
++      g_slist_free(agent->pending_requests);
++      g_free(agent);
++ }
++ 
++ static struct authorization_agent *auth_agent_new(DBusConnection *conn,
++                                              const char *name,
++                                              const char *path)
++ {
++      struct authorization_agent *agent;
++ 
++      agent = g_new0(struct authorization_agent, 1);
++ 
++      agent->name = g_strdup(name);
++      agent->path = g_strdup(path);
++ 
++      agent->conn = dbus_connection_ref(conn);
++ 
++      return agent;
++ }
++ 
++ static void default_auth_agent_exited(void *data)
++ {
++      debug("D-Bus client exited without unregistering the "
++              "default authorization agent");
++ 
++      auth_agent_cancel_requests(default_auth_agent);
++      auth_agent_free(default_auth_agent);
++      default_auth_agent = NULL;
++ }
++ 
++ static void auth_agent_release(struct authorization_agent *agent)
++ {
++      DBusMessage *message;
++ 
++      debug("Releasing authorization agent %s, %s",
++              agent->name, agent->path);
++ 
++      message = dbus_message_new_method_call(agent->name, agent->path,
++                      "org.bluez.AuthorizationAgent", "Release");
++      if (!message) {
++              error("Couldn't allocate D-Bus message");
++              return;
++      }
++ 
++      dbus_message_set_no_reply(message, TRUE);
++ 
++      dbus_connection_send(agent->conn, message, NULL);
++ 
++      dbus_message_unref(message);
++ 
++      if (agent == default_auth_agent)
++              g_dbus_remove_watch(agent->conn, agent->listener_id);
++ }
++ 
++ static DBusMessage *register_default_auth_agent(DBusConnection *conn,
++                                              DBusMessage *msg,
++                                              void *data)
++ {
++      const char *path;
++ 
++      if (default_auth_agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".AlreadyExists",
++                              "Authorization agent already exists");
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      default_auth_agent = auth_agent_new(conn,
++                                      dbus_message_get_sender(msg), path);
++      if (!default_auth_agent)
++              goto need_memory;
++ 
++      default_auth_agent->listener_id = g_dbus_add_disconnect_watch(conn,
++                                              default_auth_agent->name,
++                                              default_auth_agent_exited,
++                                                              NULL, NULL);
++ 
++      info("Default authorization agent (%s, %s) registered",
++              default_auth_agent->name, default_auth_agent->path);
++ 
++      return dbus_message_new_method_return(msg);
++ 
++ need_memory:
++      if (default_auth_agent) {
++              auth_agent_free(default_auth_agent);
++              default_auth_agent = NULL;
++      }
++ 
++      return NULL;
++ }
++ 
++ static DBusMessage *unregister_default_auth_agent(DBusConnection *conn,
++                                                      DBusMessage *msg,
++                                                      void *data)
++ {
++      const char *path, *name;
++ 
++      if (!default_auth_agent)
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Authorization agent does not exist");
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_INVALID))
++              return invalid_args(msg);
++ 
++      name = dbus_message_get_sender(msg);
++ 
++      if (strcmp(name, default_auth_agent->name) ||
++              strcmp(path, default_auth_agent->path))
++              return g_dbus_create_error(msg,
++                              ERROR_INTERFACE ".DoesNotExist",
++                              "Authorization agent does not exist");
++ 
++      g_dbus_remove_watch(default_auth_agent->conn,
++                              default_auth_agent->listener_id);
++ 
++      info("Default authorization agent (%s, %s) unregistered",
++              default_auth_agent->name, default_auth_agent->path);
++ 
++      auth_agent_cancel_requests(default_auth_agent);
++      auth_agent_free(default_auth_agent);
++      default_auth_agent = NULL;
++ 
++      return dbus_message_new_method_return(msg);
++ }
++ 
++ static void auth_agent_req_reply(DBusPendingCall *call, void *data)
++ {
++      struct auth_agent_req *req = data;
++      DBusMessage *reply = dbus_pending_call_steal_reply(call);
++      DBusError err;
++ 
++      debug("authorize reply");
++ 
++      dbus_error_init(&err);
++      dbus_set_error_from_message(&err, reply);
++      req->cb(&err, req->user_data);
++ 
++      default_auth_agent->pending_requests =
++              g_slist_remove(default_auth_agent->pending_requests, req);
++      auth_agent_req_free(req);
++ 
++      debug("auth_agent_reply: returning");
++ }
++ 
++ static DBusPendingCall *auth_agent_call_authorize(struct authorization_agent *agent,
++                                              const char *adapter_path,
++                                              const char *service_path,
++                                              const char *address,
++                                              const char *uuid)
++ {
++      DBusMessage *message;
++      DBusPendingCall *call;
++ 
++      message = dbus_message_new_method_call(agent->name, agent->path,
++                              "org.bluez.AuthorizationAgent", "Authorize");
++      if (!message) {
++              error("Couldn't allocate D-Bus message");
++              return NULL;
++      }
++ 
++      dbus_message_append_args(message,
++                              DBUS_TYPE_STRING, &adapter_path,
++                              DBUS_TYPE_STRING, &address,
++                              DBUS_TYPE_STRING, &service_path,
++                              DBUS_TYPE_STRING, &uuid,
++                              DBUS_TYPE_INVALID);
++ 
++      if (dbus_connection_send_with_reply(agent->conn, message,
++                                      &call, REQUEST_TIMEOUT) == FALSE) {
++              error("D-Bus send failed");
++              dbus_message_unref(message);
++              return NULL;
++      }
++ 
++      dbus_message_unref(message);
++      return call;
++ }
++ 
++ int handle_authorize_request_old(struct service *service, const char *path,
++                              const char *address, const char *uuid,
++                              service_auth_cb cb, void *user_data)
++ {
++      struct auth_agent_req *req;
++ 
++      if (!default_auth_agent) {
++              debug("no default agent");
++              return -EPERM;
++      }
++ 
++      req = auth_agent_req_new(default_auth_agent, path,
++                              address, service->object_path,
++                              uuid, cb, user_data);
++ 
++      req->call = auth_agent_call_authorize(default_auth_agent, path,
++                                      service->object_path, address, uuid);
++      if (!req->call) {
++              auth_agent_req_free(req);
++              return -ENOMEM;
++      }
++ 
++      dbus_pending_call_set_notify(req->call, auth_agent_req_reply, req,
++                                      NULL);
++      default_auth_agent->pending_requests =
++              g_slist_append(default_auth_agent->pending_requests, req);
++ 
++      debug("authorize request was forwarded");
++ 
++      return 0;
++ }
++ 
++ static int auth_agent_send_cancel(struct authorization_agent *agent,
++                              const char *adapter_path,
++                              const char *address)
++ {
++      struct auth_agent_req *req = NULL;
++      GSList *l;
++ 
++      for (l = agent->pending_requests; l != NULL; l = l->next) {
++              req = l->data;
++              if (!strcmp(adapter_path, req->adapter_path) &&
++                              !strcmp(address, req->address))
++                      break;
++      }
++ 
++      if (!req)
++              return -EIO;
++ 
++      auth_agent_call_cancel(req);
++      auth_agent_req_cancel(req);
++      agent->pending_requests = g_slist_remove(agent->pending_requests, req);
++      auth_agent_req_free(req);
++ 
++      return 0;
++ }
++ 
++ int cancel_authorize_request_old(const char *path, const char *address)
++ {
++      if (!default_auth_agent)
++              return -EIO;
++ 
++      return auth_agent_send_cancel(default_auth_agent, path, address);
++ }
++ 
++ static GDBusMethodTable security_methods[] = {
++      { "RegisterDefaultPasskeyAgent",        "s",    "",
++                                      register_default_passkey_agent  },
++      { "UnregisterDefaultPasskeyAgent",      "s",    "",
++                                      unregister_default_passkey_agent},
++      { "RegisterPasskeyAgent",               "ss",   "",
++                                      register_passkey_agent          },
++      { "UnregisterPasskeyAgent",             "ss",   "",
++                                      unregister_passkey_agent        },
++      { "RegisterDefaultAuthorizationAgent",  "s",    "",
++                                      register_default_auth_agent     },
++      { "UnregisterDefaultAuthorizationAgent","s",    "",
++                                      unregister_default_auth_agent   },
++      { }
++ };
++ 
++ dbus_bool_t security_init(DBusConnection *conn, const char *path)
++ {
++      return g_dbus_register_interface(conn, path, SECURITY_INTERFACE,
++                      security_methods, NULL, NULL, NULL, NULL);
++ }
++ 
++ dbus_bool_t security_cleanup(DBusConnection *conn, const char *path)
++ {
++      return g_dbus_unregister_interface(conn, path, SECURITY_INTERFACE);
++ }
++ 
++ static DBusPendingCall *agent_request(const char *path, bdaddr_t *bda,
++                                      struct passkey_agent *agent,
++                                      dbus_bool_t numeric, int old_if)
++ {
++      DBusMessage *message;
++      DBusPendingCall *call;
++      char bda_str[18], *ptr = bda_str;
++ 
++      message = dbus_message_new_method_call(agent->name, agent->path,
++                                      "org.bluez.PasskeyAgent", "Request");
++      if (message == NULL) {
++              error("Couldn't allocate D-Bus message");
++              return NULL;
++      }
++ 
++      ba2str(bda, bda_str);
++ 
++      if (old_if)
++              dbus_message_append_args(message,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_STRING, &ptr,
++                              DBUS_TYPE_INVALID);
++      else
++              dbus_message_append_args(message,
++                              DBUS_TYPE_STRING, &path,
++                              DBUS_TYPE_STRING, &ptr,
++                              DBUS_TYPE_BOOLEAN, &numeric,
++                              DBUS_TYPE_INVALID);
++ 
++      if (dbus_connection_send_with_reply(agent->conn, message,
++                                      &call, REQUEST_TIMEOUT) == FALSE) {
++              error("D-Bus send failed");
++              dbus_message_unref(message);
++              return NULL;
++      }
++ 
++      dbus_message_unref(message);
++      return call;
++ }
++ 
++ static void passkey_agent_reply(DBusPendingCall *call, void *user_data)
++ {
++      struct pending_agent_request *req = user_data;
++      struct passkey_agent *agent = req->agent;
++      struct adapter *adapter = manager_find_adapter(&req->sba);
++      pin_code_reply_cp pr;
++      DBusMessage *message;
++      DBusError err;
++      size_t len;
++      char *pin;
++ 
++      /* steal_reply will always return non-NULL since the callback
++       * is only called after a reply has been received */
++      message = dbus_pending_call_steal_reply(call);
++ 
++      dbus_error_init(&err);
++      if (dbus_set_error_from_message(&err, message)) {
++              if (!req->old_if && !strcmp(err.name, DBUS_ERROR_UNKNOWN_METHOD)) {
++                      debug("New Request API failed, trying old one");
++                      req->old_if = 1;
++                      dbus_error_free(&err);
++                      dbus_pending_call_unref(req->call);
++                      req->call = agent_request(req->path, &req->bda, agent,
++                                                      FALSE, 1);
++                      if (!req->call)
++                              goto fail;
++ 
++                      dbus_message_unref(message);
++ 
++                      dbus_pending_call_set_notify(req->call,
++                                                      passkey_agent_reply,
++                                                      req, NULL);
++                      return;
++              }
++ 
++              error("Passkey agent replied with an error: %s, %s",
++                              err.name, err.message);
++ 
++              dbus_error_free(&err);
++              goto fail;
++      }
++ 
++      dbus_error_init(&err);
++      if (!dbus_message_get_args(message, &err,
++                              DBUS_TYPE_STRING, &pin,
++                              DBUS_TYPE_INVALID)) {
++              error("Wrong passkey reply signature: %s", err.message);
++              dbus_error_free(&err);
++              goto fail;
++      }
++ 
++      len = strlen(pin);
++ 
++      if (len > 16 || len < 1) {
++              error("Invalid passkey length from handler");
++              goto fail;
++      }
++ 
++      set_pin_length(&req->sba, len);
++ 
++      memset(&pr, 0, sizeof(pr));
++      bacpy(&pr.bdaddr, &req->bda);
++      memcpy(pr.pin_code, pin, len);
++      pr.pin_len = len;
++      hci_send_cmd(req->dev, OGF_LINK_CTL,
++                      OCF_PIN_CODE_REPLY, PIN_CODE_REPLY_CP_SIZE, &pr);
++ 
++      goto done;
++ 
++ fail:
++      hci_send_cmd(req->dev, OGF_LINK_CTL,
++                      OCF_PIN_CODE_NEG_REPLY, 6, &req->bda);
++ 
++ done:
++      if (adapter)
++              adapter_auth_request_replied(adapter, &req->bda);
++ 
++      if (message)
++              dbus_message_unref(message);
++ 
++      agent->pending_requests = g_slist_remove(agent->pending_requests, req);
++      dbus_pending_call_cancel(req->call);
++      if (req->call)
++              dbus_pending_call_unref(req->call);
++      g_free(req->path);
++      g_free(req);
++ 
++      if (agent != default_agent) {
++              agent->adapter->passkey_agents = g_slist_remove(agent->adapter->passkey_agents,
++                                                              agent);
++              passkey_agent_free(agent);
++      }
++ }
++ 
++ static int call_passkey_agent(DBusConnection *conn,
++                              struct passkey_agent *agent, int dev,
++                              const char *path, bdaddr_t *sba,
++                              bdaddr_t *dba)
++ {
++      struct pending_agent_request *req;
++      struct adapter *adapter = manager_find_adapter(sba);
++ 
++      if (!agent) {
++              debug("call_passkey_agent(): no agent available");
++              goto send;
++      }
++ 
++      debug("Calling PasskeyAgent.Request: name=%s, path=%s",
++                                              agent->name, agent->path);
++ 
++      req = g_new0(struct pending_agent_request, 1);
++      req->dev = dev;
++      bacpy(&req->sba, sba);
++      bacpy(&req->bda, dba);
++      req->agent = agent;
++      req->path = g_strdup(path);
++ 
++      req->call = agent_request(path, dba, agent, FALSE, 0);
++      if (!req->call)
++              goto failed;
++ 
++      dbus_pending_call_set_notify(req->call, passkey_agent_reply, req, NULL);
++ 
++      agent->pending_requests = g_slist_append(agent->pending_requests, req);
++ 
++      return 0;
++ 
++ failed:
++      g_free(req->path);
++      g_free(req);
++ 
++ send:
++      hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
++ 
++      if (adapter)
++              adapter_auth_request_replied(adapter, dba);
++ 
++      return -1;
++ }
++ 
++ int handle_passkey_request_old(DBusConnection *conn, int dev,
++                                              struct adapter *adapter,
++                                              bdaddr_t *sba, bdaddr_t *dba)
++ {
++      struct passkey_agent *agent = default_agent;
++      GSList *l;
++      char addr[18];
++ 
++      ba2str(dba, addr);
++ 
++      for (l = adapter->passkey_agents; l != NULL; l = l->next) {
++              struct passkey_agent *a = l->data;
++              if (a != default_agent && g_slist_length(a->pending_requests) >= 1)
++                      continue;
++              if (!strcmp(a->addr, addr)) {
++                      agent = a;
++                      break;
++              }
++      }
++ 
++      return call_passkey_agent(conn, agent, dev, adapter->path, sba, dba);
++ }
++ 
++ static void send_cancel_request(struct pending_agent_request *req)
++ {
++      DBusMessage *message;
++      char address[18], *ptr = address;
++ 
++      message = dbus_message_new_method_call(req->agent->name, req->agent->path,
++                      "org.bluez.PasskeyAgent", "Cancel");
++      if (message == NULL) {
++              error("Couldn't allocate D-Bus message");
++              return;
++      }
++ 
++      ba2str(&req->bda, address);
++ 
++      dbus_message_append_args(message,
++                      DBUS_TYPE_STRING, &req->path,
++                      DBUS_TYPE_STRING, &ptr,
++                      DBUS_TYPE_INVALID);
++ 
++      dbus_message_set_no_reply(message, TRUE);
++ 
++      dbus_connection_send(req->agent->conn, message, NULL);
++ 
++      dbus_message_unref(message);
++ 
++      debug("PasskeyAgent.Request(%s, %s) was canceled", req->path, address);
++ 
++      dbus_pending_call_cancel(req->call);
++      dbus_pending_call_unref(req->call);
++      g_free(req->pin);
++      g_free(req->path);
++      g_free(req);
++ }
++ 
++ static void release_agent(struct passkey_agent *agent)
++ {
++      DBusMessage *message;
++ 
++      debug("Releasing agent %s, %s", agent->name, agent->path);
++ 
++      message = dbus_message_new_method_call(agent->name, agent->path,
++                      "org.bluez.PasskeyAgent", "Release");
++      if (message == NULL) {
++              error("Couldn't allocate D-Bus message");
++              return;
++      }
++ 
++      dbus_message_set_no_reply(message, TRUE);
++ 
++      dbus_connection_send(agent->conn, message, NULL);
++ 
++      dbus_message_unref(message);
++ 
++      if (agent == default_agent)
++              g_dbus_remove_watch(agent->conn, agent->listener_id);
++      else {
++              struct passkey_agent ref;
++ 
++              /* Only remove the name listener if there are no more agents
++               * for this name */
++              memset(&ref, 0, sizeof(ref));
++              ref.name = agent->name;
++              if (!g_slist_find_custom(agent->adapter->passkey_agents, &ref,
++                                              (GCompareFunc) agent_cmp))
++                      g_dbus_remove_watch(agent->conn, agent->listener_id);
++      }
++ }
++ 
++ void release_default_agent_old(void)
++ {
++      if (!default_agent)
++              return;
++ 
++      passkey_agent_free(default_agent);
++      default_agent = NULL;
++ }
++ 
++ void release_default_auth_agent(void)
++ {
++      if (!default_auth_agent)
++              return;
++ 
++      auth_agent_cancel_requests(default_auth_agent);
++      auth_agent_release(default_auth_agent);
++ 
++      auth_agent_free(default_auth_agent);
++      default_auth_agent = NULL;
++ }
++ 
++ void release_passkey_agents(struct adapter *adapter, bdaddr_t *bda)
++ {
++      GSList *l, *next;
++ 
++      for (l = adapter->passkey_agents; l != NULL; l = next) {
++              struct passkey_agent *agent = l->data;
++              next = l->next;
++ 
++              if (bda && agent->addr) {
++                      bdaddr_t tmp;
++                      str2ba(agent->addr, &tmp);
++                      if (bacmp(&tmp, bda))
++                              continue;
++              }
++ 
++              adapter->passkey_agents = g_slist_remove(adapter->passkey_agents, agent);
++              passkey_agent_free(agent);
++      }
++ }
++ 
++ void cancel_passkey_agent_requests(GSList *agents, const char *path,
++                                      bdaddr_t *addr)
++ {
++      GSList *l, *next;
++ 
++      /* First check the default agent */
++      for (l = default_agent ? default_agent->pending_requests : NULL; l != NULL; l = next) {
++              struct pending_agent_request *req = l->data;
++              next = l->next;
++              if (!strcmp(path, req->path) && (!addr || !bacmp(addr, &req->bda))) {
++                      send_cancel_request(req);
++                      default_agent->pending_requests = g_slist_remove(default_agent->pending_requests,
++                                                                      req);
++              }
++      }
++ 
++      /* and then the adapter specific agents */
++      for (; agents != NULL; agents = agents->next) {
++              struct passkey_agent *agent = agents->data;
++ 
++              for (l = agent->pending_requests; l != NULL; l = next) {
++                      struct pending_agent_request *req = l->data;
++                      next = l->next;
++                      if (!strcmp(path, req->path) && (!addr || !bacmp(addr, &req->bda))) {
++                              send_cancel_request(req);
++                              agent->pending_requests = g_slist_remove(agent->pending_requests, req);
++                      }
++              }
++      }
++ }
index 0000000,0000000,f75f6c1..77b3912
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,971 +1,1007 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2000-2001  Qualcomm Incorporated
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <signal.h>
++ #include <sys/stat.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
++ #include <sys/types.h>
++ #include <sys/wait.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ 
++ #include <glib.h>
++ 
+++#ifdef ANDROID_EXPAND_NAME
+++#include <cutils/properties.h>
+++#endif
+++
++ #include <dbus/dbus.h>
++ 
++ #include "hcid.h"
++ #include "sdpd.h"
++ #include "adapter.h"
++ #include "dbus-common.h"
++ #include "dbus-service.h"
++ #include "dbus-database.h"
++ #include "dbus-hci.h"
++ #include "device.h"
++ #include "agent.h"
++ 
++ struct hcid_opts hcid;
++ struct device_opts default_device;
++ struct device_opts *parser_device;
++ static struct device_list *device_list = NULL;
++ static int child_pipe[2];
++ 
++ static GKeyFile *load_config(const char *file)
++ {
++      GError *err = NULL;
++      GKeyFile *keyfile;
++ 
++      keyfile = g_key_file_new();
++ 
++      if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
++              error("Parsing %s failed: %s", file, err->message);
++              g_error_free(err);
++              g_key_file_free(keyfile);
++              return NULL;
++      }
++ 
++      return keyfile;
++ }
++ 
++ static inline void init_device_defaults(struct device_opts *device_opts)
++ {
++      memset(device_opts, 0, sizeof(*device_opts));
++      device_opts->scan = SCAN_PAGE;
++      device_opts->mode = MODE_CONNECTABLE;
++      device_opts->name = g_strdup("BlueZ");
++      device_opts->discovto = HCID_DEFAULT_DISCOVERABLE_TIMEOUT;
++ }
++ 
++ struct device_opts *alloc_device_opts(char *ref)
++ {
++      struct device_list *device;
++ 
++      device = g_try_new(struct device_list, 1);
++      if (!device) {
++              info("Can't allocate devlist opts buffer: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      device->ref = g_strdup(ref);
++      device->next = device_list;
++      device_list = device;
++ 
++      memcpy(&device->opts, &default_device, sizeof(struct device_opts));
++      device->opts.name = g_strdup(default_device.name);
++ 
++      return &device->opts;
++ }
++ 
++ static void free_device_opts(void)
++ {
++      struct device_list *device, *next;
++ 
++      g_free(default_device.name);
++ 
++      for (device = device_list; device; device = next) {
++              g_free(device->ref);
++              g_free(device->opts.name);
++              next = device->next;
++              g_free(device);
++      }
++ 
++      device_list = NULL;
++ }
++ 
++ static inline struct device_opts *find_device_opts(char *ref)
++ {
++      struct device_list *device;
++ 
++      for (device = device_list; device; device = device->next)
++              if (!strcmp(ref, device->ref))
++                      return &device->opts;
++ 
++      return NULL;
++ }
++ 
++ static struct device_opts *get_device_opts(int hdev)
++ {
++      struct device_opts *device_opts = NULL;
++      struct hci_dev_info di;
++ 
++      /* First try to get BD_ADDR based settings ... */
++      if (hci_devinfo(hdev, &di) == 0) {
++              char addr[18];
++              ba2str(&di.bdaddr, addr);
++              device_opts = find_device_opts(addr);
++      }
++ 
++      /* ... then try HCI based settings ... */
++      if (!device_opts) {
++              char ref[8];
++              snprintf(ref, sizeof(ref) - 1, "hci%d", hdev);
++              device_opts = find_device_opts(ref);
++      }
++ 
++      /* ... and last use the default settings. */
++      if (!device_opts)
++              device_opts = &default_device;
++ 
++      return device_opts;
++ }
++ 
++ static struct device_opts *get_opts(int hdev)
++ {
++      struct device_opts *device_opts = NULL;
++      struct hci_dev_info di;
++      char addr[18];
++      int sock;
++ 
++      if (hdev < 0)
++              return NULL;
++ 
++      sock = hci_open_dev(hdev);
++      if (sock < 0)
++              goto no_address;
++ 
++      if (hci_devinfo(hdev, &di) < 0) {
++              close(sock);
++              goto no_address;
++      }
++ 
++      close(sock);
++ 
++      ba2str(&di.bdaddr, addr);
++      device_opts = find_device_opts(addr);
++ 
++ no_address:
++      if (!device_opts) {
++              char ref[8];
++              snprintf(ref, sizeof(ref) - 1, "hci%d", hdev);
++              device_opts = find_device_opts(ref);
++      }
++ 
++      if (!device_opts)
++              device_opts = &default_device;
++ 
++      return device_opts;
++ }
++ 
++ uint8_t get_startup_scan(int hdev)
++ {
++      struct device_opts *device_opts = get_opts(hdev);
++      if (!device_opts)
++              return SCAN_DISABLED;
++ 
++      return device_opts->scan;
++ }
++ 
++ uint8_t get_startup_mode(int hdev)
++ {
++      struct device_opts *device_opts = get_opts(hdev);
++      if (!device_opts)
++              return MODE_OFF;
++ 
++      return device_opts->mode;
++ }
++ 
++ int get_discoverable_timeout(int hdev)
++ {
++      struct device_opts *device_opts = NULL;
++      struct hci_dev_info di;
++      char addr[18];
++      int sock, timeout;
++ 
++      if (hdev < 0)
++              return HCID_DEFAULT_DISCOVERABLE_TIMEOUT;
++ 
++      sock = hci_open_dev(hdev);
++      if (sock < 0)
++              goto no_address;
++ 
++      if (hci_devinfo(hdev, &di) < 0) {
++              close(sock);
++              goto no_address;
++      }
++ 
++      close(sock);
++ 
++      if (read_discoverable_timeout(&di.bdaddr, &timeout) == 0)
++              return timeout;
++ 
++      ba2str(&di.bdaddr, addr);
++      device_opts = find_device_opts(addr);
++ 
++ no_address:
++      if (!device_opts) {
++              char ref[8];
++              snprintf(ref, sizeof(ref) - 1, "hci%d", hdev);
++              device_opts = find_device_opts(ref);
++      }
++ 
++      if (!device_opts)
++              device_opts = &default_device;
++ 
++      return device_opts->discovto;
++ }
++ 
++ void update_service_classes(const bdaddr_t *bdaddr, uint8_t value)
++ {
++      struct hci_dev_list_req *dl;
++      struct hci_dev_req *dr;
++      int i, sk;
++ 
++      sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
++      if (sk < 0)
++              return;
++ 
++      dl = g_malloc0(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
++ 
++      dl->dev_num = HCI_MAX_DEV;
++      dr = dl->dev_req;
++ 
++      if (ioctl(sk, HCIGETDEVLIST, dl) < 0) {
++              close(sk);
++              g_free(dl);
++              return;
++      }
++ 
++      dr = dl->dev_req;
++ 
++      for (i = 0; i < dl->dev_num; i++, dr++) {
++              struct hci_dev_info di;
++              uint8_t cls[3];
++              int dd;
++ 
++              if (hci_devinfo(dr->dev_id, &di) < 0)
++                      continue;
++ 
++              if (hci_test_bit(HCI_RAW, &di.flags))
++                      continue;
++ 
++              if (!hci_test_bit(HCI_UP, &di.flags))
++                      continue;
++ 
++              if (get_device_class(di.dev_id, cls) < 0)
++                      continue;
++ 
++              dd = hci_open_dev(di.dev_id);
++              if (dd < 0)
++                      continue;
++ 
++              set_service_classes(dd, cls, value);
++ 
++              hci_close_dev(dd);
++ 
++              update_adapter(di.dev_id);
++      }
++ 
++      g_free(dl);
++ 
++      close(sk);
++ }
++ 
++ /*
++  * Device name expansion
++  *   %d - device id
++  */
++ static char *expand_name(char *dst, int size, char *str, int dev_id)
++ {
++      register int sp, np, olen;
++      char *opt, buf[10];
++ 
+++#ifdef ANDROID_EXPAND_NAME
+++     char value[PROPERTY_VALUE_MAX];
+++#endif
+++
++      if (!str && !dst)
++              return NULL;
++ 
++      sp = np = 0;
++      while (np < size - 1 && str[sp]) {
++              switch (str[sp]) {
++              case '%':
++                      opt = NULL;
++ 
++                      switch (str[sp+1]) {
++                      case 'd':
++                              sprintf(buf, "%d", dev_id);
++                              opt = buf;
++                              break;
++ 
++                      case 'h':
++                              opt = hcid.host_name;
++                              break;
++ 
+++#ifdef ANDROID_EXPAND_NAME
+++                     case 'b':
+++                             property_get("ro.product.brand", value, "");
+++                             opt = value;
+++                             break;
+++
+++                     case 'm':
+++                             property_get("ro.product.model", value, "");
+++                             opt = value;
+++                             break;
+++
+++                     case 'n':
+++                             property_get("ro.product.name", value, "");
+++                             opt = value;
+++                             break;
+++#endif
+++
++                      case '%':
++                              dst[np++] = str[sp++];
++                              /* fall through */
++                      default:
++                              sp++;
++                              continue;
++                      }
++ 
++                      if (opt) {
++                              /* substitute */
++                              olen = strlen(opt);
++                              if (np + olen < size - 1)
++                                      memcpy(dst + np, opt, olen);
++                              np += olen;
++                      }
++                      sp += 2;
++                      continue;
++ 
++              case '\\':
++                      sp++;
++                      /* fall through */
++              default:
++                      dst[np++] = str[sp++];
++                      break;
++              }
++      }
++      dst[np] = '\0';
++      return dst;
++ }
++ 
++ static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
++ {
++      int status, fd = g_io_channel_unix_get_fd(io);
++      pid_t child_pid;
++ 
++      if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
++              error("child_exit: unable to read child pid from pipe");
++              return TRUE;
++      }
++ 
++      if (waitpid(child_pid, &status, 0) != child_pid)
++              error("waitpid(%d) failed", child_pid);
++      else
++              debug("child %d exited", child_pid);
++ 
++      return TRUE;
++ }
++ 
++ static void at_child_exit(void)
++ {
++      pid_t pid = getpid();
++ 
++      if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
++              error("unable to write to child pipe");
++ }
++ 
++ static void configure_device(int dev_id)
++ {
++      struct device_opts *device_opts;
++      struct hci_dev_req dr;
++      struct hci_dev_info di;
++      char mode[14];
++      int dd;
++ 
++      device_opts = get_device_opts(dev_id);
++ 
++      if (hci_devinfo(dev_id, &di) < 0)
++              return;
++ 
++      if (hci_test_bit(HCI_RAW, &di.flags))
++              return;
++ 
++      /* Set default discoverable timeout if not set */
++      if (!(device_opts->flags & (1 << HCID_SET_DISCOVTO)))
++              device_opts->discovto = HCID_DEFAULT_DISCOVERABLE_TIMEOUT;
++ 
++      /* Set scan mode */
++      if (read_device_mode(&di.bdaddr, mode, sizeof(mode)) == 0) {
++              if (!strcmp(mode, "off") && hcid.offmode == HCID_OFFMODE_NOSCAN) {
++                      device_opts->mode = MODE_OFF;
++                      device_opts->scan = SCAN_DISABLED;
++              } else if (!strcmp(mode, "connectable")) {
++                      device_opts->mode = MODE_CONNECTABLE;
++                      device_opts->scan = SCAN_PAGE;
++              } else if (!strcmp(mode, "discoverable")) {
++                      /* Set discoverable only if timeout is 0 */
++                      if (!get_discoverable_timeout(dev_id)) {
++                              device_opts->scan = SCAN_PAGE | SCAN_INQUIRY;
++                              device_opts->mode = MODE_DISCOVERABLE;
++                      } else {
++                              device_opts->scan = SCAN_PAGE;
++                              device_opts->mode = MODE_CONNECTABLE;
++                      }
++              } else if (!strcmp(mode, "limited")) {
++                      /* Set discoverable only if timeout is 0 */
++                      if (!get_discoverable_timeout(dev_id)) {
++                              device_opts->scan = SCAN_PAGE | SCAN_INQUIRY;
++                              device_opts->mode = MODE_LIMITED;
++                      } else {
++                              device_opts->scan = SCAN_PAGE;
++                              device_opts->mode = MODE_CONNECTABLE;
++                      }
++              }
++      }
++ 
++      /* Do configuration in the separate process */
++      switch (fork()) {
++              case 0:
++                      atexit(at_child_exit);
++                      break;
++              case -1:
++                      error("Fork failed. Can't init device hci%d: %s (%d)",
++                                              dev_id, strerror(errno), errno);
++              default:
++                      return;
++      }
++ 
++      dd = hci_open_dev(dev_id);
++      if (dd < 0) {
++              error("Can't open device hci%d: %s (%d)",
++                                              dev_id, strerror(errno), errno);
++              exit(1);
++      }
++ 
++      memset(&dr, 0, sizeof(dr));
++      dr.dev_id = dev_id;
++ 
++      /* Set packet type */
++      if ((device_opts->flags & (1 << HCID_SET_PTYPE))) {
++              dr.dev_opt = device_opts->pkt_type;
++              if (ioctl(dd, HCISETPTYPE, (unsigned long) &dr) < 0) {
++                      error("Can't set packet type on hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              }
++      }
++ 
++      /* Set link mode */
++      if ((device_opts->flags & (1 << HCID_SET_LM))) {
++              dr.dev_opt = device_opts->link_mode;
++              if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0) {
++                      error("Can't set link mode on hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              }
++      }
++ 
++      /* Set link policy */
++      if ((device_opts->flags & (1 << HCID_SET_LP))) {
++              dr.dev_opt = device_opts->link_policy;
++              if (ioctl(dd, HCISETLINKPOL, (unsigned long) &dr) < 0) {
++                      error("Can't set link policy on hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              }
++      }
++ 
++      /* Set device name */
++      if ((device_opts->flags & (1 << HCID_SET_NAME)) && device_opts->name) {
++              change_local_name_cp cp;
++ 
++              memset(cp.name, 0, sizeof(cp.name));
++              expand_name((char *) cp.name, sizeof(cp.name),
++                                              device_opts->name, dev_id);
++ 
++              hci_send_cmd(dd, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
++                                      CHANGE_LOCAL_NAME_CP_SIZE, &cp);
++      }
++ 
++      /* Set device class */
++      if ((device_opts->flags & (1 << HCID_SET_CLASS))) {
++              write_class_of_dev_cp cp;
++              uint32_t class;
++              uint8_t cls[3];
++ 
++              if (read_local_class(&di.bdaddr, cls) < 0) {
++                      class = htobl(device_opts->class);
++                      cls[2] = get_service_classes(&di.bdaddr);
++                      memcpy(cp.dev_class, &class, 3);
++              } else {
++                      if (!(device_opts->scan & SCAN_INQUIRY))
++                              cls[1] &= 0xdf; /* Clear discoverable bit */
++                      cls[2] = get_service_classes(&di.bdaddr);
++                      memcpy(cp.dev_class, cls, 3);
++              }
++ 
++              hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
++                                      WRITE_CLASS_OF_DEV_CP_SIZE, &cp);
++      }
++ 
++      /* Set page timeout */
++      if ((device_opts->flags & (1 << HCID_SET_PAGETO))) {
++              write_page_timeout_cp cp;
++ 
++              cp.timeout = htobs(device_opts->pageto);
++              hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
++                                      WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
++      }
++ 
++      /* Set voice setting */
++      if ((device_opts->flags & (1 << HCID_SET_VOICE))) {
++              write_voice_setting_cp cp;
++ 
++              cp.voice_setting = htobl(device_opts->voice);
++              hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING,
++                                      WRITE_VOICE_SETTING_CP_SIZE, &cp);
++      }
++ 
++      exit(0);
++ }
++ 
++ static void init_device(int dev_id)
++ {
++      struct hci_dev_info di;
++      int dd;
++ 
++      /* Do initialization in the separate process */
++      switch (fork()) {
++              case 0:
++                      atexit(at_child_exit);
++                      break;
++              case -1:
++                      error("Fork failed. Can't init device hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              default:
++                      return;
++      }
++ 
++      dd = hci_open_dev(dev_id);
++      if (dd < 0) {
++              error("Can't open device hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              exit(1);
++      }
++ 
++      /* Start HCI device */
++      if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
++              error("Can't init device hci%d: %s (%d)",
++                                      dev_id, strerror(errno), errno);
++              goto fail;
++      }
++ 
++      if (hci_devinfo(dev_id, &di) < 0)
++              goto fail;
++ 
++      if (hci_test_bit(HCI_RAW, &di.flags))
++              goto done;
++ 
++      if (hcid.offmode == HCID_OFFMODE_DEVDOWN) {
++              char mode[16];
++ 
++              if (read_device_mode(&di.bdaddr, mode, sizeof(mode)) == 0 &&
++                                              strcmp(mode, "off") == 0) {
++                      ioctl(dd, HCIDEVDOWN, dev_id);
++                      goto done;
++              }
++      }
++ 
++ done:
++      hci_close_dev(dd);
++      exit(0);
++ 
++ fail:
++      hci_close_dev(dd);
++      exit(1);
++ }
++ 
++ static void device_devreg_setup(int dev_id)
++ {
++      struct hci_dev_info di;
++ 
++      if (hcid.auto_init)
++              init_device(dev_id);
++ 
++      if (hci_devinfo(dev_id, &di) < 0)
++              return;
++ 
++      if (!hci_test_bit(HCI_RAW, &di.flags))
++              hcid_dbus_register_device(dev_id);
++ }
++ 
++ static void device_devup_setup(int dev_id)
++ {
++      add_adapter(dev_id);
++      if (hcid.auto_init)
++              configure_device(dev_id);
++      if (hcid.security)
++              start_security_manager(dev_id);
++      start_adapter(dev_id);
++      hcid_dbus_start_device(dev_id);
++ }
++ 
++ static void init_all_devices(int ctl)
++ {
++      struct hci_dev_list_req *dl;
++      struct hci_dev_req *dr;
++      int i;
++ 
++      dl = g_try_malloc0(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
++      if (!dl) {
++              info("Can't allocate devlist buffer: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      dl->dev_num = HCI_MAX_DEV;
++      dr = dl->dev_req;
++ 
++      if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
++              info("Can't get device list: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      for (i = 0; i < dl->dev_num; i++, dr++) {
++              info("HCI dev %d registered", dr->dev_id);
++              device_devreg_setup(dr->dev_id);
++              if (hci_test_bit(HCI_UP, &dr->dev_opt)) {
++                      info("HCI dev %d already up", dr->dev_id);
++                      device_devup_setup(dr->dev_id);
++              }
++      }
++ 
++      g_free(dl);
++ }
++ 
++ static void init_defaults(void)
++ {
++      hcid.auto_init = 1;
++      hcid.security  = HCID_SEC_AUTO;
++ 
++      init_device_defaults(&default_device);
++ }
++ 
++ static inline void device_event(GIOChannel *chan, evt_stack_internal *si)
++ {
++      evt_si_device *sd = (void *) &si->data;
++ 
++      switch (sd->event) {
++      case HCI_DEV_REG:
++              info("HCI dev %d registered", sd->dev_id);
++              device_devreg_setup(sd->dev_id);
++              break;
++ 
++      case HCI_DEV_UNREG:
++              info("HCI dev %d unregistered", sd->dev_id);
++              hcid_dbus_unregister_device(sd->dev_id);
++              remove_adapter(sd->dev_id);
++              break;
++ 
++      case HCI_DEV_UP:
++              info("HCI dev %d up", sd->dev_id);
++              device_devup_setup(sd->dev_id);
++              break;
++ 
++      case HCI_DEV_DOWN:
++              info("HCI dev %d down", sd->dev_id);
++              hcid_dbus_stop_device(sd->dev_id);
++              if (hcid.security)
++                      stop_security_manager(sd->dev_id);
++              stop_adapter(sd->dev_id);
++              break;
++      }
++ }
++ 
++ static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, gpointer data)
++ {
++      unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
++      evt_stack_internal *si;
++      hci_event_hdr *eh;
++      int type;
++      size_t len;
++      GIOError err;
++ 
++      ptr = buf;
++ 
++      if ((err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len))) {
++              if (err == G_IO_ERROR_AGAIN)
++                      return TRUE;
++ 
++              error("Read from control socket failed: %s (%d)",
++                                                      strerror(errno), errno);
++              return FALSE;
++      }
++ 
++      type = *ptr++;
++ 
++      if (type != HCI_EVENT_PKT)
++              return TRUE;
++ 
++      eh = (hci_event_hdr *) ptr;
++      if (eh->evt != EVT_STACK_INTERNAL)
++              return TRUE;
++ 
++      ptr += HCI_EVENT_HDR_SIZE;
++ 
++      si = (evt_stack_internal *) ptr;
++      switch (si->type) {
++      case EVT_SI_DEVICE:
++              device_event(chan, si);
++              break;
++      }
++ 
++      return TRUE;
++ }
++ 
++ static GMainLoop *event_loop;
++ 
++ static void sig_term(int sig)
++ {
++      g_main_loop_quit(event_loop);
++ }
++ 
++ static void sig_hup(int sig)
++ {
++      info("Reloading config file");
++ 
++      free_device_opts();
++ 
++      init_defaults();
++ 
++      if (read_config(hcid.config_file) < 0)
++              error("Config reload failed");
++ 
++      init_security_data();
++ 
++      init_all_devices(hcid.sock);
++ }
++ 
++ static void sig_debug(int sig)
++ {
++      toggle_debug();
++ }
++ 
++ static void usage(void)
++ {
++      printf("hcid - HCI daemon ver %s\n", VERSION);
++      printf("Usage: \n");
++      printf("\thcid [-n] [-d] [-m mtu] [-f config file]\n");
++ }
++ 
++ int main(int argc, char *argv[])
++ {
++      struct sockaddr_hci addr;
++      struct hci_filter flt;
++      struct sigaction sa;
++      GIOChannel *ctl_io, *child_io;
++      uint16_t mtu = 0;
++      int opt, daemonize = 1, debug = 0, sdp = 1, experimental = 0;
++      GKeyFile *config;
++ 
+++#ifdef ANDROID_SET_AID_AND_CAP
+++     /* Unfortunately Android's init.rc does not yet support applying
+++      * capabilities. So we must do it in-process. */
+++     void *android_set_aid_and_cap(void);
+++     android_set_aid_and_cap();
+++#endif
+++
++      /* Default HCId settings */
++      memset(&hcid, 0, sizeof(hcid));
++      hcid.auto_init   = 1;
++      hcid.config_file = HCID_CONFIG_FILE;
++      hcid.security    = HCID_SEC_AUTO;
++      hcid.pairing     = HCID_PAIRING_MULTI;
++      hcid.offmode     = HCID_OFFMODE_NOSCAN;
++ 
++      if (gethostname(hcid.host_name, sizeof(hcid.host_name) - 1) < 0)
++              strcpy(hcid.host_name, "noname");
++ 
++      strcpy((char *) hcid.pin_code, "BlueZ");
++      hcid.pin_len = 5;
++ 
++      init_defaults();
++ 
++      while ((opt = getopt(argc, argv, "ndsm:xf:")) != EOF) {
++              switch (opt) {
++              case 'n':
++                      daemonize = 0;
++                      break;
++ 
++              case 'd':
++                      debug = 1;
++                      break;
++ 
++              case 's':
++                      sdp = 1;
++                      break;
++ 
++              case 'm':
++                      mtu = atoi(optarg);
++                      break;
++ 
++              case 'x':
++                      experimental = 1;
++                      break;
++ 
++              case 'f':
++                      hcid.config_file = g_strdup(optarg);
++                      break;
++ 
++              default:
++                      usage();
++                      exit(1);
++              }
++      }
++ 
++      if (daemonize && daemon(0, 0)) {
++              error("Can't daemonize: %s (%d)", strerror(errno), errno);
++              exit(1);
++      }
++ 
++      umask(0077);
++ 
++      start_logging("hcid", "Bluetooth HCI daemon");
++ 
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_flags = SA_NOCLDSTOP;
++      sa.sa_handler = sig_term;
++      sigaction(SIGTERM, &sa, NULL);
++      sigaction(SIGINT,  &sa, NULL);
++      sa.sa_handler = sig_hup;
++      sigaction(SIGHUP, &sa, NULL);
++ 
++      sa.sa_handler = sig_debug;
++      sigaction(SIGUSR2, &sa, NULL);
++ 
++      sa.sa_handler = SIG_IGN;
++      sigaction(SIGPIPE, &sa, NULL);
++ 
++      if (debug) {
++              info("Enabling debug information");
++              enable_debug();
++      }
++ 
++      /* Create and bind HCI socket */
++      if ((hcid.sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
++              error("Can't open HCI socket: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      /* Set filter */
++      hci_filter_clear(&flt);
++      hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
++      hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
++      if (setsockopt(hcid.sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
++              error("Can't set filter: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      addr.hci_family = AF_BLUETOOTH;
++      addr.hci_dev = HCI_DEV_NONE;
++      if (bind(hcid.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              error("Can't bind HCI socket: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      config = load_config(CONFIGDIR "/main.conf");
++ 
++      if (read_config(hcid.config_file) < 0)
++              error("Config load failed");
++ 
++      if (pipe(child_pipe) < 0) {
++              error("pipe(): %s (%d)", strerror(errno), errno);
++              exit(1);
++      }
++ 
++      child_io = g_io_channel_unix_new(child_pipe[0]);
++      g_io_channel_set_close_on_unref(child_io, TRUE);
++      g_io_add_watch(child_io,
++                      G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
++                      child_exit, NULL);
++      g_io_channel_unref(child_io);
++ 
++      init_adapters();
++ 
++      agent_init();
++ 
++      if (experimental)
++              hcid_dbus_set_experimental();
++ 
++      if (hcid_dbus_init() < 0) {
++              error("Unable to get on D-Bus");
++              exit(1);
++      }
++ 
++      start_sdp_server(mtu, hcid.deviceid, SDP_SERVER_COMPAT);
++      set_service_classes_callback(update_service_classes);
++ 
++      /* Loading plugins has to be done after D-Bus has been setup since
++       * the plugins might wanna expose some paths on the bus. However the
++       * best order of how to init various subsystems of the Bluetooth
++       * daemon needs to be re-worked. */
+++#if 0
++      plugin_init(config);
+++#endif
++ 
++      init_security_data();
++ 
++      event_loop = g_main_loop_new(NULL, FALSE);
++ 
++      ctl_io = g_io_channel_unix_new(hcid.sock);
++      g_io_channel_set_close_on_unref(ctl_io, TRUE);
++ 
++      g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
++ 
++      g_io_channel_unref(ctl_io);
++ 
++      /* Initialize already connected devices */
++      init_all_devices(hcid.sock);
++ 
++      g_main_loop_run(event_loop);
++ 
++      hcid_dbus_unregister();
++ 
+++#if 0
++      plugin_cleanup();
+++#endif
++ 
++      stop_sdp_server();
++ 
++      free_device_opts();
++ 
++      agent_exit();
++ 
++      hcid_dbus_exit();
++ 
++      g_main_loop_unref(event_loop);
++ 
++      if (config)
++              g_key_file_free(config);
++ 
++      info("Exit");
++ 
++      stop_logging();
++ 
++      return 0;
++ }
index 0000000,0000000,0000000..07c7599
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,28 @@@@
+++BUILD_PAND:=1
+++ifeq ($(BUILD_PAND),1)
+++
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-utils)/common/ \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" \
+++     -DNEED_PPOLL
+++
+++LOCAL_SRC_FILES:= \
+++     bnep.c    \
+++     main.c    \
+++     sdp.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=pand
+++
+++include $(BUILD_EXECUTABLE)
+++endif
index 0000000,0000000,0000000..dc4b7b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,28 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" -DCONFIGDIR=\"/etc\" \
+++     -DNEED_PPOLL
+++
+++LOCAL_SRC_FILES:= \
+++     kword.c \
+++     lexer.c \
+++     main.c \
+++     parser.c 
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static 
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=rfcomm
+++
+++include $(BUILD_EXECUTABLE)
index 0000000,0000000,0000000..b81ca0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,25 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# libsdpserver
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common/ \
+++     $(call include-path-for, bluez-utils)/eglib/
+++
+++LOCAL_CFLAGS:=-DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     cstate.c \
+++     request.c \
+++     service.c \
+++     server.c \
+++     servicedb.c
+++
+++LOCAL_MODULE:=libsdpserver_static
+++
+++include $(BUILD_STATIC_LIBRARY)
index 0000000,0000000,1524d1c..af7b94a
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,280 +1,298 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2001-2002  Nokia Corporation
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <sys/stat.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/l2cap.h>
++ #include <bluetooth/sdp.h>
++ #include <bluetooth/sdp_lib.h>
++ 
++ #include <sys/un.h>
++ #include <netinet/in.h>
++ 
++ #include <glib.h>
++ 
++ #include "logging.h"
++ #include "sdpd.h"
++ 
+++#include <cutils/sockets.h>
+++
++ static GIOChannel *l2cap_io = NULL, *unix_io = NULL;
++ 
++ static int l2cap_sock, unix_sock;
++ 
++ /*
++  * SDP server initialization on startup includes creating the
++  * l2cap and unix sockets over which discovery and registration clients
++  * access us respectively
++  */
++ static int init_server(uint16_t mtu, int master, int compat)
++ {
++      struct l2cap_options opts;
++      struct sockaddr_l2 l2addr;
++      struct sockaddr_un unaddr;
++      socklen_t optlen;
+++     const char *key;
++ 
++      /* Register the public browse group root */
++      register_public_browse_group();
++ 
++      /* Register the SDP server's service record */
++      register_server_service();
++ 
++      /* Create L2CAP socket */
++      l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
++      if (l2cap_sock < 0) {
++              error("opening L2CAP socket: %s", strerror(errno));
++              return -1;
++      }
++ 
++      memset(&l2addr, 0, sizeof(l2addr));
++      l2addr.l2_family = AF_BLUETOOTH;
++      bacpy(&l2addr.l2_bdaddr, BDADDR_ANY);
++      l2addr.l2_psm = htobs(SDP_PSM);
++ 
++      if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
++              error("binding L2CAP socket: %s", strerror(errno));
++              return -1;
++      }
++ 
++      if (master) {
++              int opt = L2CAP_LM_MASTER;
++              if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
++                      error("setsockopt: %s", strerror(errno));
++                      return -1;
++              }
++      }
++ 
++      if (mtu > 0) {
++              memset(&opts, 0, sizeof(opts));
++              optlen = sizeof(opts);
++ 
++              if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
++                      error("getsockopt: %s", strerror(errno));
++                      return -1;
++              }
++ 
++              opts.omtu = mtu;
++              opts.imtu = mtu;
++ 
++              if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
++                      error("setsockopt: %s", strerror(errno));
++                      return -1;
++              }
++      }
++ 
++      listen(l2cap_sock, 5);
++ 
++      if (!compat) {
++              unix_sock = -1;
++              return 0;
++      }
++ 
+++#if 0
++      /* Create local Unix socket */
++      unix_sock = socket(PF_UNIX, SOCK_STREAM, 0);
++      if (unix_sock < 0) {
++              error("opening UNIX socket: %s", strerror(errno));
++              return -1;
++      }
++ 
++      memset(&unaddr, 0, sizeof(unaddr));
++      unaddr.sun_family = AF_UNIX;
++      strcpy(unaddr.sun_path, SDP_UNIX_PATH);
++ 
++      unlink(unaddr.sun_path);
++ 
++      if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) {
++              error("binding UNIX socket: %s", strerror(errno));
++              return -1;
++      }
++ 
++      listen(unix_sock, 5);
++ 
++      chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+++#else
+++     unix_sock = android_get_control_socket("bluetooth");
+++     if (unix_sock < 0) {
+++             error("Unable to get the control socket for 'bluetooth'");
+++             return -1;
+++     }
+++
+++     if (listen(unix_sock, 5)) {
+++             error("Listening on local socket failed: %s", strerror(errno));
+++             return -1;
+++     }
+++
+++     info("Got Unix socket fd '%d' from environment", unix_sock);
+++#endif
++ 
++      return 0;
++ }
++ 
++ static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data)
++ {
++      sdp_pdu_hdr_t hdr;
++      uint8_t *buf;
++      int sk, len, size;
++ 
++      if (cond & G_IO_NVAL)
++              return FALSE;
++ 
++      sk = g_io_channel_unix_get_fd(chan);
++ 
++      if (cond & (G_IO_HUP | G_IO_ERR)) {
++              sdp_svcdb_collect_all(sk);
++              return FALSE;
++      }
++ 
++      len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
++      if (len <= 0) {
++              sdp_svcdb_collect_all(sk);
++              return FALSE;
++      }
++ 
++      size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
++      buf = malloc(size);
++      if (!buf)
++              return TRUE;
++ 
++      len = recv(sk, buf, size, 0);
++      if (len <= 0) {
++              sdp_svcdb_collect_all(sk);
++              return FALSE;
++      }
++              
++      handle_request(sk, buf, len);
++ 
++      return TRUE;
++ }
++ 
++ static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
++ {
++      GIOChannel *io;
++      int nsk;
++ 
++      if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
++              g_io_channel_unref(chan);
++              return FALSE;
++      }
++ 
++      if (data == &l2cap_sock) {
++              struct sockaddr_l2 addr;
++              socklen_t len = sizeof(addr);
++ 
++              nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len);
++      } else if (data == &unix_sock) {
++              struct sockaddr_un addr;
++              socklen_t len = sizeof(addr);
++ 
++              nsk = accept(unix_sock, (struct sockaddr *) &addr, &len);
++      } else
++              return FALSE;
++ 
++      if (nsk < 0) {
++              error("Can't accept connection: %s", strerror(errno));
++              return TRUE;
++      }
++ 
++      io = g_io_channel_unix_new(nsk);
++      g_io_channel_set_close_on_unref(io, TRUE);
++ 
++      g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
++                                      io_session_event, data);
++ 
++      g_io_channel_unref(io);
++ 
++      return TRUE;
++ }
++ 
++ int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags)
++ {
++      int compat = flags & SDP_SERVER_COMPAT;
++      int master = flags & SDP_SERVER_MASTER;
++ 
++      info("Starting SDP server");
++ 
++      if (init_server(mtu, master, compat) < 0) {
++              error("Server initialization failed");
++              return -1;
++      }
++ 
++      if (did && strlen(did) > 0) {
++              const char *ptr = did;
++              uint16_t vid = 0x0000, pid = 0x0000, ver = 0x0000;
++ 
++              vid = (uint16_t) strtol(ptr, NULL, 16);
++              ptr = strchr(ptr, ':');
++              if (ptr) {
++                      pid = (uint16_t) strtol(ptr + 1, NULL, 16);
++                      ptr = strchr(ptr + 1, ':');
++                      if (ptr)
++                              ver = (uint16_t) strtol(ptr + 1, NULL, 16);
++                      register_device_id(vid, pid, ver);
++              }
++      }
++ 
++      l2cap_io = g_io_channel_unix_new(l2cap_sock);
++      g_io_channel_set_close_on_unref(l2cap_io, TRUE);
++ 
++      g_io_add_watch(l2cap_io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
++                                      io_accept_event, &l2cap_sock);
++ 
++      if (compat && unix_sock > fileno(stderr)) {
++              unix_io = g_io_channel_unix_new(unix_sock);
++              g_io_channel_set_close_on_unref(unix_io, TRUE);
++ 
++              g_io_add_watch(unix_io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
++                                      io_accept_event, &unix_sock);
++      }
++ 
++      return 0;
++ }
++ 
++ void stop_sdp_server(void)
++ {
++      info("Stopping SDP server");
++ 
++      sdp_svcdb_reset();
++ 
++      if (unix_io)
++              g_io_channel_unref(unix_io);
++ 
++      if (l2cap_io)
++              g_io_channel_unref(l2cap_io);
++ }
index 0000000,0000000,a1072cd..b7df153
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,658 +1,672 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2001-2002  Nokia Corporation
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <stdlib.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/sdp.h>
++ #include <bluetooth/sdp_lib.h>
++ 
++ #include <netinet/in.h>
++ 
++ #include "sdpd.h"
++ #include "logging.h"
++ 
++ static sdp_record_t *server = NULL;
++ 
++ static uint8_t service_classes = 0x00;
++ static service_classes_callback_t service_classes_callback = NULL;
++ 
++ static uint16_t did_vendor = 0x0000;
++ static uint16_t did_product = 0x0000;
++ static uint16_t did_version = 0x0000;
++ 
++ /*
++  * List of version numbers supported by the SDP server.
++  * Add to this list when newer versions are supported.
++  */
++ static sdp_version_t sdpVnumArray[1] = {
++      { 1, 0 }
++ };
++ static const int sdpServerVnumEntries = 1;
++ 
++ /*
++  * The service database state is an attribute of the service record
++  * of the SDP server itself. This attribute is guaranteed to
++  * change if any of the contents of the service repository
++  * changes. This function updates the timestamp of value of
++  * the svcDBState attribute
++  * Set the SDP server DB. Simply a timestamp which is the marker
++  * when the DB was modified.
++  */
++ static void update_db_timestamp(void)
++ {
++      uint32_t dbts = sdp_get_time();
++      sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts);
++      sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
++ }
++ 
++ static void update_svclass_list(void)
++ {
++      sdp_list_t *list = sdp_get_record_list();
++      uint8_t val = 0;
++ 
++      for (; list; list = list->next) {
++              sdp_record_t *rec = (sdp_record_t *) list->data;
++ 
++              if (rec->svclass.type != SDP_UUID16)
++                      continue;
++ 
++              switch (rec->svclass.value.uuid16) {
++              case DIALUP_NET_SVCLASS_ID:
++              case CIP_SVCLASS_ID:
++                      val |= 0x42;    /* Telephony & Networking */
++                      break;
++              case IRMC_SYNC_SVCLASS_ID:
++              case OBEX_OBJPUSH_SVCLASS_ID:
++              case OBEX_FILETRANS_SVCLASS_ID:
++              case IRMC_SYNC_CMD_SVCLASS_ID:
++              case PBAP_PSE_SVCLASS_ID:
++                      val |= 0x10;    /* Object Transfer */
++                      break;
++              case HEADSET_SVCLASS_ID:
++              case HANDSFREE_SVCLASS_ID:
++                      val |= 0x20;    /* Audio */
++                      break;
++              case CORDLESS_TELEPHONY_SVCLASS_ID:
++              case INTERCOM_SVCLASS_ID:
++              case FAX_SVCLASS_ID:
++              case SAP_SVCLASS_ID:
+++             case HEADSET_AGW_SVCLASS_ID:
+++             case HANDSFREE_AGW_SVCLASS_ID:
++                      val |= 0x40;    /* Telephony */
++                      break;
++              case AUDIO_SOURCE_SVCLASS_ID:
++              case VIDEO_SOURCE_SVCLASS_ID:
++                      val |= 0x08;    /* Capturing */
++                      break;
++              case AUDIO_SINK_SVCLASS_ID:
++              case VIDEO_SINK_SVCLASS_ID:
++                      val |= 0x04;    /* Rendering */
++                      break;
++              case PANU_SVCLASS_ID:
++              case NAP_SVCLASS_ID:
++              case GN_SVCLASS_ID:
++                      val |= 0x02;    /* Networking */
++                      break;
++              }
++      }
++ 
++      debug("Service classes 0x%02x", val);
++ 
++      service_classes = val;
++ 
++      if (service_classes_callback)
++              service_classes_callback(BDADDR_ANY, val);
++ }
++ 
++ uint8_t get_service_classes(const bdaddr_t *bdaddr)
++ {
++      return service_classes;
++ }
++ 
++ void set_service_classes_callback(service_classes_callback_t callback)
++ {
++      service_classes_callback = callback;
++ }
++ 
++ void create_ext_inquiry_response(const char *name, uint8_t *data)
++ {
++      sdp_list_t *list = sdp_get_record_list();
++      uint8_t *ptr = data;
++      uint16_t uuid[24];
++      int i, index = 0;
++ 
++      if (name) {
++              int len = strlen(name);
++ 
++              if (len > 48) {
++                      len = 48;
++                      ptr[1] = 0x08;
++              } else
++                      ptr[1] = 0x09;
++ 
++              ptr[0] = len + 1;
++ 
++              memcpy(ptr + 2, name, len);
++ 
++              ptr += len + 2;
++      }
++ 
++      if (did_vendor != 0x0000) {
++              uint16_t source = 0x0002;
++              *ptr++ = 9;
++              *ptr++ = 11;
++              *ptr++ = (source & 0x00ff);
++              *ptr++ = (source & 0xff00) >> 8;
++              *ptr++ = (did_vendor & 0x00ff);
++              *ptr++ = (did_vendor & 0xff00) >> 8;
++              *ptr++ = (did_product & 0x00ff);
++              *ptr++ = (did_product & 0xff00) >> 8;
++              *ptr++ = (did_version & 0x00ff);
++              *ptr++ = (did_version & 0xff00) >> 8;
++      }
++ 
++      ptr[1] = 0x03;
++ 
++      for (; list; list = list->next) {
++              sdp_record_t *rec = (sdp_record_t *) list->data;
++ 
++              if (rec->svclass.type != SDP_UUID16)
++                      continue;
++ 
++              if (rec->svclass.value.uuid16 < 0x1100)
++                      continue;
++ 
++              if (index > 23) {
++                      ptr[1] = 0x02;
++                      break;
++              }
++ 
++              for (i = 0; i < index; i++)
++                      if (uuid[i] == rec->svclass.value.uuid16)
++                              break;
++ 
++              if (i == index - 1)
++                      continue;
++ 
++              uuid[index++] = rec->svclass.value.uuid16;
++      }
++ 
++      if (index > 0) {
++              ptr[0] = (index * 2) + 1;
++              ptr += 2;
++ 
++              for (i = 0; i < index; i++) {
++                      *ptr++ = (uuid[i] & 0x00ff);
++                      *ptr++ = (uuid[i] & 0xff00) >> 8;
++              }
++      }
++ }
++ 
++ void register_public_browse_group(void)
++ {
++      sdp_list_t *browselist;
++      uuid_t bgscid, pbgid;
++      sdp_data_t *sdpdata;
++      sdp_record_t *browse = sdp_record_alloc();
++ 
++      browse->handle = SDP_SERVER_RECORD_HANDLE + 1;
++ 
++      sdp_record_add(BDADDR_ANY, browse);
++      sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle);
++      sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata);
++ 
++      sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID);
++      browselist = sdp_list_append(0, &bgscid);
++      sdp_set_service_classes(browse, browselist);
++      sdp_list_free(browselist, 0);
++ 
++      sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP);
++      sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID,
++                              SDP_UUID16, &pbgid.value.uuid16);
++ }
++ 
++ /*
++  * The SDP server must present its own service record to
++  * the service repository. This can be accessed by service
++  * discovery clients. This method constructs a service record
++  * and stores it in the repository
++  */
++ void register_server_service(void)
++ {
++      sdp_list_t *classIDList;
++      uuid_t classID;
++      void **versions, **versionDTDs;
++      uint8_t dtd;
++      sdp_data_t *pData;
++      int i;
++ 
++      server = sdp_record_alloc();
++      server->pattern = NULL;
++ 
++      /* Force the record to be SDP_SERVER_RECORD_HANDLE */
++      server->handle = SDP_SERVER_RECORD_HANDLE;
++ 
++      sdp_record_add(BDADDR_ANY, server);
++      sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE,
++                              sdp_data_alloc(SDP_UINT32, &server->handle));
++ 
++      sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID);
++      classIDList = sdp_list_append(0, &classID);
++      sdp_set_service_classes(server, classIDList);
++      sdp_list_free(classIDList, 0);
++ 
++      /*
++       * Set the version numbers supported, these are passed as arguments
++       * to the server on command line. Now defaults to 1.0
++       * Build the version number sequence first
++       */
++      versions = (void **)malloc(sdpServerVnumEntries * sizeof(void *));
++      versionDTDs = (void **)malloc(sdpServerVnumEntries * sizeof(void *));
++      dtd = SDP_UINT16;
++      for (i = 0; i < sdpServerVnumEntries; i++) {
++              uint16_t *version = malloc(sizeof(uint16_t));
++              *version = sdpVnumArray[i].major;
++              *version = (*version << 8);
++              *version |= sdpVnumArray[i].minor;
++              versions[i] = version;
++              versionDTDs[i] = &dtd;
++      }
++      pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries);
++      for (i = 0; i < sdpServerVnumEntries; i++)
++              free(versions[i]);
++      free(versions);
++      free(versionDTDs);
++      sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData);
++ 
++      update_db_timestamp();
++      update_svclass_list();
++ }
++ 
++ void register_device_id(const uint16_t vendor, const uint16_t product,
++                                              const uint16_t version)
++ {
++      const uint16_t spec = 0x0102, source = 0x0002;
++      const uint8_t primary = 1;
++      sdp_list_t *class_list, *group_list, *profile_list;
++      uuid_t class_uuid, group_uuid;
++      sdp_data_t *sdp_data, *primary_data, *source_data;
++      sdp_data_t *spec_data, *vendor_data, *product_data, *version_data;
++      sdp_profile_desc_t profile;
++      sdp_record_t *record = sdp_record_alloc();
++ 
++      info("Adding device id record for %04x:%04x", vendor, product);
++ 
++      did_vendor = vendor;
++      did_product = product;
++      did_version = version;
++ 
++      record->handle = sdp_next_handle();
++ 
++      sdp_record_add(BDADDR_ANY, record);
++      sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
++      sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
++ 
++      sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID);
++      class_list = sdp_list_append(0, &class_uuid);
++      sdp_set_service_classes(record, class_list);
++      sdp_list_free(class_list, NULL);
++ 
++      sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP);
++      group_list = sdp_list_append(NULL, &group_uuid);
++      sdp_set_browse_groups(record, group_list);
++      sdp_list_free(group_list, NULL);
++ 
++      sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID);
++      profile.version = spec;
++      profile_list = sdp_list_append(NULL, &profile);
++      sdp_set_profile_descs(record, profile_list);
++      sdp_list_free(profile_list, NULL);
++ 
++      spec_data = sdp_data_alloc(SDP_UINT16, &spec);
++      sdp_attr_add(record, 0x0200, spec_data);
++ 
++      vendor_data = sdp_data_alloc(SDP_UINT16, &vendor);
++      sdp_attr_add(record, 0x0201, vendor_data);
++ 
++      product_data = sdp_data_alloc(SDP_UINT16, &product);
++      sdp_attr_add(record, 0x0202, product_data);
++ 
++      version_data = sdp_data_alloc(SDP_UINT16, &version);
++      sdp_attr_add(record, 0x0203, version_data);
++ 
++      primary_data = sdp_data_alloc(SDP_BOOL, &primary);
++      sdp_attr_add(record, 0x0204, primary_data);
++ 
++      source_data = sdp_data_alloc(SDP_UINT16, &source);
++      sdp_attr_add(record, 0x0205, source_data);
++ 
++      update_db_timestamp();
++      update_svclass_list();
++ }
++ 
++ int add_record_to_server(bdaddr_t *src, sdp_record_t *rec)
++ {
++      sdp_data_t *data;
++ 
++      if (rec->handle == 0xffffffff) {
++              rec->handle = sdp_next_handle();
++              if (rec->handle < 0x10000)
++                      return -1;
++      } else {
++              if (sdp_record_find(rec->handle))
++                      return -1; 
++      }
++ 
++      debug("Adding record with handle 0x%05x", rec->handle);
++ 
++      sdp_record_add(src, rec);
++ 
++      data = sdp_data_alloc(SDP_UINT32, &rec->handle);
++      sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
++ 
++      if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
++              uuid_t uuid;
++              sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
++              sdp_pattern_add_uuid(rec, &uuid);
++      }
++ 
++      update_db_timestamp();
++      update_svclass_list();
++ 
++      return 0;
++ }
++ 
++ int remove_record_from_server(uint32_t handle)
++ {
++      sdp_record_t *rec;
++ 
++      debug("Removing record with handle 0x%05x", handle);
++ 
++      rec = sdp_record_find(handle);
++      if (!rec)
++              return -ENOENT;
++ 
++      if (sdp_record_remove(handle) == 0) {
++              update_db_timestamp();
++              update_svclass_list();
++      }
++ 
++      sdp_record_free(rec);
++ 
++      return 0;
++ }
++ 
++ // FIXME: refactor for server-side
++ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, int bufsize, uint32_t handleExpected, int *scanned)
++ {
++      int extractStatus = -1, localExtractedLength = 0;
++      uint8_t dtd;
++      int seqlen = 0;
++      sdp_record_t *rec = NULL;
++      uint16_t attrId, lookAheadAttrId;
++      sdp_data_t *pAttr = NULL;
++      uint32_t handle = 0xffffffff;
++ 
++      *scanned = sdp_extract_seqtype_safe(p, bufsize, &dtd, &seqlen);
++      p += *scanned;
++      bufsize -= *scanned;
++ 
++      if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) {
++              debug("Unexpected end of packet");
++              return NULL;
++      }
++ 
++      lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t))));
++ 
++      debug("Look ahead attr id : %d", lookAheadAttrId);
++ 
++      if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
++              if (bufsize < (sizeof(uint8_t) * 2) + sizeof(uint16_t) + sizeof(uint32_t)) {
++                      debug("Unexpected end of packet");
++                      return NULL;
++              }
++              handle = ntohl(bt_get_unaligned((uint32_t *) (p +
++                              sizeof(uint8_t) + sizeof(uint16_t) +
++                              sizeof(uint8_t))));
++              debug("SvcRecHandle : 0x%x", handle);
++              rec = sdp_record_find(handle);
++      } else if (handleExpected != 0xffffffff)
++              rec = sdp_record_find(handleExpected);
++ 
++      if (!rec) {
++              rec = sdp_record_alloc();
++              rec->attrlist = NULL;
++              if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
++                      rec->handle = handle;
++                      sdp_record_add(device, rec);
++              } else if (handleExpected != 0xffffffff) {
++                      rec->handle = handleExpected;
++                      sdp_record_add(device, rec);
++              }
++      } else {
++              sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
++              rec->attrlist = NULL;
++      }
++ 
++      while (localExtractedLength < seqlen) {
++              int attrSize = sizeof(uint8_t);
++              int attrValueLength = 0;
++ 
++              if (bufsize < attrSize + sizeof(uint16_t)) {
++                      debug("Unexpected end of packet: Terminating extraction of attributes");
++                      break;
++              }
++ 
++              debug("Extract PDU, sequenceLength: %d localExtractedLength: %d", seqlen, localExtractedLength);
++              dtd = *(uint8_t *) p;
++ 
++              attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize)));
++              attrSize += sizeof(uint16_t);
++              
++              debug("DTD of attrId : %d Attr id : 0x%x", dtd, attrId);
++ 
++              pAttr = sdp_extract_attr_safe(p + attrSize, bufsize - attrSize,
++                                                      &attrValueLength, rec);
++ 
++              debug("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength);
++ 
++              attrSize += attrValueLength;
++              if (pAttr == NULL) {
++                      debug("Terminating extraction of attributes");
++                      break;
++              }
++              localExtractedLength += attrSize;
++              p += attrSize;
++              bufsize -= attrSize;
++              sdp_attr_replace(rec, attrId, pAttr);
++              extractStatus = 0;
++              debug("Extract PDU, seqLength: %d localExtractedLength: %d",
++                                      seqlen, localExtractedLength);
++      }
++ 
++      if (extractStatus == 0) {
++              debug("Successful extracting of Svc Rec attributes");
++ #ifdef SDP_DEBUG
++              sdp_print_service_attr(rec->attrlist);
++ #endif
++              *scanned += seqlen;
++      }
++      return rec;
++ }
++ 
++ /*
++  * Add the newly created service record to the service repository
++  */
++ int service_register_req(sdp_req_t *req, sdp_buf_t *rsp)
++ {
++      int scanned = 0;
++      sdp_data_t *handle;
++      uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
++      int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
++      sdp_record_t *rec;
++ 
++      req->flags = *p++;
++      if (req->flags & SDP_DEVICE_RECORD) {
++              bacpy(&req->device, (bdaddr_t *) p);
++              p += sizeof(bdaddr_t);
++              bufsize -= sizeof(bdaddr_t);
++      }
++ 
++      // save image of PDU: we need it when clients request this attribute
++      rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned);
++      if (!rec)
++              goto invalid;
++ 
++      if (rec->handle == 0xffffffff) {
++              rec->handle = sdp_next_handle();
++              if (rec->handle < 0x10000) {
++                      sdp_record_free(rec);
++                      goto invalid;
++              }
++      } else {
++              if (sdp_record_find(rec->handle)) {
+++                     /* This is a bug: extract_pdu_server() adds the
+++                      * attribute, if it is missing, so we are guaranteed
+++                      * to find it here.  Instead of freeing it, we just
+++                      * return success, skipping over the code below that
+++                      * adds the attribute, in order to avoid adding that
+++                      * attribute twice.
+++                      */
+++#if 0
++                      sdp_record_free(rec);
++                      goto invalid;
+++#else
+++                     goto success;
+++#endif
++              }
++      }
++ 
++      sdp_record_add(&req->device, rec);
++      if (!(req->flags & SDP_RECORD_PERSIST))
++              sdp_svcdb_set_collectable(rec, req->sock);
++ 
++      handle = sdp_data_alloc(SDP_UINT32, &rec->handle);
++      sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle);
++ 
++      /*
++       * if the browse group descriptor is NULL,
++       * ensure that the record belongs to the ROOT group
++       */
++      if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
++               uuid_t uuid;
++               sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
++               sdp_pattern_add_uuid(rec, &uuid);
++      }
+++success:
++ 
++      update_db_timestamp();
++      update_svclass_list();
++ 
++      /* Build a rsp buffer */
++      bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data);
++      rsp->data_size = sizeof(uint32_t);
++ 
++      return 0;
++ 
++ invalid:
++      bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data);
++      rsp->data_size = sizeof(uint16_t);
++ 
++      return -1;
++ }
++ 
++ /*
++  * Update a service record
++  */
++ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
++ {
++      sdp_record_t *orec;
++      int status = 0, scanned = 0;
++      uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
++      int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
++      uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
++ 
++      debug("Svc Rec Handle: 0x%x", handle);
++ 
++      p += sizeof(uint32_t);
++      bufsize -= sizeof(uint32_t);
++ 
++      orec = sdp_record_find(handle);
++ 
++      debug("SvcRecOld: %p", orec);
++ 
++      if (orec) {
++              sdp_record_t *nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned);
++              if (nrec && handle == nrec->handle) {
++                      update_db_timestamp();
++                      update_svclass_list();
++              } else {
++                      debug("SvcRecHandle : 0x%x", handle);
++                      debug("SvcRecHandleNew : 0x%x", nrec->handle);
++                      debug("SvcRecNew : %p", nrec);
++                      debug("SvcRecOld : %p", orec);
++                      debug("Failure to update, restore old value");
++ 
++                      if (nrec)
++                              sdp_record_free(nrec);
++                      status = SDP_INVALID_SYNTAX;
++              }
++      } else
++              status = SDP_INVALID_RECORD_HANDLE;
++ 
++      p = rsp->data;
++      bt_put_unaligned(htons(status), (uint16_t *) p);
++      rsp->data_size = sizeof(uint16_t);
++      return status;
++ }
++ 
++ /*
++  * Remove a registered service record
++  */
++ int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp)
++ {
++      uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
++      uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
++      sdp_record_t *rec;
++      int status = 0;
++ 
++      /* extract service record handle */
++      p += sizeof(uint32_t);
++ 
++      rec = sdp_record_find(handle);
++      if (rec) {
++              sdp_svcdb_collect(rec);
++              status = sdp_record_remove(handle);
++              sdp_record_free(rec);
++              if (status == 0) {
++                      update_db_timestamp();
++                      update_svclass_list();
++              }
++      } else {
++              status = SDP_INVALID_RECORD_HANDLE;
++              debug("Could not find record : 0x%x", handle);
++      }
++ 
++      p = rsp->data;
++      bt_put_unaligned(htons(status), (uint16_t *) p);
++      rsp->data_size = sizeof(uint16_t);
++ 
++      return status;
++ }
index 0000000,0000000,0000000..4329ec5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,169 @@@@
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# hstest
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     hstest.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=hstest
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# l2test
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     l2test.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=l2test
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# rctest
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     rctest.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=rctest
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# passkey-agent
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, dbus)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     passkey-agent.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libdbus
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=passkey-agent
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# scotest
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     scotest.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=scotest
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# auth-agent
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, dbus)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     auth-agent.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libdbus
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=auth-agent
+++
+++include $(BUILD_EXECUTABLE)
+++
+++#
+++# attest
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     attest.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=attest
+++
+++include $(BUILD_EXECUTABLE)
index 0000000,0000000,a6ff75f..bc57d6b
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,1096 +1,1104 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2000-2001  Qualcomm Incorporated
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <ctype.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <getopt.h>
++ #include <syslog.h>
++ #include <signal.h>
++ #include <sys/time.h>
++ #include <sys/poll.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/l2cap.h>
++ 
++ #define NIBBLE_TO_ASCII(c)  ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
++ 
++ /* Test modes */
++ enum {
++      SEND,
++      RECV,
++      RECONNECT,
++      MULTY,
++      DUMP,
++      CONNECT,
++      CRECV,
++      LSEND,
++      SENDDUMP,
++      LSENDDUMP,
++      INFOREQ
++ };
++ 
++ static unsigned char *buf;
++ 
++ /* Default mtu */
++ static int imtu = 672;
++ static int omtu = 0;
++ 
++ /* Default data size */
++ static long data_size = -1;
++ 
++ /* Default addr and psm */
++ static bdaddr_t bdaddr;
++ static unsigned short psm = 10;
++ 
++ /* Default number of frames to send (-1 = infinite) */
++ static int num_frames = -1;
++ 
++ /* Default number of consecutive frames before the delay */
++ static int count = 1;
++ 
++ /* Default delay after sending count number of frames */
++ static unsigned long delay = 0;
++ 
++ static char *filename = NULL;
++ 
++ static int rfcmode = 0;
++ static int master = 0;
++ static int auth = 0;
++ static int encrypt = 0;
++ static int secure = 0;
++ static int socktype = SOCK_SEQPACKET;
++ static int linger = 0;
++ static int reliable = 0;
++ static int timestamp = 0;
++ 
++ static float tv2fl(struct timeval tv)
++ {
++      return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
++ }
++ 
++ static char *ltoh(unsigned long c, char* s)
++ {
++      int c1;
++ 
++      c1     = (c >> 28) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >> 24) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >> 20) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >> 16) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >> 12) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >>  8) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = (c >>  4) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = c & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      *s     = 0;
++      return s;
++ }
++ 
++ static char *ctoh(char c, char* s)
++ {
++      char c1;
++ 
++      c1     = (c >> 4) & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      c1     = c & 0x0f;
++      *(s++) = NIBBLE_TO_ASCII (c1);
++      *s     = 0;
++      return s;
++ }
++ 
++ static void hexdump(unsigned char *s, unsigned long l)
++ {
++      char bfr[80];
++      char *pb;
++      unsigned long i, n = 0;
++ 
++      if (l == 0)
++              return;
++ 
++      while (n < l) {
++              pb = bfr;
++              pb = ltoh (n, pb);
++              *(pb++) = ':';
++              *(pb++) = ' ';
++              for (i = 0; i < 16; i++) {
++                      if (n + i >= l) {
++                              *(pb++) = ' ';
++                              *(pb++) = ' ';
++                      } else
++                              pb = ctoh (*(s + i), pb);
++                      *(pb++) = ' ';
++              }
++              *(pb++) = ' ';
++              for (i = 0; i < 16; i++) {
++                      if (n + i >= l)
++                              break;
++                      else
++                              *(pb++) = (isprint (*(s + i)) ? *(s + i) : '.');
++              }
++              *pb = 0;
++              n += 16;
++              s += 16;
++              puts(bfr);
++      }
++ }
++ 
++ static int do_connect(char *svr)
++ {
++      struct sockaddr_l2 addr;
++      struct l2cap_options opts;
++      struct l2cap_conninfo conn;
++      socklen_t optlen;
++      int sk, opt;
++ 
++      /* Create socket */
++      sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
++      if (sk < 0) {
++              syslog(LOG_ERR, "Can't create socket: %s (%d)",
++                                                      strerror(errno), errno);
++              return -1;
++      }
++ 
++      /* Bind to local address */
++      memset(&addr, 0, sizeof(addr));
++      addr.l2_family = AF_BLUETOOTH;
++      bacpy(&addr.l2_bdaddr, &bdaddr);
++ 
++      if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              syslog(LOG_ERR, "Can't bind socket: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Get default options */
++      memset(&opts, 0, sizeof(opts));
++      optlen = sizeof(opts);
++ 
++      if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Set new options */
++      opts.omtu = omtu;
++      opts.imtu = imtu;
++      if (rfcmode > 0)
++              opts.mode = rfcmode;
++ 
++      if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
++              syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++ #if 0
++      /* Enable SO_TIMESTAMP */
++      if (timestamp) {
++              int t = 1;
++ 
++              if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
++                      syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++      }
++ #endif
++ 
++      /* Enable SO_LINGER */
++      if (linger) {
++              struct linger l = { .l_onoff = 1, .l_linger = linger };
++ 
++              if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
++                      syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++      }
++ 
++      /* Set link mode */
++      opt = 0;
++      if (reliable)
++              opt |= L2CAP_LM_RELIABLE;
+++     if (master)
+++             opt |= L2CAP_LM_MASTER;
+++     if (auth)
+++             opt |= L2CAP_LM_AUTH;
+++     if (encrypt)
+++             opt |= L2CAP_LM_ENCRYPT;
+++     if (secure)
+++             opt |= L2CAP_LM_SECURE;
++ 
++      if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
++              syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Connect to remote device */
++      memset(&addr, 0, sizeof(addr));
++      addr.l2_family = AF_BLUETOOTH;
++      str2ba(svr, &addr.l2_bdaddr);
++      addr.l2_psm = htobs(psm);
++ 
++      if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
++              syslog(LOG_ERR, "Can't connect: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Get current options */
++      memset(&opts, 0, sizeof(opts));
++      optlen = sizeof(opts);
++ 
++      if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Get connection information */
++      memset(&conn, 0, sizeof(conn));
++      optlen = sizeof(conn);
++ 
++      if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      syslog(LOG_INFO, "Connected [imtu %d, omtu %d, flush_to %d, "
++                              "mode %d, handle %d, class 0x%02x%02x%02x]",
++              opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
++              conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
++ 
++      if (data_size > opts.omtu)
++              data_size = opts.omtu;
++ 
++      return sk;
++ 
++ error:
++      close(sk);
++      return -1;
++ }
++ 
++ static void do_listen(void (*handler)(int sk))
++ {
++      struct sockaddr_l2 addr;
++      struct l2cap_options opts;
++      struct l2cap_conninfo conn;
++      socklen_t optlen;
++      int sk, nsk, opt;
++      char ba[18];
++ 
++      /* Create socket */
++      sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
++      if (sk < 0) {
++              syslog(LOG_ERR, "Can't create socket: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      /* Bind to local address */
++      addr.l2_family = AF_BLUETOOTH;
++      bacpy(&addr.l2_bdaddr, &bdaddr);
++      addr.l2_psm = htobs(psm);
++ 
++      if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              syslog(LOG_ERR, "Can't bind socket: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Set link mode */
++      opt = 0;
++      if (reliable)
++              opt |= L2CAP_LM_RELIABLE;
++      if (master)
++              opt |= L2CAP_LM_MASTER;
++      if (auth)
++              opt |= L2CAP_LM_AUTH;
++      if (encrypt)
++              opt |= L2CAP_LM_ENCRYPT;
++      if (secure)
++              opt |= L2CAP_LM_SECURE;
++ 
++      if (opt && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
++              syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Get default options */
++      memset(&opts, 0, sizeof(opts));
++      optlen = sizeof(opts);
++ 
++      if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Set new options */
++      opts.omtu = omtu;
++      opts.imtu = imtu;
++      if (rfcmode > 0)
++              opts.mode = rfcmode;
++ 
++      if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
++              syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      if (socktype == SOCK_DGRAM) {
++              handler(sk);
++              return;
++      }
++ 
++      /* Listen for connections */
++      if (listen(sk, 10)) {
++              syslog(LOG_ERR, "Can not listen on the socket: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Check for socket address */
++      memset(&addr, 0, sizeof(addr));
++      optlen = sizeof(addr);
++ 
++      if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get socket name: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      psm = btohs(addr.l2_psm);
++ 
++      syslog(LOG_INFO, "Waiting for connection on psm %d ...", psm);
++ 
++      while(1) {
++              memset(&addr, 0, sizeof(addr));
++              optlen = sizeof(addr);
++ 
++              nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
++              if (nsk < 0) {
++                      syslog(LOG_ERR, "Accept failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++              if (fork()) {
++                      /* Parent */
++                      close(nsk);
++                      continue;
++              }
++              /* Child */
++              close(sk);
++ 
++              /* Get current options */
++              memset(&opts, 0, sizeof(opts));
++              optlen = sizeof(opts);
++ 
++              if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
++                      syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
++                                                      strerror(errno), errno);
++                      close(nsk);
++                      goto error;
++              }
++ 
++              /* Get connection information */
++              memset(&conn, 0, sizeof(conn));
++              optlen = sizeof(conn);
++ 
++              if (getsockopt(nsk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
++                      syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
++                                                      strerror(errno), errno);
++                      close(nsk);
++                      goto error;
++              }
++ 
++              ba2str(&addr.l2_bdaddr, ba);
++              syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, flush_to %d, "
++                                      "mode %d, handle %d, class 0x%02x%02x%02x]",
++                      ba, opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
++                      conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
++ 
++ #if 0
++              /* Enable SO_TIMESTAMP */
++              if (timestamp) {
++                      int t = 1;
++ 
++                      if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
++                              syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
++                                                      strerror(errno), errno);
++                              goto error;
++                      }
++              }
++ #endif
++ 
++              /* Enable SO_LINGER */
++              if (linger) {
++                      struct linger l = { .l_onoff = 1, .l_linger = linger };
++ 
++                      if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
++                              syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
++                                                      strerror(errno), errno);
++                              close(nsk);
++                              goto error;
++                      }
++              }
++ 
++              handler(nsk);
++ 
++              syslog(LOG_INFO, "Disconnect: %m");
++              exit(0);
++      }
++ 
++      return;
++ 
++ error:
++      close(sk);
++      exit(1);
++ }
++ 
++ static void dump_mode(int sk)
++ {
++      socklen_t optlen;
++      int opt, len;
++ 
++      syslog(LOG_INFO, "Receiving ...");
++      while (1) {
++              fd_set rset;
++ 
++              FD_ZERO(&rset);
++              FD_SET(sk, &rset);
++ 
++              if (select(sk + 1, &rset, NULL, NULL, NULL) < 0)
++                      return;
++ 
++              if (!FD_ISSET(sk, &rset))
++                      continue;
++ 
++              len = read(sk, buf, data_size);
++              if (len <= 0) {
++                      if (len < 0) {
++                              if (reliable && (errno == ECOMM)) {
++                                      syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.");
++                                      optlen = sizeof(opt);
++                                      if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
++                                              syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
++                                                      strerror(errno), errno);
++                                              return;
++                                      }
++                                      continue;
++                              } else {
++                                      syslog(LOG_ERR, "Read error: %s(%d)",
++                                                      strerror(errno), errno);
++                              }
++                      }
++                      return;
++              }
++ 
++              syslog(LOG_INFO, "Recevied %d bytes", len);
++              hexdump(buf, len);
++      }
++ }
++ 
++ static void recv_mode(int sk)
++ {
++      struct timeval tv_beg, tv_end, tv_diff;
++      struct pollfd p;
++      char ts[30];
++      long total;
++      uint32_t seq;
++      socklen_t optlen;
++      int opt;
++ 
++      syslog(LOG_INFO, "Receiving ...");
++ 
++      memset(ts, 0, sizeof(ts));
++ 
++      p.fd = sk;
++      p.events = POLLIN | POLLERR | POLLHUP;
++ 
++      seq = 0;
++      while (1) {
++              gettimeofday(&tv_beg, NULL);
++              total = 0;
++              while (total < data_size) {
++                      uint32_t sq;
++                      uint16_t l;
++                      int i, len;
++ 
++                      p.revents = 0;
++                      if (poll(&p, 1, -1) <= 0)
++                              return;
++ 
++                      if (p.revents & (POLLERR | POLLHUP))
++                              return;
++ 
++                      len = recv(sk, buf, data_size, 0);
++                      if (len < 0) {
++                              if (reliable && (errno == ECOMM)) {
++                                      syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.\n");
++                                      optlen = sizeof(opt);
++                                      if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
++                                              syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
++                                                      strerror(errno), errno);
++                                              return;
++                                      }
++                                      continue;
++                              } else {
++                                      syslog(LOG_ERR, "Read failed: %s (%d)",
++                                              strerror(errno), errno);
++                              }
++                      }
++ 
++                      if (len < 6)
++                              break;
++ 
++                      if (timestamp) {
++                              struct timeval tv;
++ 
++                              if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
++                                      timestamp = 0;
++                                      memset(ts, 0, sizeof(ts));
++                              } else {
++                                      sprintf(ts, "[%ld.%ld] ",
++                                                      tv.tv_sec, tv.tv_usec);
++                              }
++                      }
++ 
++                      /* Check sequence */
++                      sq = btohl(*(uint32_t *) buf);
++                      if (seq != sq) {
++                              syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
++                              seq = sq;
++                      }
++                      seq++;
++ 
++                      /* Check length */
++                      l = btohs(*(uint16_t *) (buf + 4));
++                      if (len != l) {
++                              syslog(LOG_INFO, "size missmatch: %d -> %d", len, l);
++                              continue;
++                      }
++ 
++                      /* Verify data */
++                      for (i = 6; i < len; i++) {
++                              if (buf[i] != 0x7f)
++                                      syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
++                      }
++ 
++                      total += len;
++              }
++              gettimeofday(&tv_end, NULL);
++ 
++              timersub(&tv_end, &tv_beg, &tv_diff);
++ 
++              syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
++                      tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
++      }
++ }
++ 
++ static void do_send(int sk)
++ {
++      uint32_t seq;
++      int i, fd, len;
++ 
++      syslog(LOG_INFO, "Sending ...");
++ 
++      if (filename) {
++              fd = open(filename, O_RDONLY);
++              if (fd < 0) {
++                      syslog(LOG_ERR, "Open failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      exit(1);
++              }
++              len = read(fd, buf, data_size);
++              send(sk, buf, len, 0);
++              return;
++      } else {
++              for (i = 6; i < data_size; i++)
++                      buf[i] = 0x7f;
++      }
++ 
++      seq = 0;
++      while ((num_frames == -1) || (num_frames-- > 0)) {
++              *(uint32_t *) buf = htobl(seq);
++              *(uint16_t *) (buf + 4) = htobs(data_size);
++              seq++;
++ 
++              len = send(sk, buf, data_size, 0);
++              if (len < 0 || len != data_size) {
++                      syslog(LOG_ERR, "Send failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      exit(1);
++              }
++ 
++              if (num_frames && delay && count && !(seq % count))
++                      usleep(delay);
++      }
++ }
++ 
++ static void send_mode(int sk)
++ {
++      do_send(sk);
++ 
++      syslog(LOG_INFO, "Closing channel ...");
++      if (shutdown(sk, SHUT_RDWR) < 0)
++              syslog(LOG_INFO, "Close failed: %m");
++      else
++              syslog(LOG_INFO, "Done");
++ }
++ 
++ static void senddump_mode(int sk)
++ {
++      do_send(sk);
++ 
++      dump_mode(sk);
++ }
++ 
++ static void reconnect_mode(char *svr)
++ {
++      while (1) {
++              int sk = do_connect(svr);
++              close(sk);
++      }
++ }
++ 
++ static void connect_mode(char *svr)
++ {
++      struct pollfd p;
++      int sk;
++ 
++      if ((sk = do_connect(svr)) < 0)
++              exit(1);
++ 
++      p.fd = sk;
++      p.events = POLLERR | POLLHUP;
++ 
++      while (1) {
++              p.revents = 0;
++              if (poll(&p, 1, 500))
++                      break;
++      }
++ 
++      syslog(LOG_INFO, "Disconnected");
++ 
++      close(sk);
++ }
++ 
++ static void multi_connect_mode(int argc, char *argv[])
++ {
++      int i, n, sk;
++ 
++      while (1) {
++              for (n = 0; n < argc; n++) {
++                      for (i = 0; i < count; i++) {
++                              if (fork())
++                                      continue;
++ 
++                              /* Child */
++                              sk = do_connect(argv[n]);
++                              usleep(500);
++                              close(sk);
++                              exit(0);
++                      }
++              }
++              sleep(4);
++      }
++ }
++ 
++ static void info_request(char *svr)
++ {
++      unsigned char buf[48];
++      l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
++      l2cap_info_req *req = (l2cap_info_req *) (buf + L2CAP_CMD_HDR_SIZE);
++      l2cap_info_rsp *rsp = (l2cap_info_rsp *) (buf + L2CAP_CMD_HDR_SIZE);
++      uint16_t mtu;
++      uint32_t mask;
++      struct sockaddr_l2 addr;
++      int sk, err;
++ 
++      sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
++      if (sk < 0) {
++              perror("Can't create socket");
++              return;
++      }
++ 
++      memset(&addr, 0, sizeof(addr));
++      addr.l2_family = AF_BLUETOOTH;
++      bacpy(&addr.l2_bdaddr, &bdaddr);
++ 
++      if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              perror("Can't bind socket");
++              goto failed;
++      }
++ 
++      memset(&addr, 0, sizeof(addr));
++      addr.l2_family = AF_BLUETOOTH;
++      str2ba(svr, &addr.l2_bdaddr);
++ 
++      if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
++              perror("Can't connect socket");
++              goto failed;
++      }
++ 
++      memset(buf, 0, sizeof(buf));
++      cmd->code  = L2CAP_INFO_REQ;
++      cmd->ident = 42;
++      cmd->len   = htobs(2);
++      req->type  = htobs(0x0001);
++ 
++      if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
++              perror("Can't send info request");
++              goto failed;
++      }
++ 
++      err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 2, 0);
++      if (err < 0) {
++              perror("Can't receive info response");
++              goto failed;
++      }
++ 
++      switch (btohs(rsp->result)) {
++      case 0x0000:
++              mtu = btohs(bt_get_unaligned((uint16_t *) rsp->data));
++              printf("Connectionless MTU size is %d\n", mtu);
++              break;
++      case 0x0001:
++              printf("Connectionless MTU is not supported\n");
++              break;
++      }
++ 
++      memset(buf, 0, sizeof(buf));
++      cmd->code  = L2CAP_INFO_REQ;
++      cmd->ident = 42;
++      cmd->len   = htobs(2);
++      req->type  = htobs(0x0002);
++ 
++      if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
++              perror("Can't send info request");
++              goto failed;
++      }
++ 
++      err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 4, 0);
++      if (err < 0) {
++              perror("Can't receive info response");
++              goto failed;
++      }
++ 
++      switch (btohs(rsp->result)) {
++      case 0x0000:
++              mask = btohl(bt_get_unaligned((uint32_t *) rsp->data));
++              printf("Extended feature mask is 0x%04x\n", mask);
++              if (mask & 0x01)
++                      printf("  Flow control mode\n");
++              if (mask & 0x02)
++                      printf("  Retransmission mode\n");
++              if (mask & 0x04)
++                      printf("  Bi-directional QoS\n");
++              break;
++      case 0x0001:
++              printf("Extended feature mask is not supported\n");
++              break;
++      }
++ 
++ failed:
++      close(sk);
++ }
++ 
++ static void usage(void)
++ {
++      printf("l2test - L2CAP testing\n"
++              "Usage:\n");
++      printf("\tl2test <mode> [options] [bdaddr]\n");
++      printf("Modes:\n"
++              "\t-r listen and receive\n"
++              "\t-w listen and send\n"
++              "\t-d listen and dump incoming data\n"
++              "\t-x listen, then send, then dump incoming data\n"
++              "\t-s connect and send\n"
++              "\t-u connect and receive\n"
++              "\t-n connect and be silent\n"
++              "\t-y connect, then send, then dump incoming data\n"
++              "\t-c connect, disconnect, connect, ...\n"
++              "\t-m multiple connects\n"
++              "\t-z information request\n");
++ 
++      printf("Options:\n"
++              "\t[-b bytes] [-i device] [-P psm]\n"
++              "\t[-I imtu] [-O omtu]\n"
++              "\t[-L seconds] enable SO_LINGER\n"
++              "\t[-B filename] use data packets from file\n"
++              "\t[-N num] send num frames (default = infinite)\n"
++              "\t[-C num] send num frames before delay (default = 1)\n"
++              "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
++              "\t[-X mode] select retransmission/flow-control mode\n"
++              "\t[-R] reliable mode\n"
++              "\t[-G] use connectionless channel (datagram)\n"
++              "\t[-A] request authentication\n"
++              "\t[-E] request encryption\n"
++              "\t[-S] secure connection\n"
++              "\t[-M] become master\n"
++              "\t[-T] enable timestamps\n");
++ }
++ 
++ int main(int argc, char *argv[])
++ {
++      struct sigaction sa;
++      int opt, sk, mode = RECV, need_addr = 0;
++ 
++      bacpy(&bdaddr, BDADDR_ANY);
++ 
++      while ((opt=getopt(argc,argv,"rdscuwmnxyzb:i:P:I:O:B:N:L:C:D:X:RGAESMT")) != EOF) {
++              switch(opt) {
++              case 'r':
++                      mode = RECV;
++                      break;
++ 
++              case 's':
++                      mode = SEND;
++                      need_addr = 1;
++                      break;
++ 
++              case 'w':
++                      mode = LSEND;
++                      break;
++ 
++              case 'u':
++                      mode = CRECV;
++                      need_addr = 1;
++                      break;
++ 
++              case 'd':
++                      mode = DUMP;
++                      break;
++ 
++              case 'c':
++                      mode = RECONNECT;
++                      need_addr = 1;
++                      break;
++ 
++              case 'n':
++                      mode = CONNECT;
++                      need_addr = 1;
++                      break;
++ 
++              case 'm':
++                      mode = MULTY;
++                      need_addr = 1;
++                      break;
++ 
++              case 'x':
++                      mode = LSENDDUMP;
++                      break;
++ 
++              case 'y':
++                      mode = SENDDUMP;
++                      break;
++ 
++              case 'z':
++                      mode = INFOREQ;
++                      need_addr = 1;
++                      break;
++ 
++              case 'b':
++                      data_size = atoi(optarg);
++                      break;
++ 
++              case 'i':
++                      if (!strncasecmp(optarg, "hci", 3))
++                              hci_devba(atoi(optarg + 3), &bdaddr);
++                      else
++                              str2ba(optarg, &bdaddr);
++                      break;
++ 
++              case 'P':
++                      psm = atoi(optarg);
++                      break;
++ 
++              case 'I':
++                      imtu = atoi(optarg);
++                      break;
++ 
++              case 'O':
++                      omtu = atoi(optarg);
++                      break;
++ 
++              case 'L':
++                      linger = atoi(optarg);
++                      break;
++ 
++              case 'B':
++                      filename = strdup(optarg);
++                      break;
++ 
++              case 'N':
++                      num_frames = atoi(optarg);
++                      break;
++ 
++              case 'C':
++                      count = atoi(optarg);
++                      break;
++ 
++              case 'D':
++                      delay = atoi(optarg) * 1000;
++                      break;
++ 
++              case 'X':
++                      rfcmode = atoi(optarg);
++                      break;
++ 
++              case 'R':
++                      reliable = 1;
++                      break;
++ 
++              case 'M':
++                      master = 1;
++                      break;
++ 
++              case 'A':
++                      auth = 1;
++                      break;
++ 
++              case 'E':
++                      encrypt = 1;
++                      break;
++ 
++              case 'S':
++                      secure = 1;
++                      break;
++ 
++              case 'G':
++                      socktype = SOCK_DGRAM;
++                      break;
++ 
++              case 'T':
++                      timestamp = 1;
++                      break;
++ 
++              default:
++                      usage();
++                      exit(1);
++              }
++      }
++ 
++      if (need_addr && !(argc - optind)) {
++              usage();
++              exit(1);
++      }
++ 
++      if (data_size < 0) {
++              data_size = 48;
++              if (imtu > data_size)
++                      data_size = imtu;
++              if (omtu > data_size)
++                      data_size = omtu;
++      }
++ 
++      if (!(buf = malloc(data_size))) {
++              perror("Can't allocate data buffer");
++              exit(1);
++      }
++ 
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_handler = SIG_IGN;
++      sa.sa_flags   = SA_NOCLDSTOP;
++      sigaction(SIGCHLD, &sa, NULL);
++ 
++      openlog("l2test", LOG_PERROR | LOG_PID, LOG_LOCAL0);
++ 
++      switch (mode) {
++              case RECV:
++                      do_listen(recv_mode);
++                      break;
++ 
++              case CRECV:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      recv_mode(sk);
++                      break;
++ 
++              case DUMP:
++                      do_listen(dump_mode);
++                      break;
++ 
++              case SEND:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      send_mode(sk);
++                      break;
++ 
++              case LSEND:
++                      do_listen(send_mode);
++                      break;
++ 
++              case RECONNECT:
++                      reconnect_mode(argv[optind]);
++                      break;
++ 
++              case MULTY:
++                      multi_connect_mode(argc - optind, argv + optind);
++                      break;
++ 
++              case CONNECT:
++                      connect_mode(argv[optind]);
++                      break;
++ 
++              case SENDDUMP:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      senddump_mode(sk);
++                      break;
++ 
++              case LSENDDUMP:
++                      do_listen(senddump_mode);
++                      break;
++ 
++              case INFOREQ:
++                      info_request(argv[optind]);
++                      exit(0);
++      }
++ 
++      syslog(LOG_INFO, "Exit");
++ 
++      closelog();
++ 
++      return 0;
++ }
index 0000000,0000000,4a61266..8ac91d2
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,405 +1,418 @@@@
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <signal.h>
++ #include <getopt.h>
++ #include <string.h>
++ 
++ #include <dbus/dbus.h>
++ 
++ #define INTERFACE "org.bluez.Security"
++ 
++ static char *passkey = NULL;
++ static char *address = NULL;
++ 
+++static int do_reject = 0;
+++
++ static volatile sig_atomic_t __io_canceled = 0;
++ static volatile sig_atomic_t __io_terminated = 0;
++ 
++ static void sig_term(int sig)
++ {
++      __io_canceled = 1;
++ }
++ 
++ static DBusHandlerResult agent_filter(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      const char *name, *old, *new;
++ 
++      if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old,
++                              DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
++              fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++      }
++ 
++      if (!strcmp(name, "org.bluez") && *new == '\0') {
++              fprintf(stderr, "Passkey service has been terminated\n");
++              __io_terminated = 1;
++      }
++ 
++      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++ }
++ 
++ static DBusHandlerResult request_message(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      const char *path, *address;
++      dbus_bool_t numeric;
++ 
++      if (!passkey)
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &path, DBUS_TYPE_STRING, &address,
++                      DBUS_TYPE_BOOLEAN, &numeric, DBUS_TYPE_INVALID)) {
++              fprintf(stderr, "Invalid arguments for passkey Request method");
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++      }
++ 
+++     if (do_reject) {
+++             reply = dbus_message_new_error(msg,
+++                                     "org.bluez.Error.Rejected", "");
+++             goto send;
+++     }
+++
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              fprintf(stderr, "Can't create reply message\n");
++              return DBUS_HANDLER_RESULT_NEED_MEMORY;
++      }
++ 
++      printf("Passkey request for device %s\n", address);
++ 
++      dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey,
++                                      DBUS_TYPE_INVALID);
++ 
+++send:
++      dbus_connection_send(conn, reply, NULL);
++ 
++      dbus_connection_flush(conn);
++ 
++      dbus_message_unref(reply);
++ 
++      return DBUS_HANDLER_RESULT_HANDLED;
++ }
++ 
++ static DBusHandlerResult cancel_message(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++      const char *path, *address;
++ 
++      if (!dbus_message_get_args(msg, NULL,
++                      DBUS_TYPE_STRING, &path, DBUS_TYPE_STRING, &address,
++                                                      DBUS_TYPE_INVALID)) {
++              fprintf(stderr, "Invalid arguments for passkey Confirm method");
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++      }
++ 
++      printf("Request canceled for device %s\n", address);
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              fprintf(stderr, "Can't create reply message\n");
++              return DBUS_HANDLER_RESULT_NEED_MEMORY;
++      }
++ 
++      dbus_connection_send(conn, reply, NULL);
++ 
++      dbus_connection_flush(conn);
++ 
++      dbus_message_unref(reply);
++ 
++      return DBUS_HANDLER_RESULT_HANDLED;
++ }
++ 
++ static DBusHandlerResult release_message(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      DBusMessage *reply;
++ 
++      if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
++              fprintf(stderr, "Invalid arguments for passkey Release method");
++              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++      }
++ 
++      if (!__io_canceled)
++              fprintf(stderr, "Passkey service has been released\n");
++ 
++      __io_terminated = 1;
++ 
++      reply = dbus_message_new_method_return(msg);
++      if (!reply) {
++              fprintf(stderr, "Can't create reply message\n");
++              return DBUS_HANDLER_RESULT_NEED_MEMORY;
++      }
++ 
++      dbus_connection_send(conn, reply, NULL);
++ 
++      dbus_connection_flush(conn);
++ 
++      dbus_message_unref(reply);
++ 
++      return DBUS_HANDLER_RESULT_HANDLED;
++ }
++ 
++ static DBusHandlerResult agent_message(DBusConnection *conn,
++                                              DBusMessage *msg, void *data)
++ {
++      if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Request"))
++              return request_message(conn, msg, data);
++ 
++      if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Cancel"))
++              return cancel_message(conn, msg, data);
++ 
++      if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release"))
++              return release_message(conn, msg, data);
++ 
++      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++ }
++ 
++ static const DBusObjectPathVTable agent_table = {
++      .message_function = agent_message,
++ };
++ 
++ static int register_agent(DBusConnection *conn, const char *agent_path,
++                              const char *remote_address, int use_default)
++ {
++      DBusMessage *msg, *reply;
++      DBusError err;
++      const char *path, *method, *address = remote_address;
++ 
++      if (!dbus_connection_register_object_path(conn, agent_path,
++                                                      &agent_table, NULL)) {
++              fprintf(stderr, "Can't register object path for agent\n");
++              return -1;
++      }
++ 
++      if (use_default) {
++              path = "/org/bluez";
++              method = "RegisterDefaultPasskeyAgent";
++      } else {
++              path = "/org/bluez/hci0";
++              method = "RegisterPasskeyAgent";
++      }
++ 
++      msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
++      if (!msg) {
++              fprintf(stderr, "Can't allocate new method call\n");
++              return -1;
++      }
++ 
++      if (use_default)
++              dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
++                                                      DBUS_TYPE_INVALID);
++      else
++              dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
++                              DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
++ 
++      dbus_error_init(&err);
++ 
++      reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
++ 
++      dbus_message_unref(msg);
++ 
++      if (!reply) {
++              fprintf(stderr, "Can't register passkey agent\n");
++              if (dbus_error_is_set(&err)) {
++                      fprintf(stderr, "%s\n", err.message);
++                      dbus_error_free(&err);
++              }
++              return -1;
++      }
++ 
++      dbus_message_unref(reply);
++ 
++      dbus_connection_flush(conn);
++ 
++      return 0;
++ }
++ 
++ static int unregister_agent(DBusConnection *conn, const char *agent_path,
++                              const char *remote_address, int use_default)
++ {
++      DBusMessage *msg, *reply;
++      DBusError err;
++      const char *path, *method, *address = remote_address;
++ 
++      if (use_default) {
++              path = "/org/bluez";
++              method = "UnregisterDefaultPasskeyAgent";
++      } else {
++              path = "/org/bluez/hci0";
++              method = "UnregisterPasskeyAgent";
++      }
++ 
++      msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
++      if (!msg) {
++              fprintf(stderr, "Can't allocate new method call\n");
++              dbus_connection_unref(conn);
++              exit(1);
++      }
++ 
++      if (use_default)
++              dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
++                                                      DBUS_TYPE_INVALID);
++      else
++              dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
++                              DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
++ 
++      dbus_error_init(&err);
++ 
++      reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
++ 
++      dbus_message_unref(msg);
++ 
++      if (!reply) {
++              fprintf(stderr, "Can't unregister passkey agent\n");
++              if (dbus_error_is_set(&err)) {
++                      fprintf(stderr, "%s\n", err.message);
++                      dbus_error_free(&err);
++              }
++              return -1;
++      }
++ 
++      dbus_message_unref(reply);
++ 
++      dbus_connection_flush(conn);
++ 
++      dbus_connection_unregister_object_path(conn, agent_path);
++ 
++      return 0;
++ }
++ 
++ static void usage(void)
++ {
++      printf("Bluetooth passkey agent ver %s\n\n", VERSION);
++ 
++      printf("Usage:\n"
++              "\tpasskey-agent [--default] [--path agent-path] <passkey> [address]\n"
++              "\n");
++ }
++ 
++ static struct option main_options[] = {
++      { "default",    0, 0, 'd' },
+++     { "reject",     0, 0, 'r' },
++      { "path",       1, 0, 'p' },
++      { "help",       0, 0, 'h' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ int main(int argc, char *argv[])
++ {
++      struct sigaction sa;
++      DBusConnection *conn;
++      char match_string[128], default_path[128], *agent_path = NULL;
++      int opt, use_default = 0;
++ 
++      snprintf(default_path, sizeof(default_path),
++                              "/org/bluez/passkey_agent_%d", getpid());
++ 
++      while ((opt = getopt_long(argc, argv, "+dp:h", main_options, NULL)) != EOF) {
++              switch(opt) {
++              case 'd':
++                      use_default = 1;
++                      break;
+++             case 'r':
+++                     do_reject = 1;
+++                     break;
++              case 'p':
++                      if (optarg[0] != '/') {
++                              fprintf(stderr, "Invalid path\n");
++                              exit(1);
++                      }
++                      agent_path = strdup(optarg);
++                      break;
++              case 'h':
++                      usage();
++                      exit(0);
++              default:
++                      exit(1);
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++      optind = 0;
++ 
++      if (argc < 1) {
++              usage();
++              exit(1);
++      }
++ 
++      passkey = strdup(argv[0]);
++      address = (argc > 1) ? strdup(argv[1]) : NULL;
++ 
++      if (!use_default && !address) {
++              usage();
++              exit(1);
++      }
++ 
++      if (!agent_path)
++              agent_path = strdup(default_path);
++ 
++      conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
++      if (!conn) {
++              fprintf(stderr, "Can't get on system bus");
++              exit(1);
++      }
++ 
++      if (register_agent(conn, agent_path, address, use_default) < 0) {
++              dbus_connection_unref(conn);
++              exit(1);
++      }
++ 
++      if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
++              fprintf(stderr, "Can't add signal filter");
++ 
++      snprintf(match_string, sizeof(match_string),
++                      "interface=%s,member=NameOwnerChanged,arg0=%s",
++                                      DBUS_INTERFACE_DBUS, "org.bluez");
++ 
++      dbus_bus_add_match(conn, match_string, NULL);
++ 
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_flags   = SA_NOCLDSTOP;
++      sa.sa_handler = sig_term;
++      sigaction(SIGTERM, &sa, NULL);
++      sigaction(SIGINT,  &sa, NULL);
++ 
++      while (!__io_canceled && !__io_terminated) {
++              if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
++                      break;
++      }
++ 
++      if (!__io_terminated)
++              unregister_agent(conn, agent_path, address, use_default);
++ 
++      if (passkey)
++              free(passkey);
++ 
++      dbus_connection_unref(conn);
++ 
++      return 0;
++ }
index 0000000,0000000,612855f..08620d4
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,671 +1,688 @@@@
  -     int sk;
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <ctype.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <getopt.h>
++ #include <syslog.h>
++ #include <signal.h>
++ #include <sys/time.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/rfcomm.h>
++ 
++ /* Test modes */
++ enum {
++      SEND,
++      RECV,
++      RECONNECT,
++      MULTY,
++      DUMP,
++      CONNECT,
++      CRECV,
++      LSEND
++ };
++ 
++ static unsigned char *buf;
++ 
++ /* Default data size */
++ static long data_size = 127;
++ static long num_frames = -1;
++ 
++ /* Default number of consecutive frames before the delay */
++ static int count = 1;
++ 
++ /* Default delay after sending count number of frames */
++ static unsigned long delay = 0;
++ 
++ /* Default addr and channel */
++ static bdaddr_t bdaddr;
++ static uint8_t channel = 10;
++ 
++ static char *filename = NULL;
++ 
++ static int master = 0;
++ static int auth = 0;
++ static int encrypt = 0;
++ static int secure = 0;
++ static int socktype = SOCK_STREAM;
++ static int linger = 0;
++ static int timestamp = 0;
++ 
++ static float tv2fl(struct timeval tv)
++ {
++      return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
++ }
++ 
++ static int do_connect(char *svr)
++ {
++      struct sockaddr_rc addr;
++      struct rfcomm_conninfo conn;
++      socklen_t optlen;
  -     /* Bind to local address */
  -     memset(&addr, 0, sizeof(addr));
  -     addr.rc_family = AF_BLUETOOTH;
  -     bacpy(&addr.rc_bdaddr, &bdaddr);
  -
  -     if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  -             syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+++     int sk, opt;
++ 
++      /* Create socket */
++      sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
++      if (sk < 0) {
++              syslog(LOG_ERR, "Can't create socket: %s (%d)",
++                                                      strerror(errno), errno);
++              return -1;
++      }
++ 
+++     /* Bind to local address */
+++     memset(&addr, 0, sizeof(addr));
+++     addr.rc_family = AF_BLUETOOTH;
+++     bacpy(&addr.rc_bdaddr, &bdaddr);
+++
+++     if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+++             syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+++                                                     strerror(errno), errno);
+++             goto error;
+++     }
+++
++ #if 0
++      /* Enable SO_TIMESTAMP */
++      if (timestamp) {
++              int t = 1;
++ 
++              if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
++                      syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++      }
++ #endif
++ 
++      /* Enable SO_LINGER */
++      if (linger) {
++              struct linger l = { .l_onoff = 1, .l_linger = linger };
++ 
++              if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
++                      syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++      }
++ 
+++     /* Set link mode */
+++     opt = 0;
+++     if (master)
+++             opt |= RFCOMM_LM_MASTER;
+++     if (auth)
+++             opt |= RFCOMM_LM_AUTH;
+++     if (encrypt)
+++             opt |= RFCOMM_LM_ENCRYPT;
+++     if (secure)
+++             opt |= RFCOMM_LM_SECURE;
+++
+++     if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+++             syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Connect to remote device */
++      memset(&addr, 0, sizeof(addr));
++      addr.rc_family = AF_BLUETOOTH;
++      str2ba(svr, &addr.rc_bdaddr);
++      addr.rc_channel = channel;
++ 
++      if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              syslog(LOG_ERR, "Can't connect: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Get connection information */
++      memset(&conn, 0, sizeof(conn));
++      optlen = sizeof(conn);
++ 
++      if (getsockopt(sk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
++                                                      strerror(errno), errno);
++              //goto error;
++      }
++ 
++      syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
++              conn.hci_handle,
++              conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
++ 
++      return sk;
++ 
++ error:
++      close(sk);
++      return -1;
++ }
++ 
++ static void do_listen(void (*handler)(int sk))
++ {
++      struct sockaddr_rc addr;
++      struct rfcomm_conninfo conn;
++      socklen_t optlen;
++      int sk, nsk, opt;
++      char ba[18];
++ 
++      /* Create socket */
++      sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
++      if (sk < 0) {
++              syslog(LOG_ERR, "Can't create socket: %s (%d)",
++                                                      strerror(errno), errno);
++              exit(1);
++      }
++ 
++      /* Bind to local address */
++      addr.rc_family = AF_BLUETOOTH;
++      bacpy(&addr.rc_bdaddr, &bdaddr);
++      addr.rc_channel = channel;
++ 
++      if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++              syslog(LOG_ERR, "Can't bind socket: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Set link mode */
++      opt = 0;
++      if (master)
++              opt |= RFCOMM_LM_MASTER;
++      if (auth)
++              opt |= RFCOMM_LM_AUTH;
++      if (encrypt)
++              opt |= RFCOMM_LM_ENCRYPT;
++      if (secure)
++              opt |= RFCOMM_LM_SECURE;
++ 
++      if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
++              syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Listen for connections */
++      if (listen(sk, 10)) {
++              syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      /* Check for socket address */
++      memset(&addr, 0, sizeof(addr));
++      optlen = sizeof(addr);
++ 
++      if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
++              syslog(LOG_ERR, "Can't get socket name: %s (%d)",
++                                                      strerror(errno), errno);
++              goto error;
++      }
++ 
++      channel = addr.rc_channel;
++ 
++      syslog(LOG_INFO, "Waiting for connection on channel %d ...", channel);
++ 
++      while(1) {
++              memset(&addr, 0, sizeof(addr));
++              optlen = sizeof(addr);
++ 
++              nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
++              if (nsk < 0) {
++                      syslog(LOG_ERR,"Accept failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      goto error;
++              }
++              if (fork()) {
++                      /* Parent */
++                      close(nsk);
++                      continue;
++              }
++              /* Child */
++              close(sk);
++ 
++              /* Get connection information */
++              memset(&conn, 0, sizeof(conn));
++              optlen = sizeof(conn);
++ 
++              if (getsockopt(nsk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
++                      syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
++                                                      strerror(errno), errno);
++                      //close(nsk);
++                      //goto error;
++              }
++ 
++              ba2str(&addr.rc_bdaddr, ba);
++              syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
++                      ba, conn.hci_handle,
++                      conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
++ 
++ #if 0
++              /* Enable SO_TIMESTAMP */
++              if (timestamp) {
++                      int t = 1;
++ 
++                      if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
++                              syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
++                                                      strerror(errno), errno);
++                              goto error;
++                      }
++              }
++ #endif
++ 
++              /* Enable SO_LINGER */
++              if (linger) {
++                      struct linger l = { .l_onoff = 1, .l_linger = linger };
++ 
++                      if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
++                              syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
++                                                      strerror(errno), errno);
++                              close(nsk);
++                              goto error;
++                      }
++              }
++ 
++              handler(nsk);
++ 
++              syslog(LOG_INFO, "Disconnect: %m");
++              exit(0);
++      }
++ 
++      return;
++ 
++ error:
++      close(sk);
++      exit(1);
++ }
++ 
++ static void dump_mode(int sk)
++ {
++      int len;
++ 
++      syslog(LOG_INFO, "Receiving ...");
++      while ((len = read(sk, buf, data_size)) > 0)
++              syslog(LOG_INFO, "Recevied %d bytes", len);
++ }
++ 
++ static void recv_mode(int sk)
++ {
++      struct timeval tv_beg, tv_end, tv_diff;
++      char ts[30];
++      long total;
++      uint32_t seq;
++ 
++      syslog(LOG_INFO, "Receiving ...");
++ 
++      memset(ts, 0, sizeof(ts));
++ 
++      seq = 0;
++      while (1) {
++              gettimeofday(&tv_beg,NULL);
++              total = 0;
++              while (total < data_size) {
++                      //uint32_t sq;
++                      //uint16_t l;
++                      int r;
++ 
++                      if ((r = recv(sk, buf, data_size, 0)) <= 0) {
++                              if (r < 0)
++                                      syslog(LOG_ERR, "Read failed: %s (%d)",
++                                                      strerror(errno), errno);
++                              return; 
++                      }
++ 
++                      if (timestamp) {
++                              struct timeval tv;
++ 
++                              if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
++                                      timestamp = 0;
++                                      memset(ts, 0, sizeof(ts));
++                              } else {
++                                      sprintf(ts, "[%ld.%ld] ",
++                                                      tv.tv_sec, tv.tv_usec);
++                              }
++                      }
++ 
++ #if 0
++                      /* Check sequence */
++                      sq = btohl(*(uint32_t *) buf);
++                      if (seq != sq) {
++                              syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
++                              seq = sq;
++                      }
++                      seq++;
++                      
++                      /* Check length */
++                      l = btohs(*(uint16_t *) (buf + 4));
++                      if (r != l) {
++                              syslog(LOG_INFO, "size missmatch: %d -> %d", r, l);
++                              continue;
++                      }
++                      
++                      /* Verify data */       
++                      for (i = 6; i < r; i++) {
++                              if (buf[i] != 0x7f)
++                                      syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
++                      }
++ #endif
++                      total += r;
++              }
++              gettimeofday(&tv_end,NULL);
++ 
++              timersub(&tv_end,&tv_beg,&tv_diff);
++ 
++              syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
++                      tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
++      }
++ }
++ 
++ static void do_send(int sk)
++ {
++      uint32_t seq;
++      int i, fd, len;
++ 
++      syslog(LOG_INFO,"Sending ...");
++ 
++      if (filename) {
++              fd = open(filename, O_RDONLY);
++              if (fd < 0) {
++                      syslog(LOG_ERR, "Open failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      exit(1);
++              }
++              len = read(fd, buf, data_size);
++              send(sk, buf, len, 0);
++              return;
++      } else {
++              for (i = 6; i < data_size; i++)
++                      buf[i] = 0x7f;
++      }
++ 
++      seq = 0;
++      while ((num_frames == -1) || (num_frames-- > 0)) {
++              *(uint32_t *) buf = htobl(seq);
++              *(uint16_t *) (buf + 4) = htobs(data_size);
++              seq++;
++              
++              if (send(sk, buf, data_size, 0) <= 0) {
++                      syslog(LOG_ERR, "Send failed: %s (%d)",
++                                                      strerror(errno), errno);
++                      exit(1);
++              }
++ 
++              if (num_frames && delay && count && !(seq % count))
++                      usleep(delay);
++      }
++ }
++ 
++ static void send_mode(int sk)
++ {
++      do_send(sk);
++ 
++      syslog(LOG_INFO, "Closing channel ...");
++      if (shutdown(sk, SHUT_RDWR) < 0)
++              syslog(LOG_INFO, "Close failed: %m");
++      else
++              syslog(LOG_INFO, "Done");
++ }
++ 
++ static void reconnect_mode(char *svr)
++ {
++      while(1) {
++              int sk = do_connect(svr);
++              close(sk);
++      }
++ }
++ 
++ static void multi_connect_mode(int argc, char *argv[])
++ {
++      int i, n, sk;
++ 
++      while (1) {
++              for (n = 0; n < argc; n++) {
++                      for (i = 0; i < count; i++) {
++                              if (fork())
++                                      continue;
++ 
++                              /* Child */
++                              sk = do_connect(argv[n]);
++                              usleep(500);
++                              close(sk);
++                              exit(0);
++                      }
++              }
++              sleep(4);
++      }
++ }
++ 
++ static void usage(void)
++ {
++      printf("rctest - RFCOMM testing\n"
++              "Usage:\n");
++      printf("\trctest <mode> [options] [bdaddr]\n");
++      printf("Modes:\n"
++              "\t-r listen and receive\n"
++              "\t-w listen and send\n"
++              "\t-d listen and dump incoming data\n"
++              "\t-s connect and send\n"
++              "\t-u connect and receive\n"
++              "\t-n connect and be silent\n"
++              "\t-c connect, disconnect, connect, ...\n"
++              "\t-m multiple connects\n");
++ 
++      printf("Options:\n"
++              "\t[-b bytes] [-i device] [-P channel]\n"
++              "\t[-L seconds] enabled SO_LINGER option\n"
++              "\t[-B filename] use data packets from file\n"
++              "\t[-N num] number of frames to send\n"
++              "\t[-C num] send num frames before delay (default = 1)\n"
++              "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
++              "\t[-A] request authentication\n"
++              "\t[-E] request encryption\n"
++              "\t[-S] secure connection\n"
++              "\t[-M] become master\n"
++              "\t[-T] enable timestamps\n");
++ }
++ 
++ int main(int argc, char *argv[])
++ {
++      struct sigaction sa;
++      int opt, sk, mode = RECV, need_addr = 0;
++ 
++      bacpy(&bdaddr, BDADDR_ANY);
++ 
++      while ((opt=getopt(argc,argv,"rdscuwmnb:i:P:B:N:MAESL:C:D:T")) != EOF) {
++              switch (opt) {
++              case 'r':
++                      mode = RECV;
++                      break;
++ 
++              case 's':
++                      mode = SEND;
++                      need_addr = 1;
++                      break;
++ 
++              case 'w':
++                      mode = LSEND;
++                      break;
++ 
++              case 'u':
++                      mode = CRECV;
++                      need_addr = 1;
++                      break;
++ 
++              case 'd':
++                      mode = DUMP;
++                      break;
++ 
++              case 'c':
++                      mode = RECONNECT;
++                      need_addr = 1;
++                      break;
++ 
++              case 'n':
++                      mode = CONNECT;
++                      need_addr = 1;
++                      break;
++ 
++              case 'm':
++                      mode = MULTY;
++                      need_addr = 1;
++                      break;
++ 
++              case 'b':
++                      data_size = atoi(optarg);
++                      break;
++ 
++              case 'i':
++                      if (!strncasecmp(optarg, "hci", 3))
++                              hci_devba(atoi(optarg + 3), &bdaddr);
++                      else
++                              str2ba(optarg, &bdaddr);
++                      break;
++ 
++              case 'P':
++                      channel = atoi(optarg);
++                      break;
++ 
++              case 'M':
++                      master = 1;
++                      break;
++ 
++              case 'A':
++                      auth = 1;
++                      break;
++ 
++              case 'E':
++                      encrypt = 1;
++                      break;
++ 
++              case 'S':
++                      secure = 1;
++                      break;
++ 
++              case 'L':
++                      linger = atoi(optarg);
++                      break;
++ 
++              case 'B':
++                      filename = strdup(optarg);
++                      break;
++ 
++              case 'N':
++                      num_frames = atoi(optarg);
++                      break;
++ 
++              case 'C':
++                      count = atoi(optarg);
++                      break;
++ 
++              case 'D':
++                      delay = atoi(optarg) * 1000;
++                      break;
++ 
++              case 'T':
++                      timestamp = 1;
++                      break;
++ 
++              default:
++                      usage();
++                      exit(1);
++              }
++      }
++ 
++      if (need_addr && !(argc - optind)) {
++              usage();
++              exit(1);
++      }
++ 
++      if (!(buf = malloc(data_size))) {
++              perror("Can't allocate data buffer");
++              exit(1);
++      }
++ 
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_handler = SIG_IGN;
++      sa.sa_flags   = SA_NOCLDSTOP;
++      sigaction(SIGCHLD, &sa, NULL);
++ 
++      openlog("rctest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
++ 
++      switch (mode) {
++              case RECV:
++                      do_listen(recv_mode);
++                      break;
++ 
++              case CRECV:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      recv_mode(sk);
++                      break;
++ 
++              case DUMP:
++                      do_listen(dump_mode);
++                      break;
++ 
++              case SEND:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      send_mode(sk);
++                      break;
++ 
++              case LSEND:
++                      do_listen(send_mode);
++                      break;
++ 
++              case RECONNECT:
++                      reconnect_mode(argv[optind]);
++                      break;
++ 
++              case MULTY:
++                      multi_connect_mode(argc - optind, argv + optind);
++                      break;
++ 
++              case CONNECT:
++                      sk = do_connect(argv[optind]);
++                      if (sk < 0)
++                              exit(1);
++                      dump_mode(sk);
++                      break;
++      }
++ 
++      syslog(LOG_INFO, "Exit");
++ 
++      closelog();
++ 
++      return 0;
++ }
index 0000000,0000000,0000000..6b02705
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,161 @@@@
+++BUILD_HCICONFIG:=1
+++BUILD_HCITOOL:=1
+++BUILD_L2PING:=1
+++BUILD_HCIATTACH:=1
+++
+++LOCAL_PATH:= $(call my-dir)
+++
+++#
+++# sdptool
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common/ 
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" -fpermissive
+++
+++LOCAL_SRC_FILES:= \
+++     sdptool.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth 
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE:=sdptool
+++
+++include $(BUILD_EXECUTABLE)
+++
+++ifeq ($(BUILD_HCICONFIG),1)
+++
+++#
+++# hciconfig 
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common/
+++
+++LOCAL_CFLAGS:= \
+++     -DSTORAGEDIR=\"/tmp\" \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     csr.c \
+++     csr_h4.c \
+++     hciconfig.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth 
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++
+++LOCAL_MODULE:=hciconfig
+++
+++include $(BUILD_EXECUTABLE)
+++
+++endif # BUILD_HCICONFIG
+++
+++ifeq ($(BUILD_HCITOOL),1)
+++
+++#
+++# hcitool 
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common/
+++
+++LOCAL_CFLAGS:= \
+++     -DSTORAGEDIR=\"/tmp\" \
+++     -DVERSION=\"3.36\"
+++
+++LOCAL_SRC_FILES:= \
+++     hcitool.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++
+++LOCAL_MODULE:=hcitool
+++
+++include $(BUILD_EXECUTABLE)
+++
+++endif # hcitool
+++
+++ifeq ($(BUILD_L2PING),1)
+++
+++#
+++# l2ping 
+++#
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs)
+++
+++LOCAL_SRC_FILES:= \
+++     l2ping.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth 
+++
+++LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+++LOCAL_MODULE_TAGS := eng
+++LOCAL_MODULE:=l2ping
+++
+++include $(BUILD_EXECUTABLE)
+++
+++endif # l2ping 
+++
+++
+++#
+++# hciattach
+++#
+++
+++ifeq ($(BUILD_HCIATTACH),1)
+++
+++include $(CLEAR_VARS)
+++
+++LOCAL_C_INCLUDES:= \
+++     $(call include-path-for, bluez-libs) \
+++     $(call include-path-for, bluez-utils)/common/ 
+++
+++LOCAL_CFLAGS:= \
+++     -DVERSION=\"3.36\" \
+++     -D__BSD_VISIBLE=1 
+++
+++LOCAL_SRC_FILES:= \
+++     hciattach.c \
+++     hciattach_st.c \
+++     hciattach_tialt.c
+++
+++LOCAL_SHARED_LIBRARIES := \
+++     libbluetooth 
+++
+++LOCAL_STATIC_LIBRARIES := \
+++     libbluez-utils-common-static
+++
+++LOCAL_MODULE:=hciattach
+++
+++include $(BUILD_EXECUTABLE)
+++
+++endif # hciattach
index 0000000,0000000,36f3c72..c373607
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,1392 +1,1402 @@@@
  -#define HCI_UART_H4  0
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2000-2001  Qualcomm Incorporated
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <unistd.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <signal.h>
++ #include <syslog.h>
++ #include <termios.h>
++ #include <time.h>
++ #include <sys/time.h>
++ #include <sys/poll.h>
++ #include <sys/param.h>
++ #include <sys/ioctl.h>
++ #include <sys/socket.h>
+++#include <sys/endian.h>
+++#include <sys/uio.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ 
++ #ifndef N_HCI
++ #define N_HCI        15
++ #endif
++ 
++ #define HCIUARTSETPROTO              _IOW('U', 200, int)
++ #define HCIUARTGETPROTO              _IOR('U', 201, int)
++ #define HCIUARTGETDEVICE     _IOR('U', 202, int)
++ 
  -static int read_hci_event(int fd, unsigned char* buf, int size) 
+++#define HCI_UART_H4          0
++ #define HCI_UART_BCSP        1
++ #define HCI_UART_3WIRE       2
++ #define HCI_UART_H4DS        3
+++#define HCI_UART_LL          4
++ 
++ struct uart_t {
++      char *type;
++      int  m_id;
++      int  p_id;
++      int  proto;
++      int  init_speed;
++      int  speed;
++      int  flags;
++      char *bdaddr;
++      int  (*init) (int fd, struct uart_t *u, struct termios *ti);
++ };
++ 
++ #define FLOW_CTL     0x0001
++ 
++ static volatile sig_atomic_t __io_canceled = 0;
++ 
++ static void sig_hup(int sig)
++ {
++ }
++ 
++ static void sig_term(int sig)
++ {
++      __io_canceled = 1;
++ }
++ 
++ static void sig_alarm(int sig)
++ {
++      fprintf(stderr, "Initialization timed out.\n");
++      exit(1);
++ }
++ 
++ static int uart_speed(int s)
++ {
++      switch (s) {
++      case 9600:
++              return B9600;
++      case 19200:
++              return B19200;
++      case 38400:
++              return B38400;
++      case 57600:
++              return B57600;
++      case 115200:
++              return B115200;
++      case 230400:
++              return B230400;
++      case 460800:
++              return B460800;
++      case 500000:
++              return B500000;
++      case 576000:
++              return B576000;
++      case 921600:
++              return B921600;
++      case 1000000:
++              return B1000000;
++      case 1152000:
++              return B1152000;
++      case 1500000:
++              return B1500000;
++      case 2000000:
++              return B2000000;
++ #ifdef B2500000
++      case 2500000:
++              return B2500000;
++ #endif
++ #ifdef B3000000
++      case 3000000:
++              return B3000000;
++ #endif
++ #ifdef B3500000
++      case 3500000:
++              return B3500000;
++ #endif
++ #ifdef B4000000
++      case 4000000:
++              return B4000000;
++ #endif
++      default:
++              return B57600;
++      }
++ }
++ 
++ static int set_speed(int fd, struct termios *ti, int speed)
++ {
++      cfsetospeed(ti, uart_speed(speed));
+++     cfsetispeed(ti, uart_speed(speed));
++      return tcsetattr(fd, TCSANOW, ti);
++ }
++ 
++ /* 
++  * Read an HCI event from the given file descriptor.
++  */
  -     { "texas",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, texas    },
+++int read_hci_event(int fd, unsigned char* buf, int size) 
++ {
++      int remain, r;
++      int count = 0;
++ 
++      if (size <= 0)
++              return -1;
++ 
++      /* The first byte identifies the packet type. For HCI event packets, it
++       * should be 0x04, so we read until we get to the 0x04. */
++      while (1) {
++              r = read(fd, buf, 1);
++              if (r <= 0)
++                      return -1;
++              if (buf[0] == 0x04)
++                      break;
++      }
++      count++;
++ 
++      /* The next two bytes are the event code and parameter total length. */
++      while (count < 3) {
++              r = read(fd, buf + count, 3 - count);
++              if (r <= 0)
++                      return -1;
++              count += r;
++      }
++ 
++      /* Now we read the parameters. */
++      if (buf[2] < (size - 3)) 
++              remain = buf[2];
++      else 
++              remain = size - 3;
++ 
++      while ((count - 3) < remain) {
++              r = read(fd, buf + count, remain - (count - 3));
++              if (r <= 0)
++                      return -1;
++              count += r;
++      }
++ 
++      return count;
++ }
++ 
++ /* 
++  * Ericsson specific initialization 
++  */
++ static int ericsson(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = {0, 50000};
++      char cmd[5];
++ 
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x09;
++      cmd[2] = 0xfc;
++      cmd[3] = 0x01;
++ 
++      switch (u->speed) {
++      case 57600:
++              cmd[4] = 0x03;
++              break;
++      case 115200:
++              cmd[4] = 0x02;
++              break;
++      case 230400:
++              cmd[4] = 0x01;
++              break;
++      case 460800:
++              cmd[4] = 0x00;
++              break;
++      case 921600:
++              cmd[4] = 0x20;
++              break;
++      case 2000000:
++              cmd[4] = 0x25;
++              break;
++      case 3000000:
++              cmd[4] = 0x27;
++              break;
++      case 4000000:
++              cmd[4] = 0x2B;
++              break;
++      default:
++              cmd[4] = 0x03;
++              u->speed = 57600;
++              fprintf(stderr, "Invalid speed requested, using %d bps instead\n", u->speed);
++              break;
++      }
++ 
++      /* Send initialization command */
++      if (write(fd, cmd, 5) != 5) {
++              perror("Failed to write init command");
++              return -1;
++      }
++ 
++      nanosleep(&tm, NULL);
++      return 0;
++ }
++ 
++ /* 
++  * Digianswer specific initialization 
++  */
++ static int digi(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = {0, 50000};
++      char cmd[5];
++ 
++      /* DigiAnswer set baud rate command */
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x07;
++      cmd[2] = 0xfc;
++      cmd[3] = 0x01;
++ 
++      switch (u->speed) {
++      case 57600:
++              cmd[4] = 0x08;
++              break;
++      case 115200:
++              cmd[4] = 0x09;
++              break;
++      default:
++              cmd[4] = 0x09;
++              u->speed = 115200;
++              break;
++      }
++ 
++      /* Send initialization command */
++      if (write(fd, cmd, 5) != 5) {
++              perror("Failed to write init command");
++              return -1;
++      }
++ 
++      nanosleep(&tm, NULL);
++      return 0;
++ }
++ 
++ static int texas(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = {0, 50000};
++      char cmd[4];
++      unsigned char resp[100];                /* Response */
++      int n;
++ 
++      memset(resp,'\0', 100);
++ 
++      /* It is possible to get software version with manufacturer specific 
++         HCI command HCI_VS_TI_Version_Number. But the only thing you get more
++         is if this is point-to-point or point-to-multipoint module */
++ 
++      /* Get Manufacturer and LMP version */
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x01;
++      cmd[2] = 0x10;
++      cmd[3] = 0x00;
++ 
++      do {
++              n = write(fd, cmd, 4);
++              if (n < 0) {
++                      perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
++                      return -1;
++              }
++              if (n < 4) {
++                      fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
++                      return -1;
++              }
++ 
++              /* Read reply. */
++              if (read_hci_event(fd, resp, 100) < 0) {
++                      perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
++                      return -1;
++              }
++ 
++              /* Wait for command complete event for our Opcode */
++      } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
++ 
++      /* Verify manufacturer */
++      if ((resp[11] & 0xFF) != 0x0d)
++              fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
++ 
++      /* Print LMP version */
++      fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
++ 
++      /* Print LMP subversion */
++      fprintf(stderr, "Texas module LMP sub-version : 0x%02x%02x\n", resp[14] & 0xFF, resp[13] & 0xFF);
++ 
++      nanosleep(&tm, NULL);
++      return 0;
++ }
++ 
+++extern int texasalt_init(int fd, int speed);
+++
+++int texasalt(int fd, struct uart_t *u, struct termios *ti) {
+++     texasalt_init(fd, u->speed);
+++}
+++
++ static int read_check(int fd, void *buf, int count)
++ {
++      int res;
++ 
++      do {
++              res = read(fd, buf, count);
++              if (res != -1) {
++                      buf += res; 
++                      count -= res;
++              }
++      } while (count && (errno == 0 || errno == EINTR));
++ 
++      if (count)
++              return -1;
++ 
++      return 0;
++ }
++ 
++ /*
++  * BCSP specific initialization
++  */
++ int serial_fd;
++ 
++ static void bcsp_tshy_sig_alarm(int sig)
++ {
++      static int retries=0;
++      unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
++      int len;
++ 
++      if (retries < 10) {
++              retries++;
++              len = write(serial_fd, &bcsp_sync_pkt, 10);
++              alarm(1);
++              return;
++      }
++ 
++      tcflush(serial_fd, TCIOFLUSH);
++      fprintf(stderr, "BCSP initialization timed out\n");
++      exit(1);
++ }
++ 
++ static void bcsp_tconf_sig_alarm(int sig)
++ {
++      static int retries=0;
++      unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
++      int len;
++ 
++      if (retries < 10){
++              retries++;
++              len = write(serial_fd, &bcsp_conf_pkt, 10);
++              alarm(1);
++              return;
++      }
++ 
++      tcflush(serial_fd, TCIOFLUSH);
++      fprintf(stderr, "BCSP initialization timed out\n");
++      exit(1);
++ }
++ 
++ static int bcsp(int fd, struct uart_t *u, struct termios *ti)
++ {
++      unsigned char byte, bcsph[4], bcspp[4],
++              bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0},
++              bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0},
++              bcspsync[4]     = {0xda, 0xdc, 0xed, 0xed},
++              bcspsyncresp[4] = {0xac,0xaf,0xef,0xee},
++              bcspconf[4]     = {0xad,0xef,0xac,0xed},
++              bcspconfresp[4] = {0xde,0xad,0xd0,0xd0};
++      struct sigaction sa;
++      int len;
++ 
++      if (set_speed(fd, ti, u->speed) < 0) {
++              perror("Can't set default baud rate");
++              return -1;
++      }
++ 
++      ti->c_cflag |= PARENB;
++      ti->c_cflag &= ~(PARODD);
++ 
++      if (tcsetattr(fd, TCSANOW, ti) < 0) {
++              perror("Can't set port settings");
++              return -1;
++      }
++ 
++      alarm(0);
++ 
++      serial_fd = fd;
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_flags = SA_NOCLDSTOP;
++      sa.sa_handler = bcsp_tshy_sig_alarm;
++      sigaction(SIGALRM, &sa, NULL);
++ 
++      /* State = shy */
++ 
++      bcsp_tshy_sig_alarm(0);
++      while (1) {
++              do {
++                      if (read_check(fd, &byte, 1) == -1){
++                              perror("Failed to read");
++                              return -1;
++                      }
++              } while (byte != 0xC0);
++ 
++              do {
++                      if ( read_check(fd, &bcsph[0], 1) == -1){
++                              perror("Failed to read");
++                              return -1;
++                      }
++              } while (bcsph[0] == 0xC0);
++ 
++              if ( read_check(fd, &bcsph[1], 3) == -1){
++                      perror("Failed to read");
++                      return -1;
++              }
++ 
++              if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
++                      continue;
++              if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
++                      continue;
++ 
++              if (read_check(fd, &bcspp, 4) == -1){
++                      perror("Failed to read");
++                      return -1;
++              }
++ 
++              if (!memcmp(bcspp, bcspsync, 4)) {
++                      len = write(fd, &bcsp_sync_resp_pkt,10);
++              } else if (!memcmp(bcspp, bcspsyncresp, 4))
++                      break;
++      }
++ 
++      /* State = curious */
++ 
++      alarm(0);
++      sa.sa_handler = bcsp_tconf_sig_alarm;
++      sigaction(SIGALRM, &sa, NULL);
++      alarm(1);
++ 
++      while (1) {
++              do {
++                      if (read_check(fd, &byte, 1) == -1){
++                              perror("Failed to read");
++                              return -1;
++                      }
++              } while (byte != 0xC0);
++ 
++              do {
++                      if (read_check(fd, &bcsph[0], 1) == -1){
++                              perror("Failed to read");
++                              return -1;
++                      }
++              } while (bcsph[0] == 0xC0);
++ 
++              if (read_check(fd, &bcsph[1], 3) == -1){
++                      perror("Failed to read");
++                      return -1;
++              }
++ 
++              if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
++                      continue;
++ 
++              if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
++                      continue;
++ 
++              if (read_check(fd, &bcspp, 4) == -1){
++                      perror("Failed to read");
++                      return -1;
++              }
++ 
++              if (!memcmp(bcspp, bcspsync, 4))
++                      len = write(fd, &bcsp_sync_resp_pkt, 10);
++              else if (!memcmp(bcspp, bcspconf, 4))
++                      len = write(fd, &bcsp_conf_resp_pkt, 10);
++              else if (!memcmp(bcspp, bcspconfresp,  4))
++                      break;
++      }
++ 
++      /* State = garrulous */
++ 
++      return 0;
++ }
++ 
++ /* 
++  * CSR specific initialization 
++  * Inspired strongly by code in OpenBT and experimentations with Brainboxes
++  * Pcmcia card.
++  * Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
++  */
++ static int csr(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = {0, 10000000};     /* 10ms - be generous */
++      unsigned char cmd[30];          /* Command */
++      unsigned char resp[30];         /* Response */
++      int  clen = 0;          /* Command len */
++      static int csr_seq = 0; /* Sequence number of command */
++      int  divisor;
++ 
++      /* It seems that if we set the CSR UART speed straight away, it
++       * won't work, the CSR UART gets into a state where we can't talk
++       * to it anymore.
++       * On the other hand, doing a read before setting the CSR speed
++       * seems to be ok.
++       * Therefore, the strategy is to read the build ID (useful for
++       * debugging) and only then set the CSR UART speed. Doing like
++       * this is more complex but at least it works ;-)
++       * The CSR UART control may be slow to wake up or something because
++       * every time I read its speed, its bogus...
++       * Jean II */
++ 
++      /* Try to read the build ID of the CSR chip */
++      clen = 5 + (5 + 6) * 2;
++      /* HCI header */
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x00;          /* CSR command */
++      cmd[2] = 0xfc;          /* MANUFACTURER_SPEC */
++      cmd[3] = 1 + (5 + 6) * 2;       /* len */
++      /* CSR MSG header */
++      cmd[4] = 0xC2;          /* first+last+channel=BCC */
++      /* CSR BCC header */
++      cmd[5] = 0x00;          /* type = GET-REQ */
++      cmd[6] = 0x00;          /* - msB */
++      cmd[7] = 5 + 4;         /* len */
++      cmd[8] = 0x00;          /* - msB */
++      cmd[9] = csr_seq & 0xFF;/* seq num */
++      cmd[10] = (csr_seq >> 8) & 0xFF;        /* - msB */
++      csr_seq++;
++      cmd[11] = 0x19;         /* var_id = CSR_CMD_BUILD_ID */
++      cmd[12] = 0x28;         /* - msB */
++      cmd[13] = 0x00;         /* status = STATUS_OK */
++      cmd[14] = 0x00;         /* - msB */
++      /* CSR BCC payload */
++      memset(cmd + 15, 0, 6 * 2);
++ 
++      /* Send command */
++      do {
++              if (write(fd, cmd, clen) != clen) {
++                      perror("Failed to write init command (GET_BUILD_ID)");
++                      return -1;
++              }
++ 
++              /* Read reply. */
++              if (read_hci_event(fd, resp, 100) < 0) {
++                      perror("Failed to read init response (GET_BUILD_ID)");
++                      return -1;
++              }
++ 
++      /* Event code 0xFF is for vendor-specific events, which is 
++       * what we're looking for. */
++      } while (resp[1] != 0xFF);
++ 
++ #ifdef CSR_DEBUG
++      {
++      char temp[512];
++      int i;
++      for (i=0; i < rlen; i++)
++              sprintf(temp + (i*3), "-%02X", resp[i]);
++      fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
++      // In theory, it should look like :
++      // 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
++      }
++ #endif
++      /* Display that to user */
++      fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n", 
++              resp[15] & 0xFF, resp[14] & 0xFF);
++ 
++      /* Try to read the current speed of the CSR chip */
++      clen = 5 + (5 + 4)*2;
++      /* -- HCI header */
++      cmd[3] = 1 + (5 + 4)*2; /* len */
++      /* -- CSR BCC header -- */
++      cmd[9] = csr_seq & 0xFF;        /* seq num */
++      cmd[10] = (csr_seq >> 8) & 0xFF;        /* - msB */
++      csr_seq++;
++      cmd[11] = 0x02;         /* var_id = CONFIG_UART */
++      cmd[12] = 0x68;         /* - msB */
++ 
++ #ifdef CSR_DEBUG
++      /* Send command */
++      do {
++              if (write(fd, cmd, clen) != clen) {
++                      perror("Failed to write init command (GET_BUILD_ID)");
++                      return -1;
++              }
++ 
++              /* Read reply. */
++              if (read_hci_event(fd, resp, 100) < 0) {
++                      perror("Failed to read init response (GET_BUILD_ID)");
++                      return -1;
++              }
++ 
++      /* Event code 0xFF is for vendor-specific events, which is 
++       * what we're looking for. */
++      } while (resp[1] != 0xFF);
++ 
++      {
++      char temp[512];
++      int i;
++      for (i=0; i < rlen; i++)
++              sprintf(temp + (i*3), "-%02X", resp[i]);
++      fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
++      }
++ #endif
++ 
++      if (u->speed > 1500000) {
++              fprintf(stderr, "Speed %d too high. Remaining at %d baud\n", 
++                      u->speed, u->init_speed);
++              u->speed = u->init_speed;
++      } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
++              /* Unknown speed. Why oh why can't we just pass an int to the kernel? */
++              fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
++                      u->speed, u->init_speed);
++              u->speed = u->init_speed;
++      }
++      if (u->speed == u->init_speed)
++              return 0;
++ 
++      /* Now, create the command that will set the UART speed */
++      /* CSR BCC header */
++      cmd[5] = 0x02;                  /* type = SET-REQ */
++      cmd[6] = 0x00;                  /* - msB */
++      cmd[9] = csr_seq & 0xFF;        /* seq num */
++      cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
++      csr_seq++;
++ 
++      divisor = (u->speed*64+7812)/15625;
++ 
++      /* No parity, one stop bit -> divisor |= 0x0000; */
++      cmd[15] = (divisor) & 0xFF;             /* divider */
++      cmd[16] = (divisor >> 8) & 0xFF;        /* - msB */
++      /* The rest of the payload will be 0x00 */
++ 
++ #ifdef CSR_DEBUG
++      {
++      char temp[512];
++      int i;
++      for(i = 0; i < clen; i++)
++              sprintf(temp + (i*3), "-%02X", cmd[i]);
++      fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
++      // In theory, it should look like :
++      // 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
++      // 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
++      }
++ #endif
++ 
++      /* Send the command to set the CSR UART speed */
++      if (write(fd, cmd, clen) != clen) {
++              perror("Failed to write init command (SET_UART_SPEED)");
++              return -1;
++      }
++ 
++      nanosleep(&tm, NULL);
++      return 0;
++ }
++ 
++ /* 
++  * Silicon Wave specific initialization 
++  * Thomas Moser <thomas.moser@tmoser.ch>
++  */
++ static int swave(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = { 0, 500000 };
++      char cmd[10], rsp[100];
++      int r;
++ 
++      // Silicon Wave set baud rate command
++      // see HCI Vendor Specific Interface from Silicon Wave
++      // first send a "param access set" command to set the
++      // appropriate data fields in RAM. Then send a "HCI Reset
++      // Subcommand", e.g. "soft reset" to make the changes effective.
++ 
++      cmd[0] = HCI_COMMAND_PKT;       // it's a command packet
++      cmd[1] = 0x0B;                  // OCF 0x0B     = param access set      
++      cmd[2] = 0xfc;                  // OGF bx111111 = vendor specific
++      cmd[3] = 0x06;                  // 6 bytes of data following
++      cmd[4] = 0x01;                  // param sub command
++      cmd[5] = 0x11;                  // tag 17 = 0x11 = HCI Transport Params
++      cmd[6] = 0x03;                  // length of the parameter following
++      cmd[7] = 0x01;                  // HCI Transport flow control enable
++      cmd[8] = 0x01;                  // HCI Transport Type = UART
++ 
++      switch (u->speed) {
++      case 19200:
++              cmd[9] = 0x03;
++              break;
++      case 38400:
++              cmd[9] = 0x02;
++              break;
++      case 57600:
++              cmd[9] = 0x01;
++              break;
++      case 115200:
++              cmd[9] = 0x00;
++              break;
++      default:
++              u->speed = 115200;
++              cmd[9] = 0x00;
++              break;
++      }
++ 
++      /* Send initialization command */
++      if (write(fd, cmd, 10) != 10) {
++              perror("Failed to write init command");
++              return -1;
++      }
++ 
++      // We should wait for a "GET Event" to confirm the success of 
++      // the baud rate setting. Wait some time before reading. Better:  
++      // read with timeout, parse data 
++      // until correct answer, else error handling ... todo ...
++ 
++      nanosleep(&tm, NULL);
++ 
++      r = read(fd, rsp, sizeof(rsp));
++      if (r > 0) {
++              // guess it's okay, but we should parse the reply. But since
++              // I don't react on an error anyway ... todo
++              // Response packet format:
++              //  04  Event
++              //  FF  Vendor specific
++              //  07  Parameter length
++              //  0B  Subcommand
++              //  01  Setevent
++              //  11  Tag specifying HCI Transport Layer Parameter
++              //  03  length
++              //  01  flow on
++              //  01  Hci Transport type = Uart
++              //  xx  Baud rate set (see above)
++      } else {
++              // ups, got error.
++              return -1;
++      }
++ 
++      // we probably got the reply. Now we must send the "soft reset"
++      // which is standard HCI RESET.
++ 
++      cmd[0] = HCI_COMMAND_PKT;       // it's a command packet
++      cmd[1] = 0x03;
++      cmd[2] = 0x0c;
++      cmd[3] = 0x00;
++ 
++      /* Send reset command */
++      if (write(fd, cmd, 4) != 4) {
++              perror("Can't write Silicon Wave reset cmd.");
++              return -1;
++      }
++ 
++      nanosleep(&tm, NULL);
++ 
++      // now the uart baud rate on the silicon wave module is set and effective.
++      // change our own baud rate as well. Then there is a reset event comming in
++      // on the *new* baud rate. This is *undocumented*! The packet looks like this:
++      // 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param 
++      // subcommand class". So: change to new baud rate, read with timeout, parse
++      // data, error handling. BTW: all param access in Silicon Wave is done this way.
++      // Maybe this code would belong in a seperate file, or at least code reuse...
++ 
++      return 0;
++ }
++ 
++ /*
++  * ST Microelectronics specific initialization
++  * Marcel Holtmann <marcel@holtmann.org>
++  */
++ static int st(int fd, struct uart_t *u, struct termios *ti)
++ {
++      struct timespec tm = {0, 50000};
++      char cmd[5];
++ 
++      /* ST Microelectronics set baud rate command */
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x46;                  // OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate
++      cmd[2] = 0xfc;                  // OGF = Vendor specific
++      cmd[3] = 0x01;
++ 
++      switch (u->speed) {
++      case 9600:
++              cmd[4] = 0x09;
++              break;
++      case 19200:
++              cmd[4] = 0x0b;
++              break;
++      case 38400:
++              cmd[4] = 0x0d;
++              break;
++      case 57600:
++              cmd[4] = 0x0e;
++              break;
++      case 115200:
++              cmd[4] = 0x10;
++              break;
++      case 230400:
++              cmd[4] = 0x12;
++              break;
++      case 460800:
++              cmd[4] = 0x13;
++              break;
++      case 921600:
++              cmd[4] = 0x14;
++              break;
++      default:
++              cmd[4] = 0x10;
++              u->speed = 115200;
++              break;
++      }
++ 
++      /* Send initialization command */
++      if (write(fd, cmd, 5) != 5) {
++              perror("Failed to write init command");
++              return -1;
++      }
++ 
++      nanosleep(&tm, NULL);
++      return 0;
++ }
++ 
++ extern int stlc2500_init(int fd, bdaddr_t *bdaddr);
++ 
++ static int stlc2500(int fd, struct uart_t *u, struct termios *ti)
++ {
++      bdaddr_t bdaddr;
++      unsigned char resp[10];
++      int n;
++      int rvalue;
++ 
++      /* STLC2500 has an ericsson core */
++      rvalue = ericsson(fd, u, ti);
++      if (rvalue != 0)
++              return rvalue;
++ 
++ #ifdef STLC2500_DEBUG
++      fprintf(stderr, "Setting speed\n");
++ #endif
++      if (set_speed(fd, ti, u->speed) < 0) {
++              perror("Can't set baud rate");
++              return -1;
++      }
++ 
++ #ifdef STLC2500_DEBUG
++      fprintf(stderr, "Speed set...\n");
++ #endif
++ 
++      /* Read reply */
++      if ((n = read_hci_event(fd, resp, 10)) < 0) {
++              fprintf(stderr, "Failed to set baud rate on chip\n");
++              return -1;
++      }
++ 
++ #ifdef STLC2500_DEBUG
++      for (i = 0; i < n; i++) {
++              fprintf(stderr, "resp[%d] = %02x\n", i, resp[i]);
++      }
++ #endif
++ 
++      str2ba(u->bdaddr, &bdaddr);
++      return stlc2500_init(fd, &bdaddr);
++ }
++ 
++ extern int bgb2xx_init(int fd, bdaddr_t *bdaddr);
++ 
++ static int bgb2xx(int fd, struct uart_t *u, struct termios *ti)
++ {
++      bdaddr_t bdaddr;
++ 
++      str2ba(u->bdaddr, &bdaddr);
++ 
++      return bgb2xx_init(fd, &bdaddr);
++ }
++ 
++ /*
++  * Broadcom specific initialization
++  * Extracted from Jungo openrg
++  */
++ static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
++ {
++      int n;
++      unsigned char cmd[30], resp[30];
++ 
++      /* Reset the BT Chip */
++      memset(cmd, 0, sizeof(cmd));
++      memset(resp, 0, sizeof(resp));
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x03;
++      cmd[2] = 0x0c;
++      cmd[3] = 0x00;
++ 
++      /* Send command */
++      if (write(fd, cmd, 4) != 4) {
++              fprintf(stderr, "Failed to write reset command\n");
++              return -1;
++      }
++ 
++      /* Read reply */
++      if ((n = read_hci_event(fd, resp, 4)) < 0) {
++              fprintf(stderr, "Failed to reset chip\n");
++              return -1;
++      }
++ 
++      if (u->bdaddr != NULL) {
++              /* Set BD_ADDR */
++              memset(cmd, 0, sizeof(cmd));
++              memset(resp, 0, sizeof(resp));
++              cmd[0] = HCI_COMMAND_PKT;
++              cmd[1] = 0x01;
++              cmd[2] = 0xfc;
++              cmd[3] = 0x06;
++              str2ba(u->bdaddr, (bdaddr_t *) (cmd + 4));
++ 
++              /* Send command */
++              if (write(fd, cmd, 10) != 10) {
++                      fprintf(stderr, "Failed to write BD_ADDR command\n");
++                      return -1;
++              }
++ 
++              /* Read reply */
++              if ((n = read_hci_event(fd, resp, 10)) < 0) {
++                      fprintf(stderr, "Failed to set BD_ADDR\n");
++                      return -1;
++              }
++      }
++ 
++      /* Read the local version info */
++      memset(cmd, 0, sizeof(cmd));
++      memset(resp, 0, sizeof(resp));
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x01;
++      cmd[2] = 0x10;
++      cmd[3] = 0x00;
++ 
++      /* Send command */
++      if (write(fd, cmd, 4) != 4) {
++              fprintf(stderr, "Failed to write \"read local version\" "
++                      "command\n");
++              return -1;
++      }
++ 
++      /* Read reply */
++      if ((n = read_hci_event(fd, resp, 4)) < 0) {
++              fprintf(stderr, "Failed to read local version\n");
++              return -1;
++      }
++ 
++      /* Read the local supported commands info */
++      memset(cmd, 0, sizeof(cmd));
++      memset(resp, 0, sizeof(resp));
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x02;
++      cmd[2] = 0x10;
++      cmd[3] = 0x00;
++ 
++      /* Send command */
++      if (write(fd, cmd, 4) != 4) {
++              fprintf(stderr, "Failed to write \"read local supported "
++                                              "commands\" command\n");
++              return -1;
++      }
++ 
++      /* Read reply */
++      if ((n = read_hci_event(fd, resp, 4)) < 0) {
++              fprintf(stderr, "Failed to read local supported commands\n");
++              return -1;
++      }
++ 
++      /* Set the baud rate */
++      memset(cmd, 0, sizeof(cmd));
++      memset(resp, 0, sizeof(resp));
++      cmd[0] = HCI_COMMAND_PKT;
++      cmd[1] = 0x18;
++      cmd[2] = 0xfc;
++      cmd[3] = 0x02;
++      switch (u->speed) {
++      case 57600:
++              cmd[4] = 0x00;
++              cmd[5] = 0xe6;
++              break;
++      case 230400:
++              cmd[4] = 0x22;
++              cmd[5] = 0xfa;
++              break;
++      case 460800:
++              cmd[4] = 0x22;
++              cmd[5] = 0xfd;
++              break;
++      case 921600:
++              cmd[4] = 0x55;
++              cmd[5] = 0xff;
++              break;
++      default:
++              /* Default is 115200 */
++              cmd[4] = 0x00;
++              cmd[5] = 0xf3;
++              break;
++      }
++      fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
++              cmd[4], cmd[5]);
++ 
++      /* Send command */
++      if (write(fd, cmd, 6) != 6) {
++              fprintf(stderr, "Failed to write \"set baud rate\" command\n");
++              return -1;
++      }
++ 
++      if ((n = read_hci_event(fd, resp, 6)) < 0) {
++              fprintf(stderr, "Failed to set baud rate\n");
++              return -1;
++      }
++ 
++      return 0;
++ }
++ 
++ struct uart_t uart[] = {
++      { "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
++      { "ericsson",   0x0000, 0x0000, HCI_UART_H4,   57600,  115200, FLOW_CTL, NULL, ericsson },
++      { "digi",       0x0000, 0x0000, HCI_UART_H4,   9600,   115200, FLOW_CTL, NULL, digi     },
  -             err = poll(&p, 1, 500);
+++     { "texas",      0x0000, 0x0000, HCI_UART_LL,   115200, 115200, FLOW_CTL, NULL, texasalt },
++ 
++      { "bcsp",       0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
++      { "xircom",     0x0105, 0x080a, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
++ 
++      /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
++      { "csr",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, csr      },
++ 
++      /* BrainBoxes PCMCIA card (BL620) */
++      { "bboxes",     0x0160, 0x0002, HCI_UART_H4,   115200, 460800, FLOW_CTL, NULL, csr      },
++ 
++      /* Silicon Wave kits */
++      { "swave",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, swave    },
++ 
++      /* ST Microelectronics minikits based on STLC2410/STLC2415 */
++      { "st",         0x0000, 0x0000, HCI_UART_H4,    57600, 115200, FLOW_CTL, NULL, st       },
++ 
++      /* ST Microelectronics minikits based on STLC2500 */
++      { "stlc2500",   0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, "00:80:E1:00:AB:BA", stlc2500  },
++ 
++      /* Philips generic Ericsson IP core based */
++      { "philips",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
++ 
++      /* Philips BGB2xx Module */
++      { "bgb2xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, "BD:B2:10:00:AB:BA", bgb2xx   },
++ 
++      /* Sphinx Electronics PICO Card */
++      { "picocard",   0x025e, 0x1000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
++ 
++      /* Inventel BlueBird Module */
++      { "inventel",   0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
++ 
++      /* COM One Platinium Bluetooth PC Card */
++      { "comone",     0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
++      { "tdk",        0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* Socket Bluetooth CF Card (Rev G) */
++      { "socket",     0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400, 0,        NULL, bcsp     },
++ 
++      /* 3Com Bluetooth Card (Version 3.0) */
++      { "3com",       0x0101, 0x0041, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, csr      },
++ 
++      /* AmbiCom BT2000C Bluetooth PC/CF Card */
++      { "bt2000c",    0x022d, 0x2000, HCI_UART_H4,    57600, 460800, FLOW_CTL, NULL, csr      },
++ 
++      /* Zoom Bluetooth PCMCIA Card */
++      { "zoom",       0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* Sitecom CN-504 PCMCIA Card */
++      { "sitecom",    0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* Billionton PCBTC1 PCMCIA Card */
++      { "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
++ 
++      /* Broadcom BCM2035 */
++      { "bcm2035",    0x0A5C, 0x2035, HCI_UART_H4,   115200, 460800, FLOW_CTL, NULL, bcm2035  },
++ 
++      { NULL, 0 }
++ };
++ 
++ struct uart_t * get_by_id(int m_id, int p_id)
++ {
++      int i;
++      for (i = 0; uart[i].type; i++) {
++              if (uart[i].m_id == m_id && uart[i].p_id == p_id)
++                      return &uart[i];
++      }
++      return NULL;
++ }
++ 
++ struct uart_t * get_by_type(char *type)
++ {
++      int i;
++      for (i = 0; uart[i].type; i++) {
++              if (!strcmp(uart[i].type, type))
++                      return &uart[i];
++      }
++      return NULL;
++ }
++ 
++ /* Initialize UART driver */
++ int init_uart(char *dev, struct uart_t *u, int send_break)
++ {
++      struct termios ti;
++      int fd, i;
++ 
++      fd = open(dev, O_RDWR | O_NOCTTY);
++      if (fd < 0) {
++              perror("Can't open serial port");
++              return -1;
++      }
++ 
++      tcflush(fd, TCIOFLUSH);
++ 
++      if (tcgetattr(fd, &ti) < 0) {
++              perror("Can't get port settings");
++              return -1;
++      }
++ 
++      cfmakeraw(&ti);
++ 
++      ti.c_cflag |= CLOCAL;
++      if (u->flags & FLOW_CTL)
++              ti.c_cflag |= CRTSCTS;
++      else
++              ti.c_cflag &= ~CRTSCTS;
++ 
++      if (tcsetattr(fd, TCSANOW, &ti) < 0) {
++              perror("Can't set port settings");
++              return -1;
++      }
++ 
++      /* Set initial baudrate */
++      if (set_speed(fd, &ti, u->init_speed) < 0) {
++              perror("Can't set initial baud rate");
++              return -1;
++      }
++ 
++      tcflush(fd, TCIOFLUSH);
++ 
++      if (send_break) {
++              tcsendbreak(fd, 0);
++              usleep(500000);
++      }
++ 
++      if (u->init && u->init(fd, u, &ti) < 0)
++              return -1;
++ 
++      tcflush(fd, TCIOFLUSH);
++ 
++      /* Set actual baudrate */
++      if (set_speed(fd, &ti, u->speed) < 0) {
++              perror("Can't set baud rate");
++              return -1;
++      }
++ 
++      /* Set TTY to N_HCI line discipline */
++      i = N_HCI;
++      if (ioctl(fd, TIOCSETD, &i) < 0) {
++              perror("Can't set line discipline");
++              return -1;
++      }
++ 
++      if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
++              perror("Can't set device");
++              return -1;
++      }
++ 
++      return fd;
++ }
++ 
++ static void usage(void)
++ {
++      printf("hciattach - HCI UART driver initialization utility\n");
++      printf("Usage:\n");
++      printf("\thciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");
++      printf("\thciattach -l\n");
++ }
++ 
++ int main(int argc, char *argv[])
++ {
++      struct uart_t *u = NULL;
++      int detach, printpid, opt, i, n, ld, err;
++      int to = 5; 
++      int init_speed = 0;
++      int send_break = 0;
++      pid_t pid;
++      struct sigaction sa;
++      struct pollfd p;
++      char dev[PATH_MAX];
++ 
++      detach = 1;
++      printpid = 0;
++ 
++      while ((opt=getopt(argc, argv, "bnpt:s:l")) != EOF) {
++              switch(opt) {
++              case 'b':
++                      send_break = 1;
++                      break;
++ 
++              case 'n':
++                      detach = 0;
++                      break;
++ 
++              case 'p':
++                      printpid = 1;
++                      break;
++ 
++              case 't':
++                      to = atoi(optarg);
++                      break;
++ 
++              case 's':
++                      init_speed = atoi(optarg);
++                      break;
++ 
++              case 'l':
++                      for (i = 0; uart[i].type; i++) {
++                              printf("%-10s0x%04x,0x%04x\n", uart[i].type,
++                                                      uart[i].m_id, uart[i].p_id);
++                      }
++                      exit(0);
++ 
++              default:
++                      usage();
++                      exit(1);
++              }
++      }
++ 
++      n = argc - optind;
++      if (n < 2) {
++              usage();
++              exit(1);
++      }
++ 
++      for (n = 0; optind < argc; n++, optind++) {
++              char *opt;
++ 
++              opt = argv[optind];
++ 
++              switch(n) {
++              case 0:
++                      dev[0] = 0;
++                      if (!strchr(opt, '/'))
++                              strcpy(dev, "/dev/");
++                      strcat(dev, opt);
++                      break;
++ 
++              case 1:
++                      if (strchr(argv[optind], ',')) {
++                              int m_id, p_id;
++                              sscanf(argv[optind], "%x,%x", &m_id, &p_id);
++                              u = get_by_id(m_id, p_id);
++                      } else {
++                              u = get_by_type(opt);
++                      }
++ 
++                      if (!u) {
++                              fprintf(stderr, "Unknown device type or id\n");
++                              exit(1);
++                      }
++                      
++                      break;
++ 
++              case 2:
++                      u->speed = atoi(argv[optind]);
++                      break;
++ 
++              case 3:
++                      if (!strcmp("flow", argv[optind]))
++                              u->flags |=  FLOW_CTL;
++                      else
++                              u->flags &= ~FLOW_CTL;
++                      break;
++ 
++              case 4:
++                      u->bdaddr = argv[optind];
++                      break;
++              }
++      }
++ 
++      if (!u) {
++              fprintf(stderr, "Unknown device type or id\n");
++              exit(1);
++      }
++ 
++      /* If user specified a initial speed, use that instead of
++         the hardware's default */
++      if (init_speed)
++              u->init_speed = init_speed;
++ 
++      memset(&sa, 0, sizeof(sa));
++      sa.sa_flags   = SA_NOCLDSTOP;
++      sa.sa_handler = sig_alarm;
++      sigaction(SIGALRM, &sa, NULL);
++ 
++      /* 5 seconds should be enough for initialization */
++      alarm(to);
++ 
++      n = init_uart(dev, u, send_break);
++      if (n < 0) {
++              perror("Can't initialize device"); 
++              exit(1);
++      }
++ 
++      alarm(0);
++ 
++      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 (detach) {
++              if ((pid = fork())) {
++                      if (printpid)
++                              printf("%d\n", pid);
++                      return 0;
++              }
++ 
++              for (i = 0; i < 20; i++)
++                      if (i != n)
++                              close(i);
++      }
++ 
++      p.fd = n;
++      p.events = POLLERR | POLLHUP;
++ 
++      while (!__io_canceled) {
++              p.revents = 0;
+++             err = poll(&p, 1, 5000);  // 5 seconds
++              if (err < 0 && errno == EINTR)
++                      continue;
++              if (err)
++                      break;
++      }
++ 
++      /* Restore TTY line discipline */
++      ld = N_TTY;
++      if (ioctl(n, TIOCSETD, &ld) < 0) {
++              perror("Can't restore line discipline");
++              exit(1);
++      }
++ 
++      return 0;
++ }
index 0000000,0000000,0000000..34e27bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,0 +1,271 @@@@
+++/*
+++ *
+++ *   BlueZ - Bluetooth protocol stack for Linux
+++ *
+++ *   Copyright (C) 2000-2001  Qualcomm Incorporated
+++ *   Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+++ *   Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
+++ *
+++ *
+++ *   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
+++ *
+++ */
+++
+++#include <stdio.h>
+++#include <errno.h>
+++#include <fcntl.h>
+++#include <unistd.h>
+++#include <stdlib.h>
+++#include <string.h>
+++#include <signal.h>
+++#include <syslog.h>
+++#include <termios.h>
+++#include <time.h>
+++#include <sys/time.h>
+++#include <sys/poll.h>
+++#include <sys/param.h>
+++#include <sys/ioctl.h>
+++#include <sys/socket.h>
+++#include <sys/endian.h>
+++#include <sys/uio.h>
+++
+++#include <bluetooth/bluetooth.h>
+++#include <bluetooth/hci.h>
+++#include <bluetooth/hci_lib.h>
+++
+++#define FAILIF(x, args...) do {   \
+++     if (x) {                                          \
+++             fprintf(stderr, ##args);  \
+++             return -1;                                \
+++     }                                                         \
+++} while(0)
+++
+++typedef struct {
+++     uint8_t uart_prefix;
+++     hci_event_hdr hci_hdr;
+++     evt_cmd_complete cmd_complete;
+++     uint8_t status;
+++     uint8_t data[16];
+++} __attribute__((packed)) command_complete_t;
+++
+++extern int read_hci_event(int fd, unsigned char* buf, int size);
+++
+++static int read_command_complete(int fd, unsigned short opcode, unsigned char len) {
+++     command_complete_t resp;
+++     /* Read reply. */
+++     FAILIF(read_hci_event(fd, (char *)&resp, sizeof(resp)) < 0,
+++                "Failed to read response");
+++     
+++     /* Parse speed-change reply */
+++     FAILIF(resp.uart_prefix != HCI_EVENT_PKT,
+++                "Error in response: not an event packet, but 0x%02x!\n", 
+++                resp.uart_prefix);
+++
+++     FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */
+++                "Error in response: not a cmd-complete event, "
+++                "but 0x%02x!\n", resp.hci_hdr.evt);
+++
+++     FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+++                "Error in response: plen is not >= 4, but 0x%02x!\n",
+++                resp.hci_hdr.plen);
+++
+++     /* cmd-complete event: opcode */
+++     FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode,
+++                "Error in response: opcode is 0x%04x, not 0x%04x!",
+++                resp.cmd_complete.opcode, opcode);
+++
+++     return resp.status == 0 ? 0 : -1;
+++}
+++
+++typedef struct {
+++     uint8_t uart_prefix;
+++     hci_command_hdr hci_hdr;
+++     uint32_t speed;
+++} __attribute__((packed)) texas_speed_change_cmd_t;
+++
+++static int texas_change_speed(int fd, uint32_t speed) {
+++     
+++     return 0;
+++
+++     /* Send a speed-change request */
+++
+++     texas_speed_change_cmd_t cmd;
+++     int n;
+++
+++     cmd.uart_prefix = HCI_COMMAND_PKT;
+++     cmd.hci_hdr.opcode = 0xff36;
+++     cmd.hci_hdr.plen = sizeof(uint32_t);
+++     cmd.speed = speed;
+++
+++     fprintf(stdout, "Setting speed to %d\n", speed);
+++     n = write(fd, &cmd, sizeof(cmd));
+++     if (n < 0) {
+++             perror("Failed to write speed-set command");
+++             return -1;
+++     }
+++
+++     n = write(fd, &cmd, sizeof(cmd));
+++     if (n < 0) {
+++             perror("Failed to write command.");
+++             return -1;
+++     }
+++     if (n < (int)sizeof(cmd)) {
+++             fprintf(stderr, "Wanted to write %d bytes, could only write %d. "
+++                             "Stop\n", (int)sizeof(cmd), n);
+++             return -1;
+++     }
+++
+++     /* Parse speed-change reply */
+++     if (read_command_complete(fd, 0xff36, 4) < 0) {
+++             return -1;
+++     }
+++     fprintf(stdout, "Speed changed to %d.\n", speed);
+++}
+++
+++static int texas_load_firmware(int fd, const char *firmware) {
+++
+++     fprintf(stdout, "Opening firmware file: %s\n", firmware);
+++     int fw = open(firmware, O_RDONLY);
+++     FAILIF(fw < 0, 
+++                "Could not open firmware file %s: %s (%d).\n",
+++                firmware, strerror(errno), errno);
+++
+++     fprintf(stdout, "Uploading firmware...\n");
+++     do {
+++             int nr;
+++             /* Read each command and wait for a response. */
+++             unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+++             nr = read(fw, cmdp, sizeof(cmdp));
+++             if (!nr)
+++                     break;
+++             hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1);
+++             FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n");
+++             FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n");
+++             
+++             unsigned char data[1024];
+++             FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+++                        "Could not read %d bytes of data for command with opcode %04x!\n",
+++                        cmd->plen,
+++                        cmd->opcode);
+++                             
+++             {
+++#if 0
+++                     fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n", 
+++                                     cmd->opcode, 
+++                                     cmd->plen);
+++#endif
+++                     struct iovec iov_cmd[2];
+++                     iov_cmd[0].iov_base = cmdp;
+++                     iov_cmd[0].iov_len      = sizeof(cmdp);
+++                     iov_cmd[1].iov_base = data;
+++                     iov_cmd[1].iov_len      = cmd->plen;
+++                     int nw = writev(fd, iov_cmd, 2);
+++                     FAILIF(nw != sizeof(cmd) +      cmd->plen, 
+++                                "Could not send entire command (sent only %d bytes)!\n",
+++                                nw);
+++             }
+++
+++             /* Wait for response */
+++             if (read_command_complete(fd, 
+++                                                               cmd->opcode,
+++                                                               cmd->plen) < 0) {
+++                     return -1;
+++             }
+++                     
+++     } while(1);
+++     fprintf(stdout, "Firmware upload successful.\n");
+++
+++     close(fw);
+++     return 0;
+++}
+++
+++int texasalt_init(int fd, int speed, struct termios *ti)
+++{
+++     struct timespec tm = {0, 50000};
+++     char cmd[4];
+++     unsigned char resp[100];                /* Response */
+++     int n;
+++
+++     memset(resp,'\0', 100);
+++
+++     /* It is possible to get software version with manufacturer specific 
+++        HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+++        is if this is point-to-point or point-to-multipoint module */
+++
+++     /* Get Manufacturer and LMP version */
+++     cmd[0] = HCI_COMMAND_PKT;
+++     cmd[1] = 0x01;
+++     cmd[2] = 0x10;
+++     cmd[3] = 0x00;
+++
+++     do {
+++             n = write(fd, cmd, 4);
+++             if (n < 0) {
+++                     perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+++                     return -1;
+++             }
+++             if (n < 4) {
+++                     fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+++                     return -1;
+++             }
+++
+++             /* Read reply. */
+++             if (read_hci_event(fd, resp, 100) < 0) {
+++                     perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+++                     return -1;
+++             }
+++
+++             /* Wait for command complete event for our Opcode */
+++     } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+++
+++     /* Verify manufacturer */
+++     if ((resp[11] & 0xFF) != 0x0d)
+++             fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
+++
+++     /* Print LMP version */
+++     fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
+++
+++     /* Print LMP subversion */
+++     {
+++             unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+++             unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10;
+++             static const char *c_brf_chip[5] = {
+++                     "unknown",
+++                     "unknown",
+++                     "brf6100",
+++                     "brf6150",
+++                     "brf6300"
+++             };
+++
+++             fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv);
+++
+++             fprintf(stderr,
+++                             "\tinternal version freeze: %d\n"
+++                             "\tsoftware version: %d\n"
+++                             "\tchip: %s (%d)\n",
+++                             lmp_subv & 0x7f,
+++                             ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7),
+++                             ((brf_chip > 4) ? "unknown" : c_brf_chip[brf_chip]),
+++                             brf_chip);
+++
+++             char fw[100];
+++             sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]);
+++             texas_load_firmware(fd, fw);
+++
+++             texas_change_speed(fd, speed);
+++     }
+++     nanosleep(&tm, NULL);
+++     return 0;
+++}
index 0000000,0000000,1988239..712c62b
mode 000000,000000,100644..100644
--- /dev/null
--- /dev/null
@@@@ -1,0 -1,0 -1,4055 +1,4134 @@@@
  -     sdp_set_info_attr(&record, "Serial Port", 0, "COM Port");
++ /*
++  *
++  *  BlueZ - Bluetooth protocol stack for Linux
++  *
++  *  Copyright (C) 2001-2002  Nokia Corporation
++  *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
++  *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org>
++  *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
++  *  Copyright (C) 2002-2003  Jean Tourrilhes <jt@hpl.hp.com>
++  *
++  *
++  *  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 <config.h>
++ #endif
++ 
++ #include <stdio.h>
++ #include <errno.h>
++ #include <ctype.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <getopt.h>
++ #include <sys/socket.h>
++ 
++ #include <bluetooth/bluetooth.h>
++ #include <bluetooth/hci.h>
++ #include <bluetooth/hci_lib.h>
++ #include <bluetooth/sdp.h>
++ #include <bluetooth/sdp_lib.h>
++ 
++ #include <netinet/in.h>
++ 
++ #include "sdp-xml.h"
++ 
++ #ifndef APPLE_AGENT_SVCLASS_ID
++ #define APPLE_AGENT_SVCLASS_ID 0x2112
++ #endif
++ 
++ #define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
++ 
++ /*
++  * Convert a string to a BDADDR, with a few "enhancements" - Jean II
++  */
++ static int estr2ba(char *str, bdaddr_t *ba)
++ {
++      /* Only trap "local", "any" is already dealt with */
++      if(!strcmp(str, "local")) {
++              bacpy(ba, BDADDR_LOCAL);
++              return 0;
++      }
++      return str2ba(str, ba);
++ }
++ 
++ #define DEFAULT_VIEW 0       /* Display only known attribute */
++ #define TREE_VIEW    1       /* Display full attribute tree */
++ #define RAW_VIEW     2       /* Display raw tree */
++ #define XML_VIEW     3       /* Display xml tree */
++ 
++ /* Pass args to the inquiry/search handler */
++ struct search_context {
++      char            *svc;           /* Service */
++      uuid_t          group;          /* Browse group */
++      int             view;           /* View mode */
++      uint32_t        handle;         /* Service record handle */
++ };
++ 
++ typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
++ 
++ static char UUID_str[MAX_LEN_UUID_STR];
++ static bdaddr_t interface;
++ 
++ /* Definition of attribute members */
++ struct member_def {
++      char *name;
++ };
++ 
++ /* Definition of an attribute */
++ struct attrib_def {
++      int                     num;            /* Numeric ID - 16 bits */
++      char                    *name;          /* User readable name */
++      struct member_def       *members;       /* Definition of attribute args */
++      int                     member_max;     /* Max of attribute arg definitions */
++ };
++ 
++ /* Definition of a service or protocol */
++ struct uuid_def {
++      int                     num;            /* Numeric ID - 16 bits */
++      char                    *name;          /* User readable name */
++      struct attrib_def       *attribs;       /* Specific attribute definitions */
++      int                     attrib_max;     /* Max of attribute definitions */
++ };
++ 
++ /* Context information about current attribute */
++ struct attrib_context {
++      struct uuid_def         *service;       /* Service UUID, if known */
++      struct attrib_def       *attrib;        /* Description of the attribute */
++      int                     member_index;   /* Index of current attribute member */
++ };
++ 
++ /* Context information about the whole service */
++ struct service_context {
++      struct uuid_def         *service;       /* Service UUID, if known */
++ };
++ 
++ /* Allow us to do nice formatting of the lists */
++ static char *indent_spaces = "                                         ";
++ 
++ /* ID of the service attribute.
++  * Most attributes after 0x200 are defined based on the service, so
++  * we need to find what is the service (which is messy) - Jean II */
++ #define SERVICE_ATTR 0x1
++ 
++ /* Definition of the optional arguments in protocol list */
++ static struct member_def protocol_members[] = {
++      { "Protocol"            },
++      { "Channel/Port"        },
++      { "Version"             },
++ };
++ 
++ /* Definition of the optional arguments in profile list */
++ static struct member_def profile_members[] = {
++      { "Profile"     },
++      { "Version"     },
++ };
++ 
++ /* Definition of the optional arguments in Language list */
++ static struct member_def language_members[] = {
++      { "Code ISO639"         },
++      { "Encoding"            },
++      { "Base Offset"         },
++ };
++ 
++ /* Name of the various common attributes. See BT assigned numbers */
++ static struct attrib_def attrib_names[] = {
++      { 0x0, "ServiceRecordHandle", NULL, 0 },
++      { 0x1, "ServiceClassIDList", NULL, 0 },
++      { 0x2, "ServiceRecordState", NULL, 0 },
++      { 0x3, "ServiceID", NULL, 0 },
++      { 0x4, "ProtocolDescriptorList",
++              protocol_members, sizeof(protocol_members)/sizeof(struct member_def) },
++      { 0x5, "BrowseGroupList", NULL, 0 },
++      { 0x6, "LanguageBaseAttributeIDList",
++              language_members, sizeof(language_members)/sizeof(struct member_def) },
++      { 0x7, "ServiceInfoTimeToLive", NULL, 0 },
++      { 0x8, "ServiceAvailability", NULL, 0 },
++      { 0x9, "BluetoothProfileDescriptorList",
++              profile_members, sizeof(profile_members)/sizeof(struct member_def) },
++      { 0xA, "DocumentationURL", NULL, 0 },
++      { 0xB, "ClientExecutableURL", NULL, 0 },
++      { 0xC, "IconURL", NULL, 0 },
++      { 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
++      /* Definitions after that are tricky (per profile or offset) */
++ };
++ 
++ const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def);
++ 
++ /* Name of the various SPD attributes. See BT assigned numbers */
++ static struct attrib_def sdp_attrib_names[] = {
++      { 0x200, "VersionNumberList", NULL, 0 },
++      { 0x201, "ServiceDatabaseState", NULL, 0 },
++ };
++ 
++ /* Name of the various SPD attributes. See BT assigned numbers */
++ static struct attrib_def browse_attrib_names[] = {
++      { 0x200, "GroupID", NULL, 0 },
++ };
++ 
++ /* Name of the various Device ID attributes. See Device Id spec. */
++ static struct attrib_def did_attrib_names[] = {
++      { 0x200, "SpecificationID", NULL, 0 },
++      { 0x201, "VendorID", NULL, 0 },
++      { 0x202, "ProductID", NULL, 0 },
++      { 0x203, "Version", NULL, 0 },
++      { 0x204, "PrimaryRecord", NULL, 0 },
++      { 0x205, "VendorIDSource", NULL, 0 },
++ };
++ 
++ /* Name of the various HID attributes. See HID spec. */
++ static struct attrib_def hid_attrib_names[] = {
++      { 0x200, "DeviceReleaseNum", NULL, 0 },
++      { 0x201, "ParserVersion", NULL, 0 },
++      { 0x202, "DeviceSubclass", NULL, 0 },
++      { 0x203, "CountryCode", NULL, 0 },
++      { 0x204, "VirtualCable", NULL, 0 },
++      { 0x205, "ReconnectInitiate", NULL, 0 },
++      { 0x206, "DescriptorList", NULL, 0 },
++      { 0x207, "LangIDBaseList", NULL, 0 },
++      { 0x208, "SDPDisable", NULL, 0 },
++      { 0x209, "BatteryPower", NULL, 0 },
++      { 0x20a, "RemoteWakeup", NULL, 0 },
++      { 0x20b, "ProfileVersion", NULL, 0 },
++      { 0x20c, "SupervisionTimeout", NULL, 0 },
++      { 0x20d, "NormallyConnectable", NULL, 0 },
++      { 0x20e, "BootDevice", NULL, 0 },
++ };
++ 
++ /* Name of the various PAN attributes. See BT assigned numbers */
++ /* Note : those need to be double checked - Jean II */
++ static struct attrib_def pan_attrib_names[] = {
++      { 0x200, "IpSubnet", NULL, 0 },         /* Obsolete ??? */
++      { 0x30A, "SecurityDescription", NULL, 0 },
++      { 0x30B, "NetAccessType", NULL, 0 },
++      { 0x30C, "MaxNetAccessrate", NULL, 0 },
++      { 0x30D, "IPv4Subnet", NULL, 0 },
++      { 0x30E, "IPv6Subnet", NULL, 0 },
++ };
++ 
++ /* Name of the various Generic-Audio attributes. See BT assigned numbers */
++ /* Note : totally untested - Jean II */
++ static struct attrib_def audio_attrib_names[] = {
++      { 0x302, "Remote audio volume control", NULL, 0 },
++ };
++ 
++ /* Same for the UUIDs. See BT assigned numbers */
++ static struct uuid_def uuid16_names[] = {
++      /* -- Protocols -- */
++      { 0x0001, "SDP", NULL, 0 },
++      { 0x0002, "UDP", NULL, 0 },
++      { 0x0003, "RFCOMM", NULL, 0 },
++      { 0x0004, "TCP", NULL, 0 },
++      { 0x0005, "TCS-BIN", NULL, 0 },
++      { 0x0006, "TCS-AT", NULL, 0 },
++      { 0x0008, "OBEX", NULL, 0 },
++      { 0x0009, "IP", NULL, 0 },
++      { 0x000a, "FTP", NULL, 0 },
++      { 0x000c, "HTTP", NULL, 0 },
++      { 0x000e, "WSP", NULL, 0 },
++      { 0x000f, "BNEP", NULL, 0 },
++      { 0x0010, "UPnP/ESDP", NULL, 0 },
++      { 0x0011, "HIDP", NULL, 0 },
++      { 0x0012, "HardcopyControlChannel", NULL, 0 },
++      { 0x0014, "HardcopyDataChannel", NULL, 0 },
++      { 0x0016, "HardcopyNotification", NULL, 0 },
++      { 0x0017, "AVCTP", NULL, 0 },
++      { 0x0019, "AVDTP", NULL, 0 },
++      { 0x001b, "CMTP", NULL, 0 },
++      { 0x001d, "UDI_C-Plane", NULL, 0 },
++      { 0x0100, "L2CAP", NULL, 0 },
++      /* -- Services -- */
++      { 0x1000, "ServiceDiscoveryServerServiceClassID",
++              sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1001, "BrowseGroupDescriptorServiceClassID",
++              browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1002, "PublicBrowseGroup", NULL, 0 },
++      { 0x1101, "SerialPort", NULL, 0 },
++      { 0x1102, "LANAccessUsingPPP", NULL, 0 },
++      { 0x1103, "DialupNetworking (DUN)", NULL, 0 },
++      { 0x1104, "IrMCSync", NULL, 0 },
++      { 0x1105, "OBEXObjectPush", NULL, 0 },
++      { 0x1106, "OBEXFileTransfer", NULL, 0 },
++      { 0x1107, "IrMCSyncCommand", NULL, 0 },
++      { 0x1108, "Headset",
++              audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1109, "CordlessTelephony", NULL, 0 },
++      { 0x110a, "AudioSource", NULL, 0 },
++      { 0x110b, "AudioSink", NULL, 0 },
++      { 0x110c, "RemoteControlTarget", NULL, 0 },
++      { 0x110d, "AdvancedAudio", NULL, 0 },
++      { 0x110e, "RemoteControl", NULL, 0 },
++      { 0x110f, "VideoConferencing", NULL, 0 },
++      { 0x1110, "Intercom", NULL, 0 },
++      { 0x1111, "Fax", NULL, 0 },
++      { 0x1112, "HeadsetAudioGateway", NULL, 0 },
++      { 0x1113, "WAP", NULL, 0 },
++      { 0x1114, "WAP Client", NULL, 0 },
++      { 0x1115, "PANU (PAN/BNEP)",
++              pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1116, "NAP (PAN/BNEP)",
++              pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1117, "GN (PAN/BNEP)",
++              pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1118, "DirectPrinting (BPP)", NULL, 0 },
++      { 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
++      { 0x111a, "Imaging (BIP)", NULL, 0 },
++      { 0x111b, "ImagingResponder (BIP)", NULL, 0 },
++      { 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
++      { 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
++      { 0x111e, "Handsfree", NULL, 0 },
++      { 0x111f, "HandsfreeAudioGateway", NULL, 0 },
++      { 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
++      { 0x1121, "ReflectedUI (BPP)", NULL, 0 },
++      { 0x1122, "BasicPrinting (BPP)", NULL, 0 },
++      { 0x1123, "PrintingStatus (BPP)", NULL, 0 },
++      { 0x1124, "HumanInterfaceDeviceService (HID)",
++              hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
++      { 0x1126, "HCR_Print (HCR)", NULL, 0 },
++      { 0x1127, "HCR_Scan (HCR)", NULL, 0 },
++      { 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
++      { 0x1129, "VideoConferencingGW (VCP)", NULL, 0 },
++      { 0x112a, "UDI-MT", NULL, 0 },
++      { 0x112b, "UDI-TA", NULL, 0 },
++      { 0x112c, "Audio/Video", NULL, 0 },
++      { 0x112d, "SIM Access (SAP)", NULL, 0 },
++      { 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
++      { 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 },
++      { 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
++      /* ... */
++      { 0x1200, "PnPInformation",
++              did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1201, "GenericNetworking", NULL, 0 },
++      { 0x1202, "GenericFileTransfer", NULL, 0 },
++      { 0x1203, "GenericAudio",
++              audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
++      { 0x1204, "GenericTelephony", NULL, 0 },
++      /* ... */
++      { 0x1303, "VideoSource", NULL, 0 },
++      { 0x1304, "VideoSink", NULL, 0 },
++      { 0x1305, "VideoDistribution", NULL, 0 },
++      { 0x1400, "MDP", NULL, 0 },
++      { 0x1401, "MDPSource", NULL, 0 },
++      { 0x1402, "MDPSink", NULL, 0 },
++      { 0x2112, "AppleAgent", NULL, 0 },
++ };
++ 
++ static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def);
++ 
++ static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
++ 
++ /*
++  * Parse a UUID.
++  * The BT assigned numbers only list UUID16, so I'm not sure the
++  * other types will ever get used...
++  */
++ static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
++ {
++      if (uuid) {
++              if (uuid->type == SDP_UUID16) {
++                      uint16_t uuidNum = uuid->value.uuid16;
++                      struct uuid_def *uuidDef = NULL;
++                      int i;
++ 
++                      for (i = 0; i < uuid16_max; i++)
++                              if (uuid16_names[i].num == uuidNum) {
++                                      uuidDef = &uuid16_names[i];
++                                      break;
++                              }
++ 
++                      /* Check if it's the service attribute */
++                      if (context->attrib && context->attrib->num == SERVICE_ATTR) {
++                              /* We got the service ID !!! */
++                              context->service = uuidDef;
++                      }
++ 
++                      if (uuidDef)
++                              printf("%.*sUUID16 : 0x%.4x - %s\n",
++                                      indent, indent_spaces, uuidNum, uuidDef->name);
++                      else
++                              printf("%.*sUUID16 : 0x%.4x\n",
++                                      indent, indent_spaces, uuidNum);
++              } else if (uuid->type == SDP_UUID32) {
++                      struct uuid_def *uuidDef = NULL;
++                      int i;
++ 
++                      if (!(uuid->value.uuid32 & 0xffff0000)) {
++                              uint16_t uuidNum = uuid->value.uuid32;
++                              for (i = 0; i < uuid16_max; i++)
++                                      if (uuid16_names[i].num == uuidNum) {
++                                              uuidDef = &uuid16_names[i];
++                                              break;
++                                      }
++                      }
++ 
++                      if (uuidDef)
++                              printf("%.*sUUID32 : 0x%.8x - %s\n",
++                                      indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
++                      else
++                              printf("%.*sUUID32 : 0x%.8x\n",
++                                      indent, indent_spaces, uuid->value.uuid32);
++              } else if (uuid->type == SDP_UUID128) {
++                      unsigned int data0;
++                      unsigned short data1;
++                      unsigned short data2;
++                      unsigned short data3;
++                      unsigned int data4;
++                      unsigned short data5;
++ 
++                      memcpy(&data0, &uuid->value.uuid128.data[0], 4);
++                      memcpy(&data1, &uuid->value.uuid128.data[4], 2);
++                      memcpy(&data2, &uuid->value.uuid128.data[6], 2);
++                      memcpy(&data3, &uuid->value.uuid128.data[8], 2);
++                      memcpy(&data4, &uuid->value.uuid128.data[10], 4);
++                      memcpy(&data5, &uuid->value.uuid128.data[14], 2);
++ 
++                      printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
++                              indent, indent_spaces,
++                              ntohl(data0), ntohs(data1), ntohs(data2),
++                              ntohs(data3), ntohl(data4), ntohs(data5));
++              } else
++                      printf("%.*sEnum type of UUID not set\n",
++                              indent, indent_spaces);
++      } else
++              printf("%.*sNull passed to print UUID\n",
++                              indent, indent_spaces);
++ }
++ 
++ /*
++  * Parse a sequence of data elements (i.e. a list)
++  */
++ static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
++ {
++      sdp_data_t *sdpdata = NULL;
++ 
++      sdpdata = pData;
++      if (sdpdata) {
++              context->member_index = 0;
++              do {
++                      sdp_data_printf(sdpdata, context, indent + 2);
++                      sdpdata = sdpdata->next;
++                      context->member_index++;
++              } while (sdpdata);
++      } else {
++              printf("%.*sBroken dataseq link\n", indent, indent_spaces);
++      }
++ }
++ 
++ /*
++  * Parse a single data element (either in the attribute or in a data
++  * sequence).
++  */
++ static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
++ {
++      char *member_name = NULL;
++ 
++      /* Find member name. Almost black magic ;-) */
++      if (context && context->attrib && context->attrib->members &&
++                      context->member_index < context->attrib->member_max) {
++              member_name = context->attrib->members[context->member_index].name;
++      }
++ 
++      switch (sdpdata->dtd) {
++      case SDP_DATA_NIL:
++              printf("%.*sNil\n", indent, indent_spaces);
++              break;
++      case SDP_BOOL:
++      case SDP_UINT8:
++      case SDP_UINT16:
++      case SDP_UINT32:
++      case SDP_UINT64:
++      case SDP_UINT128:
++      case SDP_INT8:
++      case SDP_INT16:
++      case SDP_INT32:
++      case SDP_INT64:
++      case SDP_INT128:
++              if (member_name) {
++                      printf("%.*s%s (Integer) : 0x%x\n",
++                              indent, indent_spaces, member_name, sdpdata->val.uint32);
++              } else {
++                      printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
++                              sdpdata->val.uint32);
++              }
++              break;
++ 
++      case SDP_UUID16:
++      case SDP_UUID32:
++      case SDP_UUID128:
++              //printf("%.*sUUID\n", indent, indent_spaces);
++              sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
++              break;
++ 
++      case SDP_TEXT_STR8:
++      case SDP_TEXT_STR16:
++      case SDP_TEXT_STR32:
++              if (sdpdata->unitSize > strlen(sdpdata->val.str)) {
++                      int i;
++                      printf("%.*sData :", indent, indent_spaces);
++                      for (i = 0; i < sdpdata->unitSize; i++)
++                              printf(" %02x", (unsigned char) sdpdata->val.str[i]);
++                      printf("\n");
++              } else
++                      printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
++              break;
++      case SDP_URL_STR8:
++      case SDP_URL_STR16:
++      case SDP_URL_STR32:
++              printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
++              break;
++ 
++      case SDP_SEQ8:
++      case SDP_SEQ16:
++      case SDP_SEQ32:
++              printf("%.*sData Sequence\n", indent, indent_spaces);
++              printf_dataseq(sdpdata->val.dataseq, context, indent);
++              break;
++ 
++      case SDP_ALT8:
++      case SDP_ALT16:
++      case SDP_ALT32:
++              printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
++              printf_dataseq(sdpdata->val.dataseq, context, indent);
++              break;
++      }
++ }
++ 
++ /*
++  * Parse a single attribute.
++  */
++ static void print_tree_attr_func(void *value, void *userData)
++ {
++      sdp_data_t *sdpdata = NULL;
++      uint16_t attrId;
++      struct service_context *service = (struct service_context *) userData;
++      struct attrib_context context;
++      struct attrib_def *attrDef = NULL;
++      int i;
++ 
++      sdpdata = (sdp_data_t *)value;
++      attrId = sdpdata->attrId;
++      /* Search amongst the generic attributes */
++      for (i = 0; i < attrib_max; i++)
++              if (attrib_names[i].num == attrId) {
++                      attrDef = &attrib_names[i];
++                      break;
++              }
++      /* Search amongst the specific attributes of this service */
++      if ((attrDef == NULL) && (service->service != NULL) &&
++                              (service->service->attribs != NULL)) {
++              struct attrib_def *svc_attribs = service->service->attribs;
++              int             svc_attrib_max = service->service->attrib_max;
++              for (i = 0; i < svc_attrib_max; i++)
++                      if (svc_attribs[i].num == attrId) {
++                              attrDef = &svc_attribs[i];
++                              break;
++                      }
++      }
++ 
++      if (attrDef)
++              printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
++      else
++              printf("Attribute Identifier : 0x%x\n", attrId);
++      /* Build context */
++      context.service = service->service;
++      context.attrib = attrDef;
++      context.member_index = 0;
++      /* Parse attribute members */
++      if (sdpdata)
++              sdp_data_printf(sdpdata, &context, 2);
++      else
++              printf("  NULL value\n");
++      /* Update service */
++      service->service = context.service;
++ }
++ 
++ /*
++  * Main entry point of this library. Parse a SDP record.
++  * We assume the record has already been read, parsed and cached
++  * locally. Jean II
++  */
++ static void print_tree_attr(sdp_record_t *rec)
++ {
++      if (rec && rec->attrlist) {
++              struct service_context service = { NULL };
++              sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
++      }
++ }
++ 
++ static void print_raw_data(sdp_data_t *data, int indent)
++ {
++      struct uuid_def *def;
++      int i, hex;
++ 
++      if (!data)
++              return;
++ 
++      for (i = 0; i < indent; i++)
++              printf("\t");
++ 
++      switch (data->dtd) {
++      case SDP_DATA_NIL:
++              printf("NIL\n");
++              break;
++      case SDP_BOOL:
++              printf("Bool %s\n", data->val.uint8 ? "True" : "False");
++              break;
++      case SDP_UINT8:
++              printf("UINT8 0x%02x\n", data->val.uint8);
++              break;
++      case SDP_UINT16:
++              printf("UINT16 0x%04x\n", data->val.uint16);
++              break;
++      case SDP_UINT32:
++              printf("UINT32 0x%08x\n", data->val.uint32);
++              break;
++      case SDP_UINT64:
++              printf("UINT64 0x%016jx\n", data->val.uint64);
++              break;
++      case SDP_UINT128:
++              printf("UINT128 ...\n");
++              break;
++      case SDP_INT8:
++              printf("INT8 %d\n", data->val.int8);
++              break;
++      case SDP_INT16:
++              printf("INT16 %d\n", data->val.int16);
++              break;
++      case SDP_INT32:
++              printf("INT32 %d\n", data->val.int32);
++              break;
++      case SDP_INT64:
++              printf("INT64 %jd\n", data->val.int64);
++              break;
++      case SDP_INT128:
++              printf("INT128 ...\n");
++              break;
++      case SDP_UUID16:
++      case SDP_UUID32:
++      case SDP_UUID128:
++              switch (data->val.uuid.type) {
++              case SDP_UUID16:
++                      def = NULL;
++                      for (i = 0; i < uuid16_max; i++)
++                              if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
++                                      def = &uuid16_names[i];
++                                      break;
++                              }
++                      if (def)
++                              printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
++                      else
++                              printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
++                      break;
++              case SDP_UUID32:
++                      def = NULL;
++                      if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
++                              uint16_t value = data->val.uuid.value.uuid32;
++                              for (i = 0; i < uuid16_max; i++)
++                                      if (uuid16_names[i].num == value) {
++                                              def = &uuid16_names[i];
++                                              break;
++                                      }
++                      }
++                      if (def)
++                              printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
++                      else
++                              printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
++                      break;
++              case SDP_UUID128:
++                      printf("UUID128 ");
++                      for (i = 0; i < 16; i++) {
++                              switch (i) {
++                              case 4:
++                              case 6:
++                              case 8:
++                              case 10:
++                                      printf("-");
++                                      break;
++                              }
++                              printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
++                      }
++                      printf("\n");
++                      break;
++              default:
++                      printf("UUID type 0x%02x\n", data->val.uuid.type);
++                      break;
++              }
++              break;
++      case SDP_TEXT_STR8:
++      case SDP_TEXT_STR16:
++      case SDP_TEXT_STR32:
++              hex = 0;
++              for (i = 0; i < data->unitSize; i++) {
++                      if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
++                              break;
++                      if (!isprint(data->val.str[i])) {
++                              hex = 1;
++                              break;
++                      }
++              }
++              if (hex) {
++                      printf("Data");
++                      for (i = 0; i < data->unitSize; i++)
++                              printf(" %02x", (unsigned char) data->val.str[i]);
++              } else {
++                      printf("String ");
++                      for (i = 0; i < data->unitSize; i++)
++                              printf("%c", data->val.str[i]);
++              }
++              printf("\n");
++              break;
++      case SDP_URL_STR8:
++      case SDP_URL_STR16:
++      case SDP_URL_STR32:
++              printf("URL %s\n", data->val.str);
++              break;
++      case SDP_SEQ8:
++      case SDP_SEQ16:
++      case SDP_SEQ32:
++              printf("Sequence\n");
++              print_raw_data(data->val.dataseq, indent + 1);
++              break;
++      case SDP_ALT8:
++      case SDP_ALT16:
++      case SDP_ALT32:
++              printf("Alternate\n");
++              print_raw_data(data->val.dataseq, indent + 1);
++              break;
++      default:
++              printf("Unknown type 0x%02x\n", data->dtd);
++              break;
++      }
++ 
++      print_raw_data(data->next, indent);
++ }
++ 
++ static void print_raw_attr_func(void *value, void *userData)
++ {
++      sdp_data_t *data = (sdp_data_t *) value;
++      struct attrib_def *def = NULL;
++      int i;
++ 
++      /* Search amongst the generic attributes */
++      for (i = 0; i < attrib_max; i++)
++              if (attrib_names[i].num == data->attrId) {
++                      def = &attrib_names[i];
++                      break;
++              }
++ 
++      if (def)
++              printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
++      else
++              printf("\tAttribute 0x%04x\n", data->attrId);
++ 
++      if (data)
++              print_raw_data(data, 2);
++      else
++              printf("  NULL value\n");
++ }
++ 
++ static void print_raw_attr(sdp_record_t *rec)
++ {
++      if (rec && rec->attrlist) {
++              printf("Sequence\n");
++              sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
++      }
++ }
++ 
++ /*
++  * Set attributes with single values in SDP record
++  * Jean II
++  */
++ static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value) 
++ {
++      sdp_list_t *attrid_list;
++      uint32_t range = 0x0000ffff;
++      sdp_record_t *rec;
++      int ret;
++ 
++      /* Get the old SDP record */
++      attrid_list = sdp_list_append(NULL, &range);
++      rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
++      sdp_list_free(attrid_list, NULL);
++ 
++      if (!rec) {
++              printf("Service get request failed.\n");
++              return -1;
++      }
++ 
++      /* Check the type of attribute */
++      if (!strncasecmp(value, "u0x", 3)) {
++              /* UUID16 */
++              uint16_t value_int = 0;
++              uuid_t value_uuid;
++              value_int = strtoul(value + 3, NULL, 16);
++              sdp_uuid16_create(&value_uuid, value_int);
++              printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
++                      attrib, value_int, handle);
++ 
++              sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
++      } else if (!strncasecmp(value, "0x", 2)) {
++              /* Int */
++              uint32_t value_int;  
++              value_int = strtoul(value + 2, NULL, 16);
++              printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
++                      attrib, value_int, handle);
++ 
++              sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
++      } else {
++              /* String */
++              printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
++                      attrib, value, handle);
++ 
++              /* Add/Update our attribute to the record */
++              sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
++      }
++ 
++      /* Update on the server */
++      ret = sdp_device_record_update(sess, &interface, rec);
++      if (ret < 0)
++              printf("Service Record update failed (%d).\n", errno);
++      sdp_record_free(rec);
++      return ret;
++ }
++ 
++ static struct option set_options[] = {
++      { "help",       0, 0, 'h' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *set_help = 
++      "Usage:\n"
++      "\tget record_handle attrib_id attrib_value\n";
++ 
++ /*
++  * Add an attribute to an existing SDP record on the local SDP server
++  */
++ static int cmd_setattr(int argc, char **argv)
++ {
++      int opt, status;
++      uint32_t handle;
++      uint16_t attrib;
++      sdp_session_t *sess;
++ 
++      for_each_opt(opt, set_options, NULL) {
++              switch(opt) {
++              default:
++                      printf(set_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 3) {
++              printf(set_help);
++              return -1;
++      }
++ 
++      /* Convert command line args */
++      handle = strtoul(argv[0], NULL, 16);
++      attrib = strtoul(argv[1], NULL, 16);
++ 
++      /* Do it */
++      sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
++      if (!sess)
++              return -1;
++ 
++      status = set_attrib(sess, handle, attrib, argv[2]);
++      sdp_close(sess);
++ 
++      return status;
++ }
++ 
++ /*
++  * We do only simple data sequences. Sequence of sequences is a pain ;-)
++  * Jean II
++  */
++ static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
++ {
++      sdp_list_t *attrid_list;
++      uint32_t range = 0x0000ffff;
++      sdp_record_t *rec;
++      sdp_data_t *pSequenceHolder = NULL;
++      void **dtdArray;
++      void **valueArray;
++      void **allocArray;
++      uint8_t uuid16 = SDP_UUID16;
++      uint8_t uint32 = SDP_UINT32;
++      uint8_t str8 = SDP_TEXT_STR8;
++      int i, ret = 0;
++ 
++      /* Get the old SDP record */
++      attrid_list = sdp_list_append(NULL, &range);
++      rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
++      sdp_list_free(attrid_list, NULL);
++ 
++      if (!rec) {
++              printf("Service get request failed.\n");
++              return -1;
++      }
++ 
++      /* Create arrays */
++      dtdArray = (void **)malloc(argc * sizeof(void *));
++      valueArray = (void **)malloc(argc * sizeof(void *));
++      allocArray = (void **)malloc(argc * sizeof(void *));
++ 
++      /* Loop on all args, add them in arrays */
++      for (i = 0; i < argc; i++) {
++              /* Check the type of attribute */
++              if (!strncasecmp(argv[i], "u0x", 3)) {
++                      /* UUID16 */
++                      uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
++                      uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t));
++                      allocArray[i] = value_uuid;
++                      sdp_uuid16_create(value_uuid, value_int);
++ 
++                      printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
++                      dtdArray[i] = &uuid16;
++                      valueArray[i] = &value_uuid->value.uuid16;
++              } else if (!strncasecmp(argv[i], "0x", 2)) {
++                      /* Int */
++                      uint32_t *value_int = (uint32_t *) malloc(sizeof(int));
++                      allocArray[i] = value_int;
++                      *value_int = strtoul((argv[i]) + 2, NULL, 16);
++ 
++                      printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
++                      dtdArray[i] = &uint32;
++                      valueArray[i] = value_int;
++              } else {
++                      /* String */
++                      printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
++                      dtdArray[i] = &str8;
++                      valueArray[i] = argv[i];
++              }
++      }
++ 
++      /* Add this sequence to the attrib list */
++      pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
++      if (pSequenceHolder) {
++              sdp_attr_replace(rec, attrib, pSequenceHolder);
++ 
++              /* Update on the server */
++              ret = sdp_device_record_update(session, &interface, rec);
++              if (ret < 0)
++                      printf("Service Record update failed (%d).\n", errno);
++      } else
++              printf("Failed to create pSequenceHolder\n");
++ 
++      /* Cleanup */
++      for (i = 0; i < argc; i++)
++              free(allocArray[i]);
++ 
++      free(dtdArray);
++      free(valueArray);
++      free(allocArray);
++ 
++      sdp_record_free(rec);
++ 
++      return ret;
++ }
++ 
++ static struct option seq_options[] = {
++      { "help",       0, 0, 'h' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *seq_help = 
++      "Usage:\n"
++      "\tget record_handle attrib_id attrib_values\n";
++ 
++ /*
++  * Add an attribute sequence to an existing SDP record
++  * on the local SDP server
++  */
++ static int cmd_setseq(int argc, char **argv)
++ {
++      int opt, status;
++      uint32_t handle;
++      uint16_t attrib;
++      sdp_session_t *sess;
++ 
++      for_each_opt(opt, seq_options, NULL) {
++              switch(opt) {
++              default:
++                      printf(seq_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 3) {
++              printf(seq_help);
++              return -1;
++      }
++ 
++      /* Convert command line args */
++      handle = strtoul(argv[0], NULL, 16);
++      attrib = strtoul(argv[1], NULL, 16);
++ 
++      argc -= 2;
++      argv += 2;
++ 
++      /* Do it */
++      sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
++      if (!sess)
++              return -1;
++ 
++      status = set_attribseq(sess, handle, attrib, argc, argv);
++      sdp_close(sess);
++ 
++      return status;
++ }
++ 
++ static void print_service_class(void *value, void *userData)
++ {
++      char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
++      uuid_t *uuid = (uuid_t *)value;
++ 
++      sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
++      sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
++      if (uuid->type != SDP_UUID128)
++              printf("  \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
++      else
++              printf("  UUID 128: %s\n", UUID_str);
++ }
++ 
++ static void print_service_desc(void *value, void *user)
++ {
++      char str[MAX_LEN_PROTOCOL_UUID_STR];
++      sdp_data_t *p = (sdp_data_t *)value, *s;
++      int i = 0, proto = 0;
++ 
++      for (; p; p = p->next, i++) {
++              switch (p->dtd) {
++              case SDP_UUID16:
++              case SDP_UUID32:
++              case SDP_UUID128:
++                      sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
++                      sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
++                      proto = sdp_uuid_to_proto(&p->val.uuid);
++                      printf("  \"%s\" (0x%s)\n", str, UUID_str);
++                      break;
++              case SDP_UINT8:
++                      if (proto == RFCOMM_UUID)
++                              printf("    Channel: %d\n", p->val.uint8);
++                      else
++                              printf("    uint8: 0x%x\n", p->val.uint8);
++                      break;
++              case SDP_UINT16:
++                      if (proto == L2CAP_UUID) {
++                              if (i == 1)
++                                      printf("    PSM: %d\n", p->val.uint16);
++                              else
++                                      printf("    Version: 0x%04x\n", p->val.uint16);
++                      } else if (proto == BNEP_UUID)
++                              if (i == 1)
++                                      printf("    Version: 0x%04x\n", p->val.uint16);
++                              else
++                                      printf("    uint16: 0x%x\n", p->val.uint16);
++                      else
++                              printf("    uint16: 0x%x\n", p->val.uint16);
++                      break;
++              case SDP_SEQ16:
++                      printf("    SEQ16:");
++                      for (s = p->val.dataseq; s; s = s->next)
++                              printf(" %x", s->val.uint16);
++                      printf("\n");
++                      break;
++              case SDP_SEQ8:
++                      printf("    SEQ8:");
++                      for (s = p->val.dataseq; s; s = s->next)
++                              printf(" %x", s->val.uint8);
++                      printf("\n");
++                      break;
++              default:
++                      printf("    FIXME: dtd=0%x\n", p->dtd);
++                      break;
++              }
++      }
++ }
++ 
++ static void print_lang_attr(void *value, void *user)
++ {
++      sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
++      printf("  code_ISO639: 0x%02x\n", lang->code_ISO639);
++      printf("  encoding:    0x%02x\n", lang->encoding);
++      printf("  base_offset: 0x%02x\n", lang->base_offset);
++ }
++ 
++ static void print_access_protos(void *value, void *userData)
++ {
++      sdp_list_t *protDescSeq = (sdp_list_t *)value;
++      sdp_list_foreach(protDescSeq, print_service_desc, 0);
++ }
++ 
++ static void print_profile_desc(void *value, void *userData)
++ {
++      sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
++      char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
++ 
++      sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
++      sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
++ 
++      printf("  \"%s\" (0x%s)\n", str, UUID_str);
++      if (desc->version)
++              printf("    Version: 0x%04x\n", desc->version);
++ }
++ 
++ /*
++  * Parse a SDP record in user friendly form.
++  */
++ static void print_service_attr(sdp_record_t *rec)
++ {
++      sdp_list_t *list = 0, *proto = 0;
++ 
++      sdp_record_print(rec);
++ 
++      printf("Service RecHandle: 0x%x\n", rec->handle);
++ 
++      if (sdp_get_service_classes(rec, &list) == 0) {
++              printf("Service Class ID List:\n");
++              sdp_list_foreach(list, print_service_class, 0);
++              sdp_list_free(list, free);
++      }
++      if (sdp_get_access_protos(rec, &proto) == 0) {
++              printf("Protocol Descriptor List:\n");
++              sdp_list_foreach(proto, print_access_protos, 0);
++              sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
++              sdp_list_free(proto, 0);
++      }
++      if (sdp_get_lang_attr(rec, &list) == 0) {
++              printf("Language Base Attr List:\n");
++              sdp_list_foreach(list, print_lang_attr, 0);
++              sdp_list_free(list, free);
++      }
++      if (sdp_get_profile_descs(rec, &list) == 0) {
++              printf("Profile Descriptor List:\n");
++              sdp_list_foreach(list, print_profile_desc, 0);
++              sdp_list_free(list, free);
++      }
++ }
++ 
++ /*
++  * Support for Service (de)registration
++  */
++ typedef struct {
++      uint32_t handle;
++      char *name;
++      char *provider;
++      char *desc;
++      unsigned int class;
++      unsigned int profile;
++      uint16_t psm;
++      uint8_t channel;
++      uint8_t network;
++ } svc_info_t;
++ 
++ static void add_lang_attr(sdp_record_t *r)
++ {
++      sdp_lang_attr_t base_lang;
++      sdp_list_t *langs = 0;
++ 
++      /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
++      base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
++      base_lang.encoding = 106;
++      base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
++      langs = sdp_list_append(0, &base_lang);
++      sdp_set_lang_attr(r, langs);
++      sdp_list_free(langs, 0);
++ }
++ 
++ static int add_sp(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
++      uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
++      sdp_profile_desc_t profile;
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 1;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++      sdp_list_free(root, 0);
++ 
++      sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &sp_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++      sdp_list_free(svclass_id, 0);
++ 
++      sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
++      profile.version = 0x0100;
++      profiles = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, profiles);
++      sdp_list_free(profiles, 0);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      add_lang_attr(&record);
++ 
  -     profile.version = 0x0100;
+++     sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
+++
+++     sdp_set_url_attr(&record, "http://www.bluez.org/",
+++                     "http://www.bluez.org/", "http://www.bluez.org/");
+++
+++     sdp_set_service_id(&record, sp_uuid);
+++     sdp_set_service_ttl(&record, 0xffff);
+++     sdp_set_service_avail(&record, 0xff);
+++     sdp_set_record_state(&record, 0x00001234);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Serial Port service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_dun(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
++      uuid_t rootu, dun, gn, l2cap, rfcomm;
++      sdp_profile_desc_t profile;
++      sdp_list_t *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 2;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &rootu);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &dun);
++      sdp_uuid16_create(&gn,  GENERIC_NETWORKING_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &gn);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
++      profile.version = 0x0100;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Dial-Up Networking service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_fax(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel? si->channel : 3;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &fax_uuid);
++      sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &tel_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
++      profile.version = 0x0100;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq  = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Fax", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++      printf("Fax service registered\n");
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++      return ret;
++ }
++ 
++ static int add_lan(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 4;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
++      profile.version = 0x0100;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("LAN Access service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_headset(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 5;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &svclass_uuid);
++      sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
++      profile.version = 0x0100;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Headset", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Headset service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
+++static int add_headset_ag(sdp_session_t *session, svc_info_t *si)
+++{
+++     sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+++     uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+++     sdp_profile_desc_t profile;
+++     sdp_list_t *aproto, *proto[2];
+++     sdp_record_t record;
+++     uint8_t u8 = si->channel ? si->channel : 7;
+++     uint16_t u16 = 0x17;
+++     sdp_data_t *channel, *features;
+++     uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+++     sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+++     int ret = 0;
+++
+++     memset(&record, 0, sizeof(sdp_record_t));
+++     record.handle = si->handle;
+++
+++     sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+++     root = sdp_list_append(0, &root_uuid);
+++     sdp_set_browse_groups(&record, root);
+++
+++     sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+++     svclass_id = sdp_list_append(0, &svclass_uuid);
+++     sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+++     svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+++     sdp_set_service_classes(&record, svclass_id);
+++
+++     sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+++     profile.version = 0x0100;
+++     pfseq = sdp_list_append(0, &profile);
+++     sdp_set_profile_descs(&record, pfseq);
+++
+++     sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+++     proto[0] = sdp_list_append(0, &l2cap_uuid);
+++     apseq = sdp_list_append(0, proto[0]);
+++
+++     sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+++     proto[1] = sdp_list_append(0, &rfcomm_uuid);
+++     channel = sdp_data_alloc(SDP_UINT8, &u8);
+++     proto[1] = sdp_list_append(proto[1], channel);
+++     apseq = sdp_list_append(apseq, proto[1]);
+++
+++     features = sdp_data_alloc(SDP_UINT16, &u16);
+++     sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+++
+++     aproto = sdp_list_append(0, apseq);
+++     sdp_set_access_protos(&record, aproto);
+++
+++     sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+++
+++     sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+++
+++     if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+++             printf("Service Record registration failed\n");
+++             ret = -1;
+++             goto end;
+++     }
+++
+++     printf("Headset AG service registered\n");
+++
+++end:
+++     sdp_data_free(channel);
+++     sdp_list_free(proto[0], 0);
+++     sdp_list_free(proto[1], 0);
+++     sdp_list_free(apseq, 0);
+++     sdp_list_free(aproto, 0);
+++
+++     return ret;
+++}
+++
++ static int add_handsfree(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 6;
++      uint16_t u16 = 0x31;
++      sdp_data_t *channel, *features;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &svclass_uuid);
++      sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
++      profile.version = 0x0101;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      features = sdp_data_alloc(SDP_UINT16, &u16);
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Handsfree", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Handsfree service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel : 7;
++      uint16_t u16 = 0x17;
++      sdp_data_t *channel, *features;
++      uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
++      sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &svclass_uuid);
++      sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+++     profile.version = 0x0105;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      features = sdp_data_alloc(SDP_UINT16, &u16);
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
++ 
++      sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
++ 
++      if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Handsfree AG service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_simaccess(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      sdp_profile_desc_t profile;
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t u8 = si->channel? si->channel : 8;
++      uint16_t u16 = 0x31;
++      sdp_data_t *channel, *features; 
++      int ret = 0;
++ 
++      memset((void *)&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &svclass_uuid);
++      sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
++      svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
++      profile.version = 0x0101;
++      pfseq = sdp_list_append(0, &profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      features = sdp_data_alloc(SDP_UINT16, &u16);
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "SIM Access", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("SIM Access service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_opush(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[3];
++      sdp_record_t record;
++      uint8_t chan = si->channel ? si->channel : 9;
++      sdp_data_t *channel;
++      uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
++      //uint8_t formats[] = { 0xff };
++      void *dtds[sizeof(formats)], *values[sizeof(formats)];
++      int i;
++      uint8_t dtd = SDP_UINT8;
++      sdp_data_t *sflist;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &opush_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &chan);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      sdp_uuid16_create(&obex_uuid, OBEX_UUID);
++      proto[2] = sdp_list_append(0, &obex_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      for (i = 0; i < sizeof(formats); i++) {
++              dtds[i] = &dtd;
++              values[i] = &formats[i];
++      }
++      sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
++ 
++      sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("OBEX Object Push service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(proto[2], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_ftp(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[3];
++      sdp_record_t record;
++      uint8_t u8 = si->channel ? si->channel: 10;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &ftrn_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &u8);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      sdp_uuid16_create(&obex_uuid, OBEX_UUID);
++      proto[2] = sdp_list_append(0, &obex_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("OBEX File Transfer service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(proto[2], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_directprint(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[3];
++      sdp_record_t record;
++      uint8_t chan = si->channel ? si->channel : 12;
++      sdp_data_t *channel;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &opush_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto[1] = sdp_list_append(0, &rfcomm_uuid);
++      channel = sdp_data_alloc(SDP_UINT8, &chan);
++      proto[1] = sdp_list_append(proto[1], channel);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      sdp_uuid16_create(&obex_uuid, OBEX_UUID);
++      proto[2] = sdp_list_append(0, &obex_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Direct Printing", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("Direct Printing service registered\n");
++ 
++ end:
++      sdp_data_free(channel);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(proto[2], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_nap(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint16_t lp = 0x000f, ver = 0x0100;
++      sdp_data_t *psm, *version;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &ftrn_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
++      proto[1] = sdp_list_append(0, &bnep_uuid);
++      version  = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++ 
++      {
++              uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
++              sdp_data_t *head, *pseq;
++              int p;
++ 
++              for (p = 0, head = NULL; p < 4; p++) {
++                      sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
++                      head = sdp_seq_append(head, data);
++              }
++              pseq = sdp_data_alloc(SDP_SEQ16, head);
++              proto[1] = sdp_list_append(proto[1], pseq);
++      }
++ 
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("NAP service registered\n");
++ 
++ end:
++      sdp_data_free(version);
++      sdp_data_free(psm);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_gn(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint16_t lp = 0x000f, ver = 0x0100;
++      sdp_data_t *psm, *version;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &ftrn_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
++      proto[1] = sdp_list_append(0, &bnep_uuid);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Group Network Service", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("GN service registered\n");
++ 
++ end:
++      sdp_data_free(version);
++      sdp_data_free(psm);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_panu(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint16_t lp = 0x000f, ver = 0x0100;
++      sdp_data_t *psm, *version;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++      sdp_list_free(root, NULL);
++ 
++      sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
++      svclass_id = sdp_list_append(NULL, &ftrn_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++      sdp_list_free(svclass_id, NULL);
++ 
++      sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(NULL, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++      sdp_list_free(pfseq, NULL);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[0] = sdp_list_append(NULL, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(NULL, proto[0]);
++ 
++      sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
++      proto[1] = sdp_list_append(NULL, &bnep_uuid);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(NULL, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "PAN User", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("PANU service registered\n");
++ 
++ end:
++      sdp_data_free(version);
++      sdp_data_free(psm);
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[3];
++      sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
++      int i;
++      uint8_t dtd = SDP_UINT16;
++      uint8_t dtd2 = SDP_UINT8;
++      uint8_t dtd_data = SDP_TEXT_STR8;
++      void *dtds[2];
++      void *values[2];
++      void *dtds2[2];
++      void *values2[2];
++      int leng[2];
++      uint8_t hid_spec_type = 0x22;
++      uint16_t hid_attr_lang[] = { 0x409, 0x100 };
++      static const uint16_t ctrl = 0x11;
++      static const uint16_t intr = 0x13;
++      static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
++      static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
++      const uint8_t hid_spec[] = { 
++              0x05, 0x01, // usage page
++              0x09, 0x06, // keyboard
++              0xa1, 0x01, // key codes
++              0x85, 0x01, // minimum
++              0x05, 0x07, // max
++              0x19, 0xe0, // logical min
++              0x29, 0xe7, // logical max
++              0x15, 0x00, // report size
++              0x25, 0x01, // report count
++              0x75, 0x01, // input data variable absolute
++              0x95, 0x08, // report count
++              0x81, 0x02, // report size
++              0x75, 0x08, 
++              0x95, 0x01, 
++              0x81, 0x01, 
++              0x75, 0x01, 
++              0x95, 0x05,
++              0x05, 0x08,
++              0x19, 0x01,
++              0x29, 0x05, 
++              0x91, 0x02,
++              0x75, 0x03,
++              0x95, 0x01,
++              0x91, 0x01,
++              0x75, 0x08,
++              0x95, 0x06,
++              0x15, 0x00,
++              0x26, 0xff,
++              0x00, 0x05,
++              0x07, 0x19,
++              0x00, 0x2a,
++              0xff, 0x00,
++              0x81, 0x00,
++              0x75, 0x01,
++              0x95, 0x01,
++              0x15, 0x00,
++              0x25, 0x01,
++              0x05, 0x0c,
++              0x09, 0xb8,
++              0x81, 0x06,
++              0x09, 0xe2,
++              0x81, 0x06,
++              0x09, 0xe9,
++              0x81, 0x02,
++              0x09, 0xea,
++              0x81, 0x02,
++              0x75, 0x01,
++              0x95, 0x04,
++              0x81, 0x01,
++              0xc0         // end tag
++      };
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      add_lang_attr(&record);
++ 
++      sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &hidkb_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      /* protocols */
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[1] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &ctrl);
++      proto[1] = sdp_list_append(proto[1], psm);
++      apseq = sdp_list_append(0, proto[1]);
++ 
++      sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
++      proto[2] = sdp_list_append(0, &hidp_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      /* additional protocols */
++      proto[1] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &intr);
++      proto[1] = sdp_list_append(proto[1], psm);
++      apseq = sdp_list_append(0, proto[1]);
++ 
++      sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
++      proto[2] = sdp_list_append(0, &hidp_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_add_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
++ 
++      for (i = 0; i < sizeof(hid_attr) / 2; i++)
++              sdp_attr_add_new(&record,
++                                      SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
++                                      SDP_UINT16, &hid_attr[i]);
++ 
++      dtds[0] = &dtd2;
++      values[0] = &hid_spec_type;
++      dtds[1] = &dtd_data;
++      values[1] = (uint8_t *) hid_spec;
++      leng[0] = 0;
++      leng[1] = sizeof(hid_spec);
++      hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
++      hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
++      sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
++ 
++      for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
++              dtds2[i] = &dtd;
++              values2[i] = &hid_attr_lang[i];
++      }
++ 
++      lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
++      lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
++      sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
++ 
++      for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
++              sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
++                                              SDP_UINT16, &hid_attr2[i + 1]);
++ 
++      if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("HID keyboard service registered\n");
++ 
++      return 0;
++ }
++ 
++ static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[3];
++      sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
++      int i;
++      uint8_t dtd = SDP_UINT16;
++      uint8_t dtd2 = SDP_UINT8;
++      uint8_t dtd_data = SDP_TEXT_STR8;
++      void *dtds[2];
++      void *values[2];
++      void *dtds2[2];
++      void *values2[2];
++      int leng[2];
++      uint8_t hid_spec_type = 0x22;
++      uint16_t hid_attr_lang[] = { 0x409, 0x100 };
++      uint16_t ctrl = 0x11, intr = 0x13;
++      uint16_t hid_release = 0x0100, parser_version = 0x0111;
++      uint8_t subclass = 0x04, country = 0x33;
++      uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
++      uint8_t battery = 1, remote_wakeup = 1;
++      uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
++      uint8_t norm_connect = 0, boot_device = 0;
++      const uint8_t hid_spec[] = {
++              0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
++              0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
++              0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
++              0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
++              0xc0, 0x00
++      };
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
++      svclass_id = sdp_list_append(NULL, &hid_uuid);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(NULL, profile);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto[1] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &ctrl);
++      proto[1] = sdp_list_append(proto[1], psm);
++      apseq = sdp_list_append(0, proto[1]);
++ 
++      sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
++      proto[2] = sdp_list_append(0, &hidp_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      proto[1] = sdp_list_append(0, &l2cap_uuid);
++      psm = sdp_data_alloc(SDP_UINT16, &intr);
++      proto[1] = sdp_list_append(proto[1], psm);
++      apseq = sdp_list_append(0, proto[1]);
++ 
++      sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
++      proto[2] = sdp_list_append(0, &hidp_uuid);
++      apseq = sdp_list_append(apseq, proto[2]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_add_access_protos(&record, aproto);
++ 
++      add_lang_attr(&record);
++ 
++      sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
++                                      "Nintendo", "Nintendo RVL-CNT-01");
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
++                                              SDP_UINT16, &hid_release);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
++                                              SDP_UINT16, &parser_version);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
++                                              SDP_UINT8, &subclass);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
++                                              SDP_UINT8, &country);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
++                                              SDP_BOOL, &virtual_cable);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
++                                              SDP_BOOL, &reconnect);
++ 
++      dtds[0] = &dtd2;
++      values[0] = &hid_spec_type;
++      dtds[1] = &dtd_data;
++      values[1] = (uint8_t *) hid_spec;
++      leng[0] = 0;
++      leng[1] = sizeof(hid_spec);
++      hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
++      hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
++      sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
++ 
++      for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
++              dtds2[i] = &dtd;
++              values2[i] = &hid_attr_lang[i];
++      }
++ 
++      lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
++      lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
++      sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
++                                              SDP_BOOL, &sdp_disable);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
++                                              SDP_BOOL, &battery);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
++                                              SDP_BOOL, &remote_wakeup);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
++                                              SDP_UINT16, &profile_version);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
++                                              SDP_UINT16, &superv_timeout);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
++                                              SDP_BOOL, &norm_connect);
++ 
++      sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
++                                              SDP_BOOL, &boot_device);
++ 
++      if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("Wii-Mote service registered\n");
++ 
++      return 0;
++ }
++ 
++ static int add_cip(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, cmtp, cip;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint16_t psm = si->psm ? si->psm : 0x1001;
++      uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
++      sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &cip);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      apseq = sdp_list_append(0, proto[0]);
++      proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&cmtp, CMTP_UUID);
++      proto[1] = sdp_list_append(0, &cmtp);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
++ 
++      sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("CIP service registered\n");
++ 
++ end:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++      sdp_data_free(network);
++ 
++      return ret;
++ }
++ 
++ static int add_ctp(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, tcsbin, ctp;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
++      sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &ctp);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
++      proto[1] = sdp_list_append(0, &tcsbin);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
++ 
++      sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto end;
++      }
++ 
++      printf("CTP service registered\n");
++ 
++ end:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++      sdp_data_free(network);
++ 
++      return ret;
++ }
++ 
++ static int add_a2source(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, avdtp, a2src;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      sdp_data_t *psm, *version;
++      uint16_t lp = 0x0019, ver = 0x0100;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &a2src);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&avdtp, AVDTP_UUID);
++      proto[1] = sdp_list_append(0, &avdtp);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Audio Source", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto done;
++      }
++ 
++      printf("Audio source service registered\n");
++ 
++ done:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_a2sink(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, avdtp, a2snk;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      sdp_data_t *psm, *version;
++      uint16_t lp = 0x0019, ver = 0x0100;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &a2snk);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&avdtp, AVDTP_UUID);
++      proto[1] = sdp_list_append(0, &avdtp);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      sdp_set_info_attr(&record, "Audio Sink", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto done;
++      }
++ 
++      printf("Audio sink service registered\n");
++ 
++ done:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_avrct(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, avctp, avrct;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      sdp_data_t *psm, *version, *features;
++      uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &avrct);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&avctp, AVCTP_UUID);
++      proto[1] = sdp_list_append(0, &avctp);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      features = sdp_data_alloc(SDP_UINT16, &feat);
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
++ 
++      sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto done;
++      }
++ 
++      printf("Remote control service registered\n");
++ 
++ done:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_avrtg(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++      uuid_t root_uuid, l2cap, avctp, avrtg;
++      sdp_profile_desc_t profile[1];
++      sdp_list_t *aproto, *proto[2];
++      sdp_record_t record;
++      sdp_data_t *psm, *version, *features;
++      uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
++      int ret = 0;
++ 
++      memset(&record, 0, sizeof(sdp_record_t));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(0, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
++      svclass_id = sdp_list_append(0, &avrtg);
++      sdp_set_service_classes(&record, svclass_id);
++ 
++      sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
++      profile[0].version = 0x0100;
++      pfseq = sdp_list_append(0, &profile[0]);
++      sdp_set_profile_descs(&record, pfseq);
++ 
++      sdp_uuid16_create(&l2cap, L2CAP_UUID);
++      proto[0] = sdp_list_append(0, &l2cap);
++      psm = sdp_data_alloc(SDP_UINT16, &lp);
++      proto[0] = sdp_list_append(proto[0], psm);
++      apseq = sdp_list_append(0, proto[0]);
++ 
++      sdp_uuid16_create(&avctp, AVCTP_UUID);
++      proto[1] = sdp_list_append(0, &avctp);
++      version = sdp_data_alloc(SDP_UINT16, &ver);
++      proto[1] = sdp_list_append(proto[1], version);
++      apseq = sdp_list_append(apseq, proto[1]);
++ 
++      aproto = sdp_list_append(0, apseq);
++      sdp_set_access_protos(&record, aproto);
++ 
++      features = sdp_data_alloc(SDP_UINT16, &feat);
++      sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
++ 
++      sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              ret = -1;
++              goto done;
++      }
++ 
++      printf("Remote target service registered\n");
++ 
++ done:
++      sdp_list_free(proto[0], 0);
++      sdp_list_free(proto[1], 0);
++      sdp_list_free(apseq, 0);
++      sdp_list_free(aproto, 0);
++ 
++      return ret;
++ }
++ 
++ static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel: 18;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++      sdp_list_free(root, NULL);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++              sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++      sdp_list_free(svclass, NULL);
++ 
++      sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("UDI UE service registered\n");
++ 
++      return 0;
++ }
++ 
++ static int add_udi_te(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel: 19;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++      sdp_list_free(root, NULL);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++              sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++      sdp_list_free(svclass, NULL);
++ 
++      sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("UDI TE service registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char sr1_uuid[] = {  0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
++                                      0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
++ 
++ static int add_sr1(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass;
++      uuid_t root_uuid, svclass_uuid;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("Toshiba Speech Recognition SR-1 service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char syncmls_uuid[] = {      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
++ 
++ static unsigned char syncmlc_uuid[] = {      0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
++ 
++ static int add_syncml(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
++      uint8_t channel = si->channel ? si->channel: 15;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++              sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_uuid16_create(&obex_uuid, OBEX_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("SyncML Client service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char async_uuid[] = {        0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
++                                      0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
++ 
++ static int add_activesync(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel: 21;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++      sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("ActiveSync service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char hotsync_uuid[] = {      0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
++                                      0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
++ 
++ static int add_hotsync(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel: 22;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++      sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("HotSync service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char palmos_uuid[] = {       0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
++                                      0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
++ 
++ static int add_palmos(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass;
++      uuid_t root_uuid, svclass_uuid;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("PalmOS service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char nokid_uuid[] = {        0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
++ 
++ static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass;
++      uuid_t root_uuid, svclass_uuid;
++      uint16_t verid = 0x005f;
++      sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              sdp_data_free(version);
++              return -1;
++      }
++ 
++      printf("Nokia ID service record registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char pcsuite_uuid[] = {      0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
++ 
++ static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel: 14;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++              sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
++      svclass = sdp_list_append(NULL, &svclass_uuid);
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("Nokia PC Suite service registered\n");
++ 
++      return 0;
++ }
++ 
++ static unsigned char nftp_uuid[] = { 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
++ 
++ static unsigned char nsyncml_uuid[] = {      0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
++ 
++ static unsigned char ngage_uuid[] = {        0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
++                                      0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
++ 
++ static unsigned char apple_uuid[] = {        0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
++                                      0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
++ 
++ static int add_apple(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root;
++      uuid_t root_uuid;
++      uint32_t attr783 = 0x00000000;
++      uint32_t attr785 = 0x00000002;
++      uint16_t attr786 = 0x1234;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
++      sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
++      sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
++      sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
++      sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
++      sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
++      sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
++ 
++      sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("Apple attribute service registered\n");
++ 
++      return 0;
++ }
++ 
++ static int add_isync(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_list_t *root, *svclass, *proto;
++      uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
++      uint8_t channel = si->channel ? si->channel : 16;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
++ 
++      sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++      proto = sdp_list_append(proto, sdp_list_append(
++              sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
++      svclass = sdp_list_append(NULL, &serial_uuid);
++ 
++      sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
++      svclass = sdp_list_append(svclass, &svclass_uuid);
++ 
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      printf("Apple iSync service registered\n");
++ 
++      return 0;
++ }
++ 
++ static int add_semchla(sdp_session_t *session, svc_info_t *si)
++ {
++      sdp_record_t record;
++      sdp_profile_desc_t profile;
++      sdp_list_t *root, *svclass, *proto, *profiles;
++      uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
++      uint16_t psm = 0xf0f9;
++ 
++      memset(&record, 0, sizeof(record));
++      record.handle = si->handle;
++ 
++      sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++      root = sdp_list_append(NULL, &root_uuid);
++      sdp_set_browse_groups(&record, root);
++ 
++      sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++      proto = sdp_list_append(NULL, sdp_list_append(
++              sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
++ 
++      sdp_uuid32_create(&semchla_uuid, 0x8e770300);
++      proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
++ 
++      sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
++ 
++      sdp_uuid32_create(&service_uuid, 0x8e771301);
++      svclass = sdp_list_append(NULL, &service_uuid);
++ 
++      sdp_set_service_classes(&record, svclass);
++ 
++      sdp_uuid32_create(&profile.uuid, 0x8e771302);   // Headset
++      //sdp_uuid32_create(&profile.uuid, 0x8e771303); // Phone
++      profile.version = 0x0100;
++      profiles = sdp_list_append(NULL, &profile);
++      sdp_set_profile_descs(&record, profiles);
++ 
++      sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
++ 
++      if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
++              printf("Service Record registration failed\n");
++              return -1;
++      }
++ 
++      /* SEMC High Level Authentication */
++      printf("SEMC HLA service registered\n");
++ 
++      return 0;
++ }
++ 
++ struct {
++      char            *name;
++      uint32_t        class;
++      int             (*add)(sdp_session_t *sess, svc_info_t *si);
++      unsigned char *uuid;
++ } service[] = {
++      { "DID",        PNP_INFO_SVCLASS_ID,            NULL,           },
++ 
++      { "SP",         SERIAL_PORT_SVCLASS_ID,         add_sp          },
++      { "DUN",        DIALUP_NET_SVCLASS_ID,          add_dun         },
++      { "LAN",        LAN_ACCESS_SVCLASS_ID,          add_lan         },
++      { "FAX",        FAX_SVCLASS_ID,                 add_fax         },
++      { "OPUSH",      OBEX_OBJPUSH_SVCLASS_ID,        add_opush       },
++      { "FTP",        OBEX_FILETRANS_SVCLASS_ID,      add_ftp         },
++      { "PRINT",      DIRECT_PRINTING_SVCLASS_ID,     add_directprint },
++ 
++      { "HS",         HEADSET_SVCLASS_ID,             add_headset     },
+++     { "HSAG",       HEADSET_AGW_SVCLASS_ID, add_headset_ag},
++      { "HF",         HANDSFREE_SVCLASS_ID,           add_handsfree   },
++      { "HFAG",       HANDSFREE_AGW_SVCLASS_ID,       add_handsfree_ag},
++      { "SAP",        SAP_SVCLASS_ID,                 add_simaccess   },
++ 
++      { "NAP",        NAP_SVCLASS_ID,                 add_nap         },
++      { "GN",         GN_SVCLASS_ID,                  add_gn          },
++      { "PANU",       PANU_SVCLASS_ID,                add_panu        },
++ 
++      { "HCRP",       HCR_SVCLASS_ID,                 NULL            },
++      { "HID",        HID_SVCLASS_ID,                 NULL            },
++      { "KEYB",       HID_SVCLASS_ID,                 add_hid_keyb    },
++      { "WIIMOTE",    HID_SVCLASS_ID,                 add_hid_wiimote },
++      { "CIP",        CIP_SVCLASS_ID,                 add_cip         },
++      { "CTP",        CORDLESS_TELEPHONY_SVCLASS_ID,  add_ctp         },
++ 
++      { "A2SRC",      AUDIO_SOURCE_SVCLASS_ID,        add_a2source    },
++      { "A2SNK",      AUDIO_SINK_SVCLASS_ID,          add_a2sink      },
++      { "AVRCT",      AV_REMOTE_SVCLASS_ID,           add_avrct       },
++      { "AVRTG",      AV_REMOTE_TARGET_SVCLASS_ID,    add_avrtg       },
++ 
++      { "UDIUE",      UDI_MT_SVCLASS_ID,              add_udi_ue      },
++      { "UDITE",      UDI_TA_SVCLASS_ID,              add_udi_te      },
++ 
++      { "SEMCHLA",    0x8e771301,                     add_semchla     },
++ 
++      { "SR1",        0,                              add_sr1,        sr1_uuid        },
++      { "SYNCML",     0,                              add_syncml,     syncmlc_uuid    },
++      { "SYNCMLSERV", 0,                              NULL,           syncmls_uuid    },
++      { "ACTIVESYNC", 0,                              add_activesync, async_uuid      },
++      { "HOTSYNC",    0,                              add_hotsync,    hotsync_uuid    },
++      { "PALMOS",     0,                              add_palmos,     palmos_uuid     },
++      { "NOKID",      0,                              add_nokiaid,    nokid_uuid      },
++      { "PCSUITE",    0,                              add_pcsuite,    pcsuite_uuid    },
++      { "NFTP",       0,                              NULL,           nftp_uuid       },
++      { "NSYNCML",    0,                              NULL,           nsyncml_uuid    },
++      { "NGAGE",      0,                              NULL,           ngage_uuid      },
++      { "APPLE",      0,                              add_apple,      apple_uuid      },
++ 
++      { "ISYNC",      APPLE_AGENT_SVCLASS_ID,         add_isync,      },
++ 
++      { 0 }
++ };
++ 
++ /* Add local service */
++ static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
++ {
++      sdp_session_t *sess;
++      int i, ret = -1;
++ 
++      if (!si->name)
++              return -1;
++ 
++      sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++      if (!sess)
++              return -1;
++ 
++      for (i = 0; service[i].name; i++)
++              if (!strcasecmp(service[i].name, si->name)) {
++                      if (service[i].add)
++                              ret = service[i].add(sess, si);
++                      goto done;
++              }
++ 
++      printf("Unknown service name: %s\n", si->name);
++ 
++ done:
++      free(si->name);
++      sdp_close(sess);
++ 
++      return ret;
++ }
++ 
++ static struct option add_options[] = {
++      { "help",       0, 0, 'h' },
++      { "handle",     1, 0, 'r' },
++      { "psm",        1, 0, 'p' },
++      { "channel",    1, 0, 'c' },
++      { "network",    1, 0, 'n' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *add_help = 
++      "Usage:\n"
++      "\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
++ 
++ static int cmd_add(int argc, char **argv)
++ {
++      svc_info_t si;
++      int opt;
++ 
++      memset(&si, 0, sizeof(si));
++      si.handle = 0xffffffff;
++ 
++      for_each_opt(opt, add_options, 0) {
++              switch (opt) {
++              case 'r':
++                      if (strncasecmp(optarg, "0x", 2))
++                              si.handle = atoi(optarg);
++                      else
++                              si.handle = strtol(optarg + 2, NULL, 16);
++                      break;
++              case 'p':
++                      if (strncasecmp(optarg, "0x", 2))
++                              si.psm = atoi(optarg);
++                      else
++                              si.psm = strtol(optarg + 2, NULL, 16);
++                      break;
++              case 'c':
++                      if (strncasecmp(optarg, "0x", 2))
++                              si.channel = atoi(optarg);
++                      else
++                              si.channel = strtol(optarg + 2, NULL, 16);
++                      break;
++              case 'n':
++                      if (strncasecmp(optarg, "0x", 2))
++                              si.network = atoi(optarg);
++                      else
++                              si.network = strtol(optarg + 2, NULL, 16);
++                      break;
++              default:
++                      printf(add_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 1) {
++              printf(add_help);
++              return -1;
++      }
++ 
++      si.name = strdup(argv[0]);
++ 
++      return add_service(0, &si);
++ }
++ 
++ /* Delete local service */
++ static int del_service(bdaddr_t *bdaddr, void *arg)
++ {
++      uint32_t handle, range = 0x0000ffff;
++      sdp_list_t *attr;
++      sdp_session_t *sess;
++      sdp_record_t *rec;
++ 
++      if (!arg) { 
++              printf("Record handle was not specified.\n");
++              return -1;
++      }
++ 
++      sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++      if (!sess) {
++              printf("No local SDP server!\n");
++              return -1;
++      }
++ 
++      handle = strtoul((char *)arg, 0, 16);
++      attr = sdp_list_append(0, &range);
++      rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
++      sdp_list_free(attr, 0);
++ 
++      if (!rec) {
++              printf("Service Record not found.\n");
++              sdp_close(sess);
++              return -1;
++      }
++ 
++      if (sdp_device_record_unregister(sess, &interface, rec)) {
++              printf("Failed to unregister service record: %s\n", strerror(errno));
++              sdp_close(sess);
++              return -1;
++      }
++ 
++      printf("Service Record deleted.\n");
++      sdp_close(sess);
++ 
++      return 0;
++ }
++ 
++ static struct option del_options[] = {
++      { "help",       0, 0, 'h' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *del_help = 
++      "Usage:\n"
++      "\tdel record_handle\n";
++ 
++ static int cmd_del(int argc, char **argv)
++ {
++      int opt;
++ 
++      for_each_opt(opt, del_options, 0) {
++              switch (opt) {
++              default:
++                      printf(del_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 1) {
++              printf(del_help);
++              return -1;
++      }
++ 
++      return del_service(NULL, argv[0]);
++ }
++ 
++ /*
++  * Perform an inquiry and search/browse all peer found.
++  */
++ static void inquiry(handler_t handler, void *arg)
++ {
++      inquiry_info ii[20];
++      uint8_t count = 0;
++      int i;
++ 
++      printf("Inquiring ...\n");
++      if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
++              printf("Inquiry failed\n");
++              return;
++      }
++ 
++      for (i = 0; i < count; i++)
++              handler(&ii[i].bdaddr, arg);
++ }
++ 
++ static void doprintf(void *data, const char *str)
++ {
++      printf(str);
++ }
++ 
++ /*
++  * Search for a specific SDP service
++  */
++ static int do_search(bdaddr_t *bdaddr, struct search_context *context)
++ {
++      sdp_list_t *attrid, *search, *seq, *next;
++      uint32_t range = 0x0000ffff;
++      char str[20];
++      sdp_session_t *sess;
++ 
++      if (!bdaddr) {
++              inquiry(do_search, context);
++              return 0;
++      }
++ 
++      sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
++      ba2str(bdaddr, str);
++      if (!sess) {
++              printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
++              return -1;
++      }
++ 
++      if (context->view != RAW_VIEW) {
++              if (context->svc)
++                      printf("Searching for %s on %s ...\n", context->svc, str);
++              else
++                      printf("Browsing %s ...\n", str);
++      }
++ 
++      attrid = sdp_list_append(0, &range);
++      search = sdp_list_append(0, &context->group);
++      if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
++              printf("Service Search failed: %s\n", strerror(errno));
++              sdp_close(sess);
++              return -1;
++      }
++      sdp_list_free(attrid, 0);
++      sdp_list_free(search, 0);
++ 
++      for (; seq; seq = next) {
++              sdp_record_t *rec = (sdp_record_t *) seq->data;
++              struct search_context sub_context;
++ 
++              switch (context->view) {
++              case DEFAULT_VIEW:
++                      /* Display user friendly form */
++                      print_service_attr(rec);
++                      printf("\n");
++                      break;
++              case TREE_VIEW:
++                      /* Display full tree */
++                      print_tree_attr(rec);
++                      printf("\n");
++                      break;
++              case XML_VIEW:
++                      /* Display raw XML tree */
++                      convert_sdp_record_to_xml(rec, 0, doprintf);
++                      break;
++              default:
++                      /* Display raw tree */
++                      print_raw_attr(rec);
++                      break;
++              }
++ 
++              if (sdp_get_group_id(rec, &sub_context.group) != -1) {
++                      /* Set the subcontext for browsing the sub tree */
++                      memcpy(&sub_context, context, sizeof(struct search_context));
++                      /* Browse the next level down if not done */
++                      if (sub_context.group.value.uuid16 != context->group.value.uuid16)
++                              do_search(bdaddr, &sub_context);
++              }
++              next = seq->next;
++              free(seq);
++              sdp_record_free(rec);
++      }
++ 
++      sdp_close(sess);
++      return 0;
++ }
++ 
++ static struct option browse_options[] = {
++      { "help",       0, 0, 'h' },
++      { "tree",       0, 0, 't' },
++      { "raw",        0, 0, 'r' },
++      { "xml",        0, 0, 'x' },
++      { "uuid",       1, 0, 'u' },
++      { "l2cap",      0, 0, 'l' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *browse_help = 
++      "Usage:\n"
++      "\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
++ 
++ /*
++  * Browse the full SDP database (i.e. list all services starting from the
++  * root/top-level).
++  */
++ static int cmd_browse(int argc, char **argv)
++ {
++      struct search_context context;
++      int opt, num;
++ 
++      /* Initialise context */
++      memset(&context, '\0', sizeof(struct search_context));
++      /* We want to browse the top-level/root */
++      sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
++ 
++      for_each_opt(opt, browse_options, 0) {
++              switch (opt) {
++              case 't':
++                      context.view = TREE_VIEW;
++                      break;
++              case 'r':
++                      context.view = RAW_VIEW;
++                      break;
++              case 'x':
++                      context.view = XML_VIEW;
++                      break;
++              case 'u':
++                      if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
++                              printf("Invalid uuid %s\n", optarg);
++                              return -1;
++                      }
++                      sdp_uuid16_create(&context.group, num);
++                      break;
++              case 'l':
++                      sdp_uuid16_create(&context.group, L2CAP_UUID);
++                      break;
++              default:
++                      printf(browse_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc >= 1) {
++              bdaddr_t bdaddr;
++              estr2ba(argv[0], &bdaddr);
++              return do_search(&bdaddr, &context);
++      }
++ 
++      return do_search(NULL, &context);
++ }
++ 
++ static struct option search_options[] = {
++      { "help",       0, 0, 'h' },
++      { "bdaddr",     1, 0, 'b' },
++      { "tree",       0, 0, 't' },
++      { "raw",        0, 0, 'r' },
++      { "xml",        0, 0, 'x' },
++      { 0, 0, 0, 0}
++ };
++ 
++ static char *search_help = 
++      "Usage:\n"
++      "\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
++      "SERVICE is a name (string) or UUID (0x1002)\n";
++ 
++ /*
++  * Search for a specific SDP service
++  *
++  * Note : we should support multiple services on the command line :
++  *          sdptool search 0x0100 0x000f 0x1002
++  * (this would search a service supporting both L2CAP and BNEP directly in
++  * the top level browse group)
++  */
++ static int cmd_search(int argc, char **argv)
++ {
++      struct search_context context;
++      unsigned char *uuid = NULL;
++      uint32_t class = 0;
++      bdaddr_t bdaddr;
++      int has_addr = 0;
++      int i;
++      int opt;
++ 
++      /* Initialise context */
++      memset(&context, '\0', sizeof(struct search_context));
++ 
++      for_each_opt(opt, search_options, 0) {
++              switch (opt) {
++              case 'b':
++                      estr2ba(optarg, &bdaddr);
++                      has_addr = 1;
++                      break;
++              case 't':
++                      context.view = TREE_VIEW;
++                      break;
++              case 'r':
++                      context.view = RAW_VIEW;
++                      break;
++              case 'x':
++                      context.view = XML_VIEW;
++                      break;
++              default:
++                      printf(search_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 1) {
++              printf(search_help);
++              return -1;
++      }
++ 
++      /* Note : we need to find a way to support search combining
++       * multiple services */
++      context.svc = strdup(argv[0]);
++      if (!strncasecmp(context.svc, "0x", 2)) {
++              int num;
++              /* This is a UUID16, just convert to int */
++              sscanf(context.svc + 2, "%X", &num);
++              class = num;
++              printf("Class 0x%X\n", class);
++      } else {
++              /* Convert class name to an UUID */
++ 
++              for (i = 0; service[i].name; i++)
++                      if (strcasecmp(context.svc, service[i].name) == 0) {
++                              class = service[i].class;
++                              uuid = service[i].uuid;
++                              break;
++                      }
++              if (!class && !uuid) {
++                      printf("Unknown service %s\n", context.svc);
++                      return -1;
++              }
++      }
++ 
++      if (class) {
++              if (class & 0xffff0000)
++                      sdp_uuid32_create(&context.group, class);
++              else {
++                      uint16_t class16 = class & 0xffff;
++                      sdp_uuid16_create(&context.group, class16);
++              }
++      } else
++              sdp_uuid128_create(&context.group, uuid);
++ 
++      if (has_addr)
++              return do_search(&bdaddr, &context);
++ 
++      return do_search(NULL, &context);
++ }
++ 
++ /*
++  * Show how to get a specific SDP record by its handle.
++  * Not really useful to the user, just show how it can be done...
++  */
++ static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
++ {
++      sdp_list_t *attrid;
++      uint32_t range = 0x0000ffff;
++      sdp_record_t *rec;
++      sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
++ 
++      if (!session) {
++              char str[20];
++              ba2str(bdaddr, str);
++              printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
++              return -1;
++      }
++ 
++      attrid = sdp_list_append(0, &range);
++      rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
++      sdp_list_free(attrid, 0);
++      sdp_close(session);
++ 
++      if (!rec) {
++              if (!quite) {
++                      printf("Service get request failed.\n");
++                      return -1;
++              } else
++                      return 0;
++      }
++ 
++      switch (context->view) {
++      case DEFAULT_VIEW:
++              /* Display user friendly form */
++              print_service_attr(rec);
++              printf("\n");
++              break;
++      case TREE_VIEW:
++              /* Display full tree */
++              print_tree_attr(rec);
++              printf("\n");
++              break;
++      case XML_VIEW:
++              /* Display raw XML tree */
++              convert_sdp_record_to_xml(rec, 0, doprintf);
++              break;
++      default:
++              /* Display raw tree */
++              print_raw_attr(rec);
++              break;
++      }
++ 
++      sdp_record_free(rec);
++      return 0;
++ }
++ 
++ static struct option records_options[] = {
++      { "help",       0, 0, 'h' },
++      { "tree",       0, 0, 't' },
++      { "raw",        0, 0, 'r' },
++      { "xml",        0, 0, 'x' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *records_help = 
++      "Usage:\n"
++      "\trecords [--tree] [--raw] [--xml] bdaddr\n";
++ 
++ /*
++  * Request possible SDP service records
++  */
++ static int cmd_records(int argc, char **argv)
++ {
++      struct search_context context;
++      uint32_t base[] = { 0x10000, 0x10300, 0x10500,
++                      0x1002e, 0x110b, 0x90000, 0x2008000,
++                      0x4000000, 0x100000, 0x1000000, 0x4f491100 };
++      bdaddr_t bdaddr;
++      int i, n, opt, err = 0, num = 32;
++ 
++      /* Initialise context */
++      memset(&context, '\0', sizeof(struct search_context));
++ 
++      for_each_opt(opt, records_options, 0) {
++              switch (opt) {
++              case 't':
++                      context.view = TREE_VIEW;
++                      break;
++              case 'r':
++                      context.view = RAW_VIEW;
++                      break;
++              case 'x':
++                      context.view = XML_VIEW;
++                      break;
++              default:
++                      printf(records_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 1) {
++              printf(records_help);
++              return -1;
++      }
++ 
++      /* Convert command line parameters */
++      estr2ba(argv[0], &bdaddr);
++ 
++      for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
++              for (n = 0; n < num; n++) {
++                      context.handle = base[i] + n;
++                      err = get_service(&bdaddr, &context, 1);
++                      if (err < 0)
++                              goto done;
++              }
++ 
++ done:
++      return 0;
++ }
++ 
++ static struct option get_options[] = {
++      { "help",       0, 0, 'h' },
++      { "bdaddr",     1, 0, 'b' },
++      { "tree",       0, 0, 't' },
++      { "raw",        0, 0, 'r' },
++      { "xml",        0, 0, 'x' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ static char *get_help = 
++      "Usage:\n"
++      "\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
++ 
++ /*
++  * Get a specific SDP record on the local SDP server
++  */
++ static int cmd_get(int argc, char **argv)
++ {
++      struct search_context context;
++      bdaddr_t bdaddr;
++      int has_addr = 0;
++      int opt;
++ 
++      /* Initialise context */
++      memset(&context, '\0', sizeof(struct search_context));
++ 
++      for_each_opt(opt, get_options, 0) {
++              switch (opt) {
++              case 'b':
++                      estr2ba(optarg, &bdaddr);
++                      has_addr = 1;
++                      break;
++              case 't':
++                      context.view = TREE_VIEW;
++                      break;
++              case 'r':
++                      context.view = RAW_VIEW;
++                      break;
++              case 'x':
++                      context.view = XML_VIEW;
++                      break;
++              default:
++                      printf(get_help);
++                      return -1;
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++ 
++      if (argc < 1) {
++              printf(get_help);
++              return -1;
++      }
++ 
++      /* Convert command line parameters */
++      context.handle = strtoul(argv[0], 0, 16);
++ 
++      return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
++ }
++ 
++ static struct {
++      char *cmd;
++      int (*func)(int argc, char **argv);
++      char *doc;
++ } command[] = {
++      { "search",  cmd_search,      "Search for a service"          },
++      { "browse",  cmd_browse,      "Browse all available services" },
++      { "records", cmd_records,     "Request all records"           },
++      { "add",     cmd_add,         "Add local service"             },
++      { "del",     cmd_del,         "Delete local service"          },
++      { "get",     cmd_get,         "Get local service"             },
++      { "setattr", cmd_setattr,     "Set/Add attribute to a SDP record"          },
++      { "setseq",  cmd_setseq,      "Set/Add attribute sequence to a SDP record" },
++      { 0, 0, 0 }
++ };
++ 
++ static void usage(void)
++ {
++      int i, pos = 0;
++ 
++      printf("sdptool - SDP tool v%s\n", VERSION);
++      printf("Usage:\n"
++              "\tsdptool [options] <command> [command parameters]\n");
++      printf("Options:\n"
++              "\t-h\t\tDisplay help\n"
++              "\t-i\t\tSpecify source interface\n");
++ 
++      printf("Commands:\n");
++      for (i = 0; command[i].cmd; i++)
++              printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
++ 
++      printf("\nServices:\n\t");
++      for (i = 0; service[i].name; i++) {
++              printf("%s ", service[i].name);
++              pos += strlen(service[i].name) + 1;
++              if (pos > 60) {
++                      printf("\n\t");
++                      pos = 0;
++              }
++      }
++      printf("\n");
++ }
++ 
++ static struct option main_options[] = {
++      { "help",       0, 0, 'h' },
++      { "device",     1, 0, 'i' },
++      { 0, 0, 0, 0 }
++ };
++ 
++ int main(int argc, char *argv[])
++ {
++      int i, opt;
++ 
++      bacpy(&interface, BDADDR_ANY);
++ 
++      while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
++              switch(opt) {
++              case 'i':
++                      if (!strncmp(optarg, "hci", 3))
++                              hci_devba(atoi(optarg + 3), &interface);
++                      else
++                              str2ba(optarg, &interface);
++                      break;
++ 
++              case 'h':
++                      usage();
++                      exit(0);
++ 
++              default:
++                      exit(1);
++              }
++      }
++ 
++      argc -= optind;
++      argv += optind;
++      optind = 0;
++ 
++      if (argc < 1) {
++              usage();
++              exit(1);
++      }
++ 
++      for (i = 0; command[i].cmd; i++)
++              if (strncmp(command[i].cmd, argv[0], 4) == 0)
++                      return command[i].func(argc, argv);
++ 
++      return 1;
++ }