--- /dev/null
--- /dev/null
--- /dev/null
+++#
+++# 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
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
--- /dev/null
+++ 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
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
- #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 */
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
- #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 */
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
++/*
++ *
++ * 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);
++}
--- /dev/null
--- /dev/null
- #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++;
++}
--- /dev/null
--- /dev/null
--- /dev/null
+++include $(call all-subdir-makefiles)
--- /dev/null
--- /dev/null
--- /dev/null
+++# 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
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
--- /dev/null
+++#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);
+++}
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
++ #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);
++ }
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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, ¶m);
++
++ /* Add Trusted devices to the list */
++ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
++ textfile_foreach(filename, list_remote_devices_do_append, ¶m);
++
++ /* Add Last Used devices to the list */
++ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
++ textfile_foreach(filename, list_remote_devices_do_append, ¶m);
++
++ 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, ¶m);
++
++ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
++ textfile_foreach(filename, list_remote_devices_do_append, ¶m);
++
++ /* 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, ¶m);
++
++ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
++ textfile_foreach(filename, list_remote_devices_do_append, ¶m);
++
++ /* 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;
++ }
--- /dev/null
--- /dev/null
-
++ /*
++ *
++ * 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;
++ }
--- /dev/null
--- /dev/null
-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);
++ }
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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);
++ }
++ }
++ }
++ }
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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;
++ }
--- /dev/null
--- /dev/null
--- /dev/null
+++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
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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);
++ }
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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;
++ }
--- /dev/null
--- /dev/null
--- /dev/null
+++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)
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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;
++ }
--- /dev/null
--- /dev/null
++ /*
++ *
++ * 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;
++ }
--- /dev/null
--- /dev/null
- 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;
++ }
--- /dev/null
--- /dev/null
--- /dev/null
+++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
--- /dev/null
--- /dev/null
-#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;
++ }
--- /dev/null
--- /dev/null
--- /dev/null
+++/*
+++ *
+++ * 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;
+++}
--- /dev/null
--- /dev/null
- 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;
++ }