Dbus method call sent but no reply got
Fei Zheng
Fei.Zheng at csr.com
Wed Sep 18 02:30:04 PDT 2013
Change the topic tile.
-----Original Message-----
From: Fei Zheng
Sent: Wednesday, September 18, 2013 5:06 PM
To: dbus at lists.freedesktop.org
Cc: Wenbin Fu; Yingzhong Zha; Barry Song; Binghua Duan
Subject: RE: dbus Digest, Vol 98, Issue 11
Hi Simon,
Thank you.
Yes, this issue is due to the main loop I used is:
while(dbus_connection_read_write_dispatch(bus, -1));
After replace it with the glib main loop, or implement a main loop with poll, the reply could be sent asynchronously well. By the way bluez uses glib main loop, so it is fine.
Thanks all of you to help me fix it!
Best regards
Fei Zheng
-----Original Message-----
From: dbus-bounces+fei.zheng=csr.com at lists.freedesktop.org [mailto:dbus-bounces+fei.zheng=csr.com at lists.freedesktop.org] On Behalf Of dbus-request at lists.freedesktop.org
Sent: Tuesday, September 17, 2013 8:07 PM
To: dbus at lists.freedesktop.org
Subject: dbus Digest, Vol 98, Issue 11
Send dbus mailing list submissions to
dbus at lists.freedesktop.org
To subscribe or unsubscribe via the World Wide Web, visit
http://lists.freedesktop.org/mailman/listinfo/dbus
or, via email, send a message with subject or body 'help' to
dbus-request at lists.freedesktop.org
You can reach the person managing the list at
dbus-owner at lists.freedesktop.org
When replying, please edit your Subject line so it is more specific
than "Re: Contents of dbus digest..."
Today's Topics:
1. Re: Dbus method call sent but no reply got (Simon McVittie)
2. Re: Dbus method call sent but no reply got (Fei Zheng)
3. Re: Dbus method call sent but no reply got (Mandeep Sandhu)
4. Re: Dbus method call sent but no reply got (Simon McVittie)
5. RE: Dbus method call sent but no reply got (Fei Zheng)
6. RE: Dbus method call sent but no reply got (Fei Zheng)
----------------------------------------------------------------------
Message: 1
Date: Tue, 17 Sep 2013 11:17:56 +0100
From: Simon McVittie <simon.mcvittie at collabora.co.uk>
To: dbus at lists.freedesktop.org
Subject: Re: Dbus method call sent but no reply got
Message-ID: <52382C54.4030409 at collabora.co.uk>
Content-Type: text/plain; charset=ISO-8859-1
On 17/09/13 11:06, Yang Chengwei wrote:
> On Tue, Sep 17, 2013 at 09:50:18AM +0000, Fei Zheng wrote:
>> In the linux Bluetooth stack(bluez), some message handler works
>> asynchronous, I don't know why it has no such issue.
I suspect it's because BlueZ has an ordinary epoll/poll/select-based
main loop rather than using POSIX signals for this. If you have a
problem to solve, POSIX signals (as in signal()/sigaction()) are
usually the wrong solution.
POSIX signal handlers interrupt whatever else is going on (e.g.
libdbus blocking in read_write_dispatch, with a lock held) and run
code - it's almost as though you had two threads. That's a very
precarious state to run it - even malloc() is quite likely to crash!
That's why it isn't safe to call library functions, and that's
probably also why your code is deadlocking.
More or less the only safe thing to do in a POSIX signal handler is to
call simple syscalls like write() or close() (the well-known "pipe to
self" trick), to which you respond in the main loop.
On 17/09/13 11:06, Yang Chengwei wrote:
> As you know, asynchronous is invalid.
That isn't true. Using D-Bus asynchronously is correct, and indeed,
recommended - but you have to do your asynchronous operations in a
supported way (e.g. with a main loop, or using GLib's GDBus and its
worker thread[1]), not via POSIX signals.
> static GDBusMethodTable adapter_methods[] = { { "GetProperties",
> "", "a{sv}",get_properties }, { "SetProperty", "sv", "",
> set_property, G_DBUS_METHOD_FLAG_ASYNC},
Confusingly, BlueZ has its own wrapper around libdbus, which uses the
namespace "GDBus*", "G_DBUS_*", "g_dbus_*", but is not the same thing
as GDBus, an object-oriented reimplementation of D-Bus found in GLib.
GLib's GDBus is what I recommend using.
Regards,
S
[1] Internally, GLib's GDBus uses a main loop in a worker thread, which
is OK because, unlike libdbus, it has a well-defined threading
model. libdbus wasn't thread-safe until recently, and perhaps still
isn't.
------------------------------
Message: 2
Date: Tue, 17 Sep 2013 11:40:41 +0000
From: Fei Zheng <Fei.Zheng at csr.com>
To: "dbus at lists.freedesktop.org" <dbus at lists.freedesktop.org>
Cc: Yingzhong Zha <Yingzhong.Zha at csr.com>, Barry Song
<Barry.Song at csr.com>, Wenbin Fu <Wenbin.Fu at csr.com>
Subject: Re: Dbus method call sent but no reply got
Message-ID:
<DA7BB8DF55A0AA4FB4D08E0760C77EF80129575D77 at SHAASIEXM01.ASIA.ROOT.PRI>
Content-Type: text/plain; charset="us-ascii"
Hi Simon,
Thank you for your response.
Glib and Qt are licensed under GPL, if I use them, the code should be open, right? That is why I choose the low-level API.
In my test code the posix signal handler is used to send the reply asynchronously, it also could be replaced by a posix thread etc. As Chengwei said, the reply in dbus cannot be sent asynchronously, then I try to block in the message handler and send it synchronously. Thank you.
Best regards
Fei Zheng
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/dbus/attachments/20130917/ca2a2854/attachment-0001.html>
------------------------------
Message: 3
Date: Tue, 17 Sep 2013 17:18:46 +0530
From: Mandeep Sandhu <mandeepsandhu.chd at gmail.com>
To: Fei Zheng <Fei.Zheng at csr.com>
Cc: Yingzhong Zha <Yingzhong.Zha at csr.com>,
"dbus at lists.freedesktop.org" <dbus at lists.freedesktop.org>, Wenbin Fu
<Wenbin.Fu at csr.com>, Barry Song <Barry.Song at csr.com>
Subject: Re: Dbus method call sent but no reply got
Message-ID:
<CAC+QLdR7XLwo+M9+mWU6ddx=hfjTeOtVQwK9xgGbK2FQUkCU-A at mail.gmail.com>
Content-Type: text/plain; charset="iso-8859-1"
On Tue, Sep 17, 2013 at 5:10 PM, Fei Zheng <Fei.Zheng at csr.com> wrote:
> Hi Simon,****
>
> ** **
>
> Thank you for your response.****
>
> ** **
>
> Glib and Qt are licensed under GPL, if I use them, the code should be
> open, right? That is why I choose the low-level API.****
>
Both are available under LGPL, so you need not opensource your code. This
applies to dynamic linking only though.
HTH,
-mandeep
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/dbus/attachments/20130917/ed66e269/attachment-0001.html>
------------------------------
Message: 4
Date: Tue, 17 Sep 2013 12:59:12 +0100
From: Simon McVittie <simon.mcvittie at collabora.co.uk>
To: dbus at lists.freedesktop.org
Subject: Re: Dbus method call sent but no reply got
Message-ID: <52384410.6040203 at collabora.co.uk>
Content-Type: text/plain; charset=ISO-8859-1
On 17/09/13 12:40, Fei Zheng wrote:
> Glib and Qt are licensed under GPL, if I use them, the code should be
> open, right? That is why I choose the low-level API.
GLib and Qt are both licensed under the Lesser GPL, a weaker copyleft
license than the GPL, which means they can be used in proprietary software.
> In my test code the posix signal handler is used to send the reply
> asynchronously, it also could be replaced by a posix thread etc.
D-Bus is designed to be used asynchronously from a main loop (event
loop, etc.) - see <https://en.wikipedia.org/wiki/Event_loop> and
specifically the "File interface" section. I recommend this approach.
In my experience, using threads to emulate asynchronous I/O only works
if you are very, very careful, and if not, you just get:
"You had a problem and you solved it with threads. Now you have two
problems, in parallel, and they interfere with each other."
Worse, using POSIX signals to emulate asynchronous I/O tends to result in:
"You had a problem and you solved it with POSIX signals. Now you still
have a problem, but it can be interrupted at any time by another problem."
Regards,
S
------------------------------
Message: 5
Date: Tue, 17 Sep 2013 12:03:31 +0000
From: Fei Zheng <Fei.Zheng at csr.com>
To: Mandeep Sandhu <mandeepsandhu.chd at gmail.com>
Cc: Yingzhong Zha <Yingzhong.Zha at csr.com>,
"dbus at lists.freedesktop.org" <dbus at lists.freedesktop.org>, Wenbin Fu
<Wenbin.Fu at csr.com>, Barry Song <Barry.Song at csr.com>
Subject: RE: Dbus method call sent but no reply got
Message-ID:
<DA7BB8DF55A0AA4FB4D08E0760C77EF80129575EBD at SHAASIEXM01.ASIA.ROOT.PRI>
Content-Type: text/plain; charset="us-ascii"
Hi Mandeep,
I got it. Thank you.
Best regards
Fei Zheng
From: Mandeep Sandhu [mailto:mandeepsandhu.chd at gmail.com]
Sent: Tuesday, September 17, 2013 7:49 PM
To: Fei Zheng
Cc: dbus at lists.freedesktop.org; Yingzhong Zha; Barry Song; Wenbin Fu
Subject: Re: Dbus method call sent but no reply got
On Tue, Sep 17, 2013 at 5:10 PM, Fei Zheng <Fei.Zheng at csr.com<mailto:Fei.Zheng at csr.com>> wrote:
Hi Simon,
Thank you for your response.
Glib and Qt are licensed under GPL, if I use them, the code should be open, right? That is why I choose the low-level API.
Both are available under LGPL, so you need not opensource your code. This applies to dynamic linking only though.
HTH,
-mandeep
To report this email as spam click here<https://www.mailcontrol.com/sr/MZbqvYs5QwJvpeaetUwhCQ==>.
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/dbus/attachments/20130917/02e4c011/attachment-0001.html>
------------------------------
Message: 6
Date: Tue, 17 Sep 2013 12:00:37 +0000
From: Fei Zheng <Fei.Zheng at csr.com>
To: Yang Chengwei <chengwei.yang at intel.com>
Cc: Yingzhong Zha <Yingzhong.Zha at csr.com>,
"dbus at lists.freedesktop.org" <dbus at lists.freedesktop.org>, Wenbin Fu
<Wenbin.Fu at csr.com>, Barry Song <Barry.Song at csr.com>
Subject: RE: Dbus method call sent but no reply got
Message-ID:
<DA7BB8DF55A0AA4FB4D08E0760C77EF80129575D8F at SHAASIEXM01.ASIA.ROOT.PRI>
Content-Type: text/plain; charset="utf-8"
Yes. But I still confused by it. Bluez use gdbus, and gdbus is also implemented by the dbus low api. I have checked the gdbus code with bluez, but it seems same with the way I used.
In object.c line 519, object_path_ref() calls dbus_connection_register_object_path() to register generic_table to handle the message. In object.c line 454, generic_message() is the message handler, and in this function process_message() is called to process the message, in line 232, when the method handler function is marked asynchronously, and the rely is null, DBUS_HANDLER_RESULT_HANDLED is returned directly instead of sending reply to client. >From the code it seems using the way glib works is same with the test code I attached. Thanks.
Best regards
Fei Zheng
-----Original Message-----
From: Yang Chengwei [mailto:chengwei.yang at intel.com]
Sent: Tuesday, September 17, 2013 6:07 PM
To: Fei Zheng
Cc: Yang Chengwei; dbus at lists.freedesktop.org; Yingzhong Zha; Barry Song; Wenbin Fu
Subject: Re: Dbus method call sent but no reply got
On Tue, Sep 17, 2013 at 09:50:18AM +0000, Fei Zheng wrote:
> Hi Chengwei,
>
> Thank you for your response.
>
> There is the scenario in my side: When handling a method call, the data for reply may not ready in the message handler, then I don?t want to send reply in message handler, and send the reply later when the data is ready. Then it would be asynchronous scenario.
As you know, asynchronous is invalid. There are general two ways to
work around.
1. make it synchronous, then the server will block and then send back
reply, this may cause a timeout error if the data isn't ready in time.
2. broadcast a signal if the data is ready and the client then send
request to server.
>
> In the linux Bluetooth stack(bluez), some message handler works asynchronous, I don't know why it has no such issue.
>
> The bluez code adapter.c is attached, in line 559, set_discoverable() is called by the message handler to handle the method call and send the reply, but it return null. The reply is sent asynchronously in line 2897: in the function set_mode_complete().
The magic is GDBus, see below.
static GDBusMethodTable adapter_methods[] = {
{ "GetProperties", "", "a{sv}",get_properties },
{ "SetProperty", "sv", "", set_property,
G_DBUS_METHOD_FLAG_ASYNC},
>
> The way it works almost same with the test code, but the bluez works.
>
> Could you give some advice on it? Thanks.
So as Simon already suggested, maybe GDBus is your choice.
--
Thanks,
Chengwei
>
> Best regards
> Fei Zheng
>
> -----Original Message-----
> From: Yang Chengwei [mailto:chengwei.yang at intel.com]
> Sent: Tuesday, September 17, 2013 5:03 PM
> To: Fei Zheng
> Cc: dbus at lists.freedesktop.org; Yingzhong Zha; Barry Song; Wenbin Fu
> Subject: Re: Dbus method call sent but no reply got
>
> On Mon, Sep 16, 2013 at 05:59:27AM +0000, Fei Zheng wrote:
> > Hi,
> >
> >
> >
> > I need your help to resolve a problem about making method call but no
> > reply got.
> >
> >
> >
> > In Dbus_send.c, method call is the sent to remote and block to wait the reply.
> >
> > 1. dbus_bus_get()
> >
> > 2. dbus_message_new_method_call()
> >
> > 3. dbus_connection_send_with_reply_and_block()
> >
> >
> >
> > in dbus_recv.c, message_handler () is registered to handle the method
> > call, then enter the main loop to dispatch the message.
> >
> > 1. dbus_bus_get()
> >
> > 2. dbus_bus_request_name()
> >
> > 3. dbus_connection_register_object_path()
> >
> > 4. dbus_connection_read_write_dispatch()
> >
> >
> >
> > In message_handler, if signal_handler() is called to send the reply in
> > the this function, In Dbus_send.c the caller could get the reply
> > correctly. But if the reply is sent asynchronously in a signal
> > handler, the reply could not be got by the caller.
> >
> >
> >
> > static DBusHandlerResult message_handler (DBusConnection *connection,
> > DBusMessage *message, void *user_data)
>
> The message_handler is a user defined callback from libdbus view, and will be invoked to handle messages. That means when libdbus comes here, the socket is writable and you can send reply/error. After that, the socket status will changed to readable and wait for input infinitely.
> And later write is invalid and can not get the socket io lock.
>
> I'm wondering why you're trying to handle message in signal handler?
>
> --
> Thanks,
> Chengwei
>
> >
> > {
> >
> > dmsg = message;
> >
> > dconn = connection;
> >
> >
> >
> > #if 1
> >
> > //reply cannot be got by sender when reply is sent in
> > a signal handler.
> >
> >
> >
> > printf("Create signal\n");
> >
> > incb = 0;
> >
> > signal(SIGALRM, sigHandler);
> >
> > printf("Start timer \n");
> >
> > alarm(1);
> >
> > printf("Increase the reference\n");
> >
> > dbus_connection_ref(connection);
> >
> > dbus_message_ref(message);
> >
> >
> >
> > #else
> >
> > // it is ok to send the reply here.
> >
> > signal_handler();
> >
> > #endif
> >
> >
> >
> > return DBUS_HANDLER_RESULT_HANDLED;
> >
> > }
> >
> >
> >
> > The two file is attached, could you help check what?s wrong with it? Thanks.
> >
> >
> >
> > Best regards
> >
> > Fei Zheng
> >
> >
> >
> >
> >
> > Member of the CSR plc group of companies. CSR plc registered in
> > England and Wales, registered number 4187346, registered office
> > Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4
> > 0WZ, United Kingdom More information can be found at www.csr.com.
> > Follow CSR on Twitter at http:// twitter.com/CSR_PLC and read our blog
> > at www.csr.com/blog
>
> > // #include <glib.h>
> > #include <dbus/dbus.h>
> > // #include <dbus/dbus-glib.h>
> > #include <stdio.h>
> > #include <signal.h>
> >
> > static DBusHandlerResult message_handler(DBusConnection *connection,
> > DBusMessage *message, void *user_data); static void signal_handler();
> >
> > DBusConnection *dconn;
> > DBusMessage *dmsg;
> > int incb = 0;
> >
> > #define object_event_filter message_handler
> >
> > #define LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg) \
> > { printf("%s: D-Bus error in %s: %s (%s)", __FUNCTION__, \
> > dbus_message_get_member((msg)), (err)->name, (err)->message); \
> > dbus_error_free((err)); }
> > #define LOG_AND_FREE_DBUS_ERROR(err) \
> > { printf("%s: D-Bus error: %s (%s)", __FUNCTION__, \
> > (err)->name, (err)->message); \
> > dbus_error_free((err)); }
> >
> >
> > static const DBusObjectPathVTable object_vtable = {
> > NULL, object_event_filter, NULL, NULL, NULL, NULL };
> >
> > int main()
> > {
> >
> > DBusConnection *bus;
> > DBusError error;
> > int res;
> >
> > dbus_error_init(&error);
> >
> > dbus_threads_init_default();
> >
> > char object_path[] = "/com/csr/test/path";
> >
> > bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
> > if (!bus) {
> > printf("Connect d-Bus failed: %s", error.message);
> >
> > dbus_error_free(&error);
> > return 1;
> > }
> >
> > /* request our name on the bus and check for errors */
> > res = dbus_bus_request_name(bus, "com.csr.test", DBUS_NAME_FLAG_REPLACE_EXISTING , &error);
> > if (dbus_error_is_set(&error)) {
> > printf("Name Error (%s)", error.message);
> > dbus_error_free(&error);
> > }
> > printf("dbus_bus_request_name, return %d\n", res);
> >
> > if (dbus_error_is_set(&error)) {
> > printf("add match Error (%s)", error.message);
> > dbus_error_free(&error);
> > }
> >
> > printf("Register objext path:%s\n", object_path);
> >
> > if (!dbus_connection_register_object_path(bus, object_path,
> > &object_vtable, NULL))
> > {
> > printf("%s: Can't register object path %s for agent!",
> > __FUNCTION__, object_path);
> > return -1;
> > }
> >
> > while(dbus_connection_read_write_dispatch(bus, -1));
> > return 0;
> > }
> >
> > static void sigHandler()
> > {
> > printf("in sigHandler\n");
> >
> > signal_handler();
> > }
> >
> >
> > static DBusHandlerResult message_handler (DBusConnection
> > *connection, DBusMessage *message, void *user_data) {
> > dmsg = message;
> > dconn = connection;
> >
> > #if 1
> > printf("Create signal\n");
> > incb = 0;
> > signal(SIGALRM, sigHandler);
> > printf("Start timer \n");
> > alarm(1);
> > printf("Increase the reference\n");
> > dbus_connection_ref(connection);
> > dbus_message_ref(message);
> >
> > #else
> > signal_handler();
> > #endif
> >
> > return DBUS_HANDLER_RESULT_HANDLED;
> > }
> >
> > static void signal_handler()
> > {
> > DBusMessage *message = dmsg;
> > DBusConnection *connection = dconn;
> >
> > if (dbus_message_is_method_call(message, "com.csr.test.Adapter", "SetProperty"))
> > {
> > printf("getProperty is got\n");
> > // reply
> > DBusMessage *reply = dbus_message_new_method_return(message);
> >
> > if (!reply)
> > {
> > printf("%s: Cannot create message reply\n", __FUNCTION__);
> > return;
> > }
> >
> > printf("Send reply to remote, return %d\n", dbus_connection_send(connection, reply, NULL));
> >
> > printf("Free the message\n");
> > dbus_message_unref(dmsg);
> > printf("Free the reply\n");
> > dbus_message_unref(reply);
> > }
> >
> > return ;
> > }
> >
>
> > #include <glib.h>
> > #include <dbus/dbus.h>
> > //#include <dbus/dbus-glib.h>
> > #include <stdio.h>
> >
> >
> > static int send_method_call(DBusConnection *bus);
> >
> > #define DBUS_NAME "com.csr.test"
> > //#define DBUS_NAME_AGENT "com.csr.test.agent"
> > #define DBUS_PATH "/com/csr/test/path"
> > #define DBUS_PATH_AGENT "/com/csr/test/agent"
> > #define DBUS_INTERFACE_APAPTER "com.csr.test.Adapter"
> > #define DBUS_INTERFACE_AGENT "com.csr.test.Agent"
> >
> >
> > // LOGE and free a D-Bus error
> > // Using #define so that __FUNCTION__ resolves usefully
> > #define LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg) \
> > { printf("%s: D-Bus error in %s: %s (%s)", __FUNCTION__, \
> > dbus_message_get_member((msg)), (err)->name, (err)->message); \
> > dbus_error_free((err)); }
> > #define LOG_AND_FREE_DBUS_ERROR(err) \
> > { printf("%s: D-Bus error: %s (%s)", __FUNCTION__, \
> > (err)->name, (err)->message); \
> > dbus_error_free((err)); }
> >
> > int main ()
> > {
> > DBusConnection *bus;
> > DBusError error;
> >
> > char object_path[] = DBUS_PATH;
> >
> > dbus_error_init (&error);
> >
> > printf("Connect to dbus\n");
> > bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
> > if (!bus) {
> > g_warning("Conenct D-Bus failed: %s", error.message);
> > dbus_error_free(&error);
> > return 1;
> > }
> >
> > printf("Send method call \n");
> >
> > send_method_call(bus);
> >
> > printf("Start loop\n");
> >
> > while(dbus_connection_read_write_dispatch(bus, -1));
> >
> > printf("Return from main\n");
> > return 0;
> > }
> >
> > #define STRCMP strcmp
> >
> > static int send_method_call(DBusConnection *bus)
> > {
> > DBusMessage *msg = NULL;
> > DBusMessage *reply = NULL;
> > DBusError error;
> > DBusMessageIter iter,subiter;
> >
> > char *name = "DiscoverableTimeout";
> > int discoverable_timeout = 60;
> > int pairable_timeout = 0;
> > char powered = 0;
> >
> > dbus_error_init(&error);
> > /* Compose the command */
> > printf("Make a method call\n");
> > msg = dbus_message_new_method_call(DBUS_NAME,
> > DBUS_PATH,
> > DBUS_INTERFACE_APAPTER, "SetProperty");
> >
> >
> > printf("Send and wait for reply\n");
> > reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &error);
> >
> > if (dbus_error_is_set(&error)) {
> > LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&error, msg);
> > goto done;
> > }
> > else
> > {
> > printf("Reply is got\n");
> > }
> >
> > done:
> > if (reply) dbus_message_unref(reply);
> > if (msg) dbus_message_unref(msg);
> >
> > return TRUE;
> > }
> >
> >
>
> > _______________________________________________
> > dbus mailing list
> > dbus at lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dbus
>
> /*
> *
> * BlueZ - Bluetooth protocol stack for Linux
> *
> * Copyright (C) 2006-2010 Nokia Corporation
> * Copyright (C) 2004-2010 Marcel Holtmann <marcel at 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 <sys/ioctl.h>
>
> #ifdef ANDROID_EXPAND_NAME
> #include <cutils/properties.h>
> #endif
>
> #include <bluetooth/bluetooth.h>
> #include <bluetooth/uuid.h>
> #include <bluetooth/sdp.h>
> #include <bluetooth/sdp_lib.h>
>
> #include <glib.h>
> #include <dbus/dbus.h>
> #include <gdbus.h>
>
> #include "log.h"
> #include "textfile.h"
>
> #include "hcid.h"
> #include "sdpd.h"
> #include "adapter.h"
> #include "manager.h"
> #include "device.h"
> #include "dbus-common.h"
> #include "event.h"
> #include "error.h"
> #include "glib-helper.h"
> #include "agent.h"
> #include "storage.h"
> #include "attrib-server.h"
> #include "att.h"
> #include "eir.h"
>
> /* Flags Descriptions */
> #define EIR_LIM_DISC 0x01 /* LE Limited Discoverable Mode */
> #define EIR_GEN_DISC 0x02 /* LE General Discoverable Mode */
> #define EIR_BREDR_UNSUP 0x04 /* BR/EDR Not Supported */
> #define EIR_SIM_CONTROLLER 0x08 /* Simultaneous LE and BR/EDR to Same
> Device Capable (Controller) */
> #define EIR_SIM_HOST 0x10 /* Simultaneous LE and BR/EDR to Same
> Device Capable (Host) */
>
> #define IO_CAPABILITY_DISPLAYONLY 0x00
> #define IO_CAPABILITY_DISPLAYYESNO 0x01
> #define IO_CAPABILITY_KEYBOARDONLY 0x02
> #define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
> #define IO_CAPABILITY_INVALID 0xFF
>
> #define check_address(address) bachk(address)
>
> static DBusConnection *connection = NULL;
> static GSList *adapter_drivers = NULL;
>
> static GSList *ops_candidates = NULL;
>
> const struct btd_adapter_ops *adapter_ops = NULL;
>
> struct session_req {
> struct btd_adapter *adapter;
> DBusConnection *conn; /* Connection reference */
> DBusMessage *msg; /* Unreplied message ref */
> char *owner; /* Bus name of the owner */
> guint id; /* Listener id */
> uint8_t mode; /* Requested mode */
> int refcount; /* Session refcount */
> gboolean got_reply; /* Agent reply received */
> };
>
> struct service_auth {
> service_auth_cb cb;
> void *user_data;
> struct btd_device *device;
> struct btd_adapter *adapter;
> };
>
> struct btd_adapter {
> uint16_t dev_id;
> int up;
> char *path; /* adapter object path */
> bdaddr_t bdaddr; /* adapter Bluetooth Address */
> uint32_t dev_class; /* Class of Device */
> char name[MAX_NAME_LENGTH + 1]; /* adapter name */
> guint discov_timeout_id; /* discoverable timeout id */
> guint stop_discov_id; /* stop inquiry/scanning id */
> uint32_t discov_timeout; /* discoverable time(sec) */
> guint pairable_timeout_id; /* pairable timeout id */
> uint32_t pairable_timeout; /* pairable time(sec) */
> uint8_t scan_mode; /* scan mode: SCAN_DISABLED, SCAN_PAGE,
> * SCAN_INQUIRY */
> uint8_t mode; /* off, connectable, discoverable,
> * limited */
> uint8_t global_mode; /* last valid global mode */
> struct session_req *pending_mode;
> int state; /* standard inq, periodic inq, name
> * resolving, suspended discovery */
> GSList *found_devices;
> GSList *oor_devices; /* out of range device list */
> struct agent *agent; /* For the new API */
> guint auth_idle_id; /* Ongoing authorization */
> GSList *connections; /* Connected devices */
> GSList *devices; /* Devices structure pointers */
> GSList *mode_sessions; /* Request Mode sessions */
> GSList *disc_sessions; /* Discovery sessions */
> guint scheduler_id; /* Scheduler handle */
> sdp_list_t *services; /* Services associated to adapter */
>
> uint8_t features[8];
>
> gboolean pairable; /* pairable state */
> gboolean initialized;
>
> gboolean off_requested; /* DEVDOWN ioctl was called */
>
> gint ref;
>
> GSList *powered_callbacks;
>
> gboolean name_stored;
>
> GSList *loaded_drivers;
> };
>
> static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
> guint interval);
> static DBusMessage *set_discoverable(DBusConnection *conn, DBusMessage *msg,
> gboolean discoverable, void *data);
>
> static 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;
> }
>
> static void dev_info_free(struct remote_dev_info *dev)
> {
> g_free(dev->name);
> g_free(dev->alias);
> g_slist_foreach(dev->services, (GFunc) g_free, NULL);
> g_slist_free(dev->services);
> g_strfreev(dev->uuids);
> g_free(dev);
> }
>
> /*
> * 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 = main_opts.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;
> }
>
> int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
> uint8_t minor)
> {
> return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
> }
>
> static int pending_remote_name_cancel(struct btd_adapter *adapter)
> {
> struct remote_dev_info *dev, match;
> int err;
>
> /* find the pending remote name request */
> memset(&match, 0, sizeof(struct remote_dev_info));
> bacpy(&match.bdaddr, BDADDR_ANY);
> match.name_status = NAME_REQUESTED;
>
> dev = adapter_search_found_devices(adapter, &match);
> if (!dev) /* no pending request */
> return -ENODATA;
>
> err = adapter_ops->cancel_resolve_name(adapter->dev_id, &dev->bdaddr);
> if (err < 0)
> error("Remote name cancel failed: %s(%d)",
> strerror(errno), errno);
>
> adapter_set_state(adapter, STATE_IDLE);
>
> return err;
> }
>
> int adapter_resolve_names(struct btd_adapter *adapter)
> {
> struct remote_dev_info *dev, match;
> int err;
>
> /* Do not attempt to resolve more names if on suspended state */
> if (adapter->state == STATE_SUSPENDED)
> return 0;
>
> memset(&match, 0, sizeof(struct remote_dev_info));
> bacpy(&match.bdaddr, BDADDR_ANY);
> match.name_status = NAME_REQUIRED;
>
> dev = adapter_search_found_devices(adapter, &match);
> if (!dev)
> return -ENODATA;
>
> /* send at least one request or return failed if the list is empty */
> do {
> /* flag to indicate the current remote name requested */
> dev->name_status = NAME_REQUESTED;
>
> err = adapter_ops->resolve_name(adapter->dev_id, &dev->bdaddr);
>
> if (!err)
> break;
>
> error("Unable to send HCI remote name req: %s (%d)",
> strerror(errno), errno);
>
> /* if failed, request the next element */
> /* remove the element from the list */
> adapter_remove_found_device(adapter, &dev->bdaddr);
>
> /* get the next element */
> dev = adapter_search_found_devices(adapter, &match);
> } while (dev);
>
> return err;
> }
>
> static const char *mode2str(uint8_t mode)
> {
> switch(mode) {
> case MODE_OFF:
> return "off";
> case MODE_CONNECTABLE:
> return "connectable";
> case MODE_DISCOVERABLE:
> return "discoverable";
> default:
> return "unknown";
> }
> }
>
> static uint8_t get_mode(const bdaddr_t *bdaddr, 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("on", mode) == 0) {
> char onmode[14], srcaddr[18];
>
> ba2str(bdaddr, srcaddr);
> if (read_on_mode(srcaddr, onmode, sizeof(onmode)) < 0)
> return MODE_CONNECTABLE;
>
> return get_mode(bdaddr, onmode);
> } else
> return MODE_UNKNOWN;
> }
>
> static void adapter_set_limited_discoverable(struct btd_adapter *adapter,
> gboolean limited)
> {
> DBG("%s", limited ? "TRUE" : "FALSE");
>
> adapter_ops->set_limited_discoverable(adapter->dev_id, limited);
> }
>
> static void adapter_remove_discov_timeout(struct btd_adapter *adapter)
> {
> if (!adapter)
> return;
>
> if (adapter->discov_timeout_id == 0)
> return;
>
> g_source_remove(adapter->discov_timeout_id);
> adapter->discov_timeout_id = 0;
> }
>
> static gboolean discov_timeout_handler(gpointer user_data)
> {
> struct btd_adapter *adapter = user_data;
>
> adapter->discov_timeout_id = 0;
>
> set_discoverable(NULL, NULL, FALSE, user_data);
>
> return FALSE;
> }
>
> static void adapter_set_discov_timeout(struct btd_adapter *adapter,
> guint interval)
> {
> if (adapter->discov_timeout_id) {
> g_source_remove(adapter->discov_timeout_id);
> adapter->discov_timeout_id = 0;
> }
>
> if (interval == 0) {
> adapter_set_limited_discoverable(adapter, FALSE);
> return;
> }
>
> /* Set limited discoverable if pairable and interval between 0 to 60
> sec */
> if (adapter->pairable && interval <= 60)
> adapter_set_limited_discoverable(adapter, TRUE);
> else
> adapter_set_limited_discoverable(adapter, FALSE);
>
> adapter->discov_timeout_id = g_timeout_add_seconds(interval,
> discov_timeout_handler,
> adapter);
> }
>
> static struct session_req *session_ref(struct session_req *req)
> {
> req->refcount++;
>
> DBG("%p: ref=%d", req, req->refcount);
>
> return req;
> }
>
> static struct session_req *create_session(struct btd_adapter *adapter,
> DBusConnection *conn, DBusMessage *msg,
> uint8_t mode, GDBusWatchFunction cb)
> {
> const char *sender = dbus_message_get_sender(msg);
> struct session_req *req;
>
> req = g_new0(struct session_req, 1);
> req->adapter = adapter;
> req->conn = dbus_connection_ref(conn);
> req->msg = dbus_message_ref(msg);
> req->mode = mode;
>
> if (cb == NULL)
> return session_ref(req);
>
> req->owner = g_strdup(sender);
> req->id = g_dbus_add_disconnect_watch(conn, sender, cb, req, NULL);
>
> info("%s session %p with %s activated",
> req->mode ? "Mode" : "Discovery", req, sender);
>
> return session_ref(req);
> }
>
> static int adapter_set_mode(struct btd_adapter *adapter, uint8_t mode)
> {
> int err;
>
> if (mode == MODE_CONNECTABLE)
> err = adapter_ops->set_discoverable(adapter->dev_id, FALSE);
> else
> err = adapter_ops->set_discoverable(adapter->dev_id, TRUE);
>
> if (err < 0)
> return err;
>
> if (mode == MODE_CONNECTABLE)
> return 0;
>
> adapter_remove_discov_timeout(adapter);
>
> if (adapter->discov_timeout)
> adapter_set_discov_timeout(adapter, adapter->discov_timeout);
>
> return 0;
> }
>
> static struct session_req *find_session_by_msg(GSList *list, const DBusMessage *msg)
> {
> for (; list; list = list->next) {
> struct session_req *req = list->data;
>
> if (req->msg == msg)
> return req;
> }
>
> return NULL;
> }
>
> static int set_mode(struct btd_adapter *adapter, uint8_t new_mode,
> DBusMessage *msg)
> {
> int err;
> const char *modestr;
>
> if (adapter->pending_mode != NULL)
> return -EALREADY;
>
> if (!adapter->up && new_mode != MODE_OFF) {
> err = adapter_ops->set_powered(adapter->dev_id, TRUE);
> if (err < 0)
> return err;
>
> goto done;
> }
>
> if (adapter->up && new_mode == MODE_OFF) {
> err = adapter_ops->set_powered(adapter->dev_id, FALSE);
> if (err < 0)
> return err;
>
> adapter->off_requested = TRUE;
>
> goto done;
> }
>
> if (new_mode == adapter->mode)
> return 0;
>
> err = adapter_set_mode(adapter, new_mode);
>
> if (err < 0)
> return err;
>
> done:
> modestr = mode2str(new_mode);
> write_device_mode(&adapter->bdaddr, modestr);
>
> DBG("%s", modestr);
>
> if (msg != NULL) {
> struct session_req *req;
>
> req = find_session_by_msg(adapter->mode_sessions, msg);
> if (req) {
> adapter->pending_mode = req;
> session_ref(req);
> } else
> /* Wait for mode change to reply */
> adapter->pending_mode = create_session(adapter,
> connection, msg, new_mode, NULL);
> } else
> /* Nothing to reply just write the new mode */
> adapter->mode = new_mode;
>
> return 0;
> }
>
> static DBusMessage *set_discoverable(DBusConnection *conn, DBusMessage *msg,
> gboolean discoverable, void *data)
> {
> struct btd_adapter *adapter = data;
> uint8_t mode;
> int err;
>
> mode = discoverable ? MODE_DISCOVERABLE : MODE_CONNECTABLE;
>
> if (mode == adapter->mode) {
> adapter->global_mode = mode;
> return dbus_message_new_method_return(msg);
> }
>
> err = set_mode(adapter, mode, msg);
> if (err < 0)
> return btd_error_failed(msg, strerror(-err));
>
> return NULL;
> }
>
> static DBusMessage *set_powered(DBusConnection *conn, DBusMessage *msg,
> gboolean powered, void *data)
> {
> struct btd_adapter *adapter = data;
> uint8_t mode;
> int err;
>
> if (powered) {
> mode = get_mode(&adapter->bdaddr, "on");
> return set_discoverable(conn, msg, mode == MODE_DISCOVERABLE,
> data);
> }
>
> mode = MODE_OFF;
>
> if (mode == adapter->mode) {
> adapter->global_mode = mode;
> return dbus_message_new_method_return(msg);
> }
>
> err = set_mode(adapter, mode, msg);
> if (err < 0)
> return btd_error_failed(msg, strerror(-err));
>
> return NULL;
> }
>
> void btd_adapter_pairable_changed(struct btd_adapter *adapter,
> gboolean pairable)
> {
> adapter->pairable = pairable;
>
> write_device_pairable(&adapter->bdaddr, pairable);
>
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Pairable",
> DBUS_TYPE_BOOLEAN, &pairable);
>
> if (pairable && adapter->pairable_timeout)
> adapter_set_pairable_timeout(adapter,
> adapter->pairable_timeout);
> }
>
> static DBusMessage *set_pairable(DBusConnection *conn, DBusMessage *msg,
> gboolean pairable, void *data)
> {
> struct btd_adapter *adapter = data;
> int err;
>
> if (adapter->scan_mode == SCAN_DISABLED)
> return btd_error_not_ready(msg);
>
> if (pairable == adapter->pairable)
> goto done;
>
> if (!(adapter->scan_mode & SCAN_INQUIRY))
> goto store;
>
> err = set_mode(adapter, MODE_DISCOVERABLE, NULL);
> if (err < 0 && msg)
> return btd_error_failed(msg, strerror(-err));
>
> store:
> adapter_ops->set_pairable(adapter->dev_id, pairable);
>
> done:
> return msg ? dbus_message_new_method_return(msg) : NULL;
> }
>
> static gboolean pairable_timeout_handler(void *data)
> {
> set_pairable(NULL, NULL, FALSE, data);
>
> return FALSE;
> }
>
> static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
> guint interval)
> {
> if (adapter->pairable_timeout_id) {
> g_source_remove(adapter->pairable_timeout_id);
> adapter->pairable_timeout_id = 0;
> }
>
> if (interval == 0)
> return;
>
> adapter->pairable_timeout_id = g_timeout_add_seconds(interval,
> pairable_timeout_handler,
> adapter);
> }
>
> static struct session_req *find_session(GSList *list, const char *sender)
> {
> for (; list; list = list->next) {
> struct session_req *req = list->data;
>
> if (g_str_equal(req->owner, sender))
> return req;
> }
>
> return NULL;
> }
>
> static uint8_t get_needed_mode(struct btd_adapter *adapter, uint8_t mode)
> {
> GSList *l;
>
> if (adapter->global_mode > mode)
> mode = adapter->global_mode;
>
> for (l = adapter->mode_sessions; l; l = l->next) {
> struct session_req *req = l->data;
>
> if (req->mode > mode)
> mode = req->mode;
> }
>
> return mode;
> }
>
> static GSList *remove_bredr(GSList *all)
> {
> GSList *l, *le;
>
> for (l = all, le = NULL; l; l = l->next) {
> struct remote_dev_info *dev = l->data;
> if (dev->le == FALSE) {
> dev_info_free(dev);
> continue;
> }
>
> le = g_slist_append(le, dev);
> }
>
> g_slist_free(all);
>
> return le;
> }
>
> static void stop_discovery(struct btd_adapter *adapter)
> {
> pending_remote_name_cancel(adapter);
>
> adapter->found_devices = remove_bredr(adapter->found_devices);
>
> if (adapter->oor_devices) {
> g_slist_free(adapter->oor_devices);
> adapter->oor_devices = NULL;
> }
>
> /* Reset if suspended, otherwise remove timer (software scheduler)
> or request inquiry to stop */
> if (adapter->state == STATE_SUSPENDED) {
> adapter_set_state(adapter, STATE_IDLE);
> return;
> }
>
> if (adapter->scheduler_id) {
> g_source_remove(adapter->scheduler_id);
> adapter->scheduler_id = 0;
> return;
> }
>
> adapter_ops->stop_discovery(adapter->dev_id);
> }
>
> static void session_remove(struct session_req *req)
> {
> struct btd_adapter *adapter = req->adapter;
>
> /* Ignore set_mode session */
> if (req->owner == NULL)
> return;
>
> DBG("%s session %p with %s deactivated",
> req->mode ? "Mode" : "Discovery", req, req->owner);
>
> if (req->mode) {
> uint8_t mode;
>
> adapter->mode_sessions = g_slist_remove(adapter->mode_sessions,
> req);
>
> mode = get_needed_mode(adapter, adapter->global_mode);
>
> if (mode == adapter->mode)
> return;
>
> DBG("Switching to '%s' mode", mode2str(mode));
>
> set_mode(adapter, mode, NULL);
> } else {
> adapter->disc_sessions = g_slist_remove(adapter->disc_sessions,
> req);
>
> if (adapter->disc_sessions)
> return;
>
> DBG("Stopping discovery");
>
> stop_discovery(adapter);
> }
> }
>
> static void session_free(struct session_req *req)
> {
> if (req->id)
> g_dbus_remove_watch(req->conn, req->id);
>
> session_remove(req);
>
> if (req->msg) {
> dbus_message_unref(req->msg);
> if (!req->got_reply && req->mode && req->adapter->agent)
> agent_cancel(req->adapter->agent);
> }
>
> if (req->conn)
> dbus_connection_unref(req->conn);
> g_free(req->owner);
> g_free(req);
> }
>
> static void session_owner_exit(DBusConnection *conn, void *user_data)
> {
> struct session_req *req = user_data;
>
> req->id = 0;
>
> session_free(req);
> }
>
> static void session_unref(struct session_req *req)
> {
> req->refcount--;
>
> DBG("%p: ref=%d", req, req->refcount);
>
> if (req->refcount)
> return;
>
> session_free(req);
> }
>
> static void confirm_mode_cb(struct agent *agent, DBusError *derr, void *data)
> {
> struct session_req *req = data;
> int err;
> DBusMessage *reply;
>
> req->got_reply = TRUE;
>
> if (derr && dbus_error_is_set(derr)) {
> reply = dbus_message_new_error(req->msg, derr->name,
> derr->message);
> g_dbus_send_message(req->conn, reply);
> session_unref(req);
> return;
> }
>
> err = set_mode(req->adapter, req->mode, req->msg);
> if (err < 0)
> reply = btd_error_failed(req->msg, strerror(-err));
> else if (!req->adapter->pending_mode)
> reply = dbus_message_new_method_return(req->msg);
> else
> reply = NULL;
>
> if (reply) {
> /*
> * Send reply immediately only if there was an error changing
> * mode, or change is not needed. Otherwise, reply is sent in
> * set_mode_complete.
> */
> g_dbus_send_message(req->conn, reply);
>
> dbus_message_unref(req->msg);
> req->msg = NULL;
> }
>
> if (!find_session(req->adapter->mode_sessions, req->owner))
> session_unref(req);
> }
>
> static DBusMessage *set_discoverable_timeout(DBusConnection *conn,
> DBusMessage *msg,
> uint32_t timeout,
> void *data)
> {
> struct btd_adapter *adapter = data;
> const char *path;
>
> if (adapter->discov_timeout == timeout && timeout == 0)
> return dbus_message_new_method_return(msg);
>
> if (adapter->scan_mode & SCAN_INQUIRY)
> adapter_set_discov_timeout(adapter, timeout);
>
> adapter->discov_timeout = timeout;
>
> write_discoverable_timeout(&adapter->bdaddr, timeout);
>
> path = dbus_message_get_path(msg);
>
> emit_property_changed(conn, path,
> ADAPTER_INTERFACE, "DiscoverableTimeout",
> DBUS_TYPE_UINT32, &timeout);
>
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *set_pairable_timeout(DBusConnection *conn,
> DBusMessage *msg,
> uint32_t timeout,
> void *data)
> {
> struct btd_adapter *adapter = data;
> const char *path;
>
> if (adapter->pairable_timeout == timeout && timeout == 0)
> return dbus_message_new_method_return(msg);
>
> if (adapter->pairable)
> adapter_set_pairable_timeout(adapter, timeout);
>
> adapter->pairable_timeout = timeout;
>
> write_pairable_timeout(&adapter->bdaddr, timeout);
>
> path = dbus_message_get_path(msg);
>
> emit_property_changed(conn, path,
> ADAPTER_INTERFACE, "PairableTimeout",
> DBUS_TYPE_UINT32, &timeout);
>
> return dbus_message_new_method_return(msg);
> }
>
> void btd_adapter_class_changed(struct btd_adapter *adapter, uint32_t new_class)
> {
> uint8_t class[3];
>
> class[2] = (new_class >> 16) & 0xff;
> class[1] = (new_class >> 8) & 0xff;
> class[0] = new_class & 0xff;
>
> write_local_class(&adapter->bdaddr, class);
>
> adapter->dev_class = new_class;
>
> if (main_opts.attrib_server) {
> /* Removes service class */
> class[1] = class[1] & 0x1f;
> attrib_gap_set(GATT_CHARAC_APPEARANCE, class, 2);
> }
>
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Class",
> DBUS_TYPE_UINT32, &new_class);
> }
>
> void adapter_update_local_name(struct btd_adapter *adapter, const char *name)
> {
> if (strncmp(name, adapter->name, MAX_NAME_LENGTH) == 0)
> return;
>
> strncpy(adapter->name, name, MAX_NAME_LENGTH);
>
> if (main_opts.attrib_server)
> attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
> (const uint8_t *) adapter->name, strlen(adapter->name));
>
> if (!adapter->name_stored) {
> char *name_ptr = adapter->name;
>
> write_local_name(&adapter->bdaddr, adapter->name);
>
> if (connection)
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Name",
> DBUS_TYPE_STRING, &name_ptr);
> }
>
> adapter->name_stored = FALSE;
> }
>
> static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg,
> const char *name, void *data)
> {
> struct btd_adapter *adapter = data;
> char *name_ptr = adapter->name;
>
> if (!g_utf8_validate(name, -1, NULL)) {
> error("Name change failed: supplied name isn't valid UTF-8");
> return btd_error_invalid_args(msg);
> }
>
> if (strncmp(name, adapter->name, MAX_NAME_LENGTH) == 0)
> goto done;
>
> strncpy(adapter->name, name, MAX_NAME_LENGTH);
> write_local_name(&adapter->bdaddr, name);
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Name",
> DBUS_TYPE_STRING, &name_ptr);
>
> if (adapter->up) {
> int err = adapter_ops->set_name(adapter->dev_id, name);
> if (err < 0)
> return btd_error_failed(msg, strerror(-err));
>
> adapter->name_stored = TRUE;
> }
>
> done:
> return dbus_message_new_method_return(msg);
> }
>
> struct btd_device *adapter_find_device(struct btd_adapter *adapter,
> const char *dest)
> {
> struct btd_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;
> }
>
> static void adapter_update_devices(struct btd_adapter *adapter)
> {
> char **devices;
> int i;
> GSList *l;
>
> /* Devices */
> devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
> for (i = 0, l = adapter->devices; l; l = l->next, i++) {
> struct btd_device *dev = l->data;
> devices[i] = (char *) device_get_path(dev);
> }
>
> emit_array_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Devices",
> DBUS_TYPE_OBJECT_PATH, &devices, i);
> g_free(devices);
> }
>
> static void adapter_emit_uuids_updated(struct btd_adapter *adapter)
> {
> char **uuids;
> int i;
> sdp_list_t *list;
>
> if (!adapter->initialized)
> return;
>
> uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
>
> for (i = 0, list = adapter->services; list; list = list->next) {
> char *uuid;
> sdp_record_t *rec = list->data;
>
> uuid = bt_uuid2string(&rec->svclass);
> if (uuid)
> uuids[i++] = uuid;
> }
>
> emit_array_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
>
> g_strfreev(uuids);
> }
>
> static uint8_t get_uuid_mask(uuid_t *uuid)
> {
> if (uuid->type != SDP_UUID16)
> return 0;
>
> switch (uuid->value.uuid16) {
> case DIALUP_NET_SVCLASS_ID:
> case CIP_SVCLASS_ID:
> return 0x42; /* Telephony & Networking */
> 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:
> return 0x10; /* Object Transfer */
> case HEADSET_SVCLASS_ID:
> case HANDSFREE_SVCLASS_ID:
> return 0x20; /* Audio */
> case CORDLESS_TELEPHONY_SVCLASS_ID:
> case INTERCOM_SVCLASS_ID:
> case FAX_SVCLASS_ID:
> case SAP_SVCLASS_ID:
> /*
> * Setting the telephony bit for the handsfree audio gateway
> * role is not required by the HFP specification, but the
> * Nokia 616 carkit is just plain broken! It will refuse
> * pairing without this bit set.
> */
> case HANDSFREE_AGW_SVCLASS_ID:
> return 0x40; /* Telephony */
> case AUDIO_SOURCE_SVCLASS_ID:
> case VIDEO_SOURCE_SVCLASS_ID:
> return 0x08; /* Capturing */
> case AUDIO_SINK_SVCLASS_ID:
> case VIDEO_SINK_SVCLASS_ID:
> return 0x04; /* Rendering */
> case PANU_SVCLASS_ID:
> case NAP_SVCLASS_ID:
> case GN_SVCLASS_ID:
> return 0x02; /* Networking */
> default:
> return 0;
> }
> }
>
> static int uuid_cmp(const void *a, const void *b)
> {
> const sdp_record_t *rec = a;
> const uuid_t *uuid = b;
>
> return sdp_uuid_cmp(&rec->svclass, uuid);
> }
>
> void adapter_service_insert(struct btd_adapter *adapter, void *r)
> {
> sdp_record_t *rec = r;
> gboolean new_uuid;
>
> if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
> new_uuid = TRUE;
> else
> new_uuid = FALSE;
>
> adapter->services = sdp_list_insert_sorted(adapter->services, rec,
> record_sort);
>
> if (new_uuid) {
> uint8_t svc_hint = get_uuid_mask(&rec->svclass);
> adapter_ops->add_uuid(adapter->dev_id, &rec->svclass, svc_hint);
> }
>
> adapter_emit_uuids_updated(adapter);
> }
>
> void adapter_service_remove(struct btd_adapter *adapter, void *r)
> {
> sdp_record_t *rec = r;
>
> adapter->services = sdp_list_remove(adapter->services, rec);
>
> if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
> adapter_ops->remove_uuid(adapter->dev_id, &rec->svclass);
>
> adapter_emit_uuids_updated(adapter);
> }
>
> static struct btd_device *adapter_create_device(DBusConnection *conn,
> struct btd_adapter *adapter,
> const char *address,
> device_type_t type)
> {
> struct btd_device *device;
> const char *path;
>
> DBG("%s", address);
>
> device = device_create(conn, adapter, address, type);
> if (!device)
> return NULL;
>
> device_set_temporary(device, TRUE);
>
> adapter->devices = g_slist_append(adapter->devices, device);
>
> path = device_get_path(device);
> g_dbus_emit_signal(conn, adapter->path,
> ADAPTER_INTERFACE, "DeviceCreated",
> DBUS_TYPE_OBJECT_PATH, &path,
> DBUS_TYPE_INVALID);
>
> adapter_update_devices(adapter);
>
> return device;
> }
>
> void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
> struct btd_device *device,
> gboolean remove_storage)
> {
> const gchar *dev_path = device_get_path(device);
> struct agent *agent;
>
> adapter->devices = g_slist_remove(adapter->devices, device);
> adapter->connections = g_slist_remove(adapter->connections, device);
>
> adapter_update_devices(adapter);
>
> g_dbus_emit_signal(conn, adapter->path,
> ADAPTER_INTERFACE, "DeviceRemoved",
> DBUS_TYPE_OBJECT_PATH, &dev_path,
> DBUS_TYPE_INVALID);
>
> agent = device_get_agent(device);
>
> if (agent && device_is_authorizing(device))
> agent_cancel(agent);
>
> device_remove(device, remove_storage);
> }
>
> struct btd_device *adapter_get_device(DBusConnection *conn,
> struct btd_adapter *adapter,
> const gchar *address)
> {
> struct btd_device *device;
>
> DBG("%s", address);
>
> if (!adapter)
> return NULL;
>
> device = adapter_find_device(adapter, address);
> if (device)
> return device;
>
> return adapter_create_device(conn, adapter, address,
> DEVICE_TYPE_BREDR);
> }
>
> static int start_discovery(struct btd_adapter *adapter)
> {
> /* Do not start if suspended */
> if (adapter->state == STATE_SUSPENDED)
> return 0;
>
> /* Postpone discovery if still resolving names */
> if (adapter->state == STATE_RESOLVNAME)
> return -EINPROGRESS;
>
> pending_remote_name_cancel(adapter);
>
> return adapter_ops->start_discovery(adapter->dev_id);
> }
>
> static gboolean discovery_cb(gpointer user_data)
> {
> struct btd_adapter *adapter = user_data;
> int err;
>
> err = start_discovery(adapter);
> if (err == -EINPROGRESS)
> return TRUE;
> else if (err < 0)
> error("start_discovery: %s (%d)", strerror(-err), -err);
>
> return FALSE;
> }
>
> static DBusMessage *adapter_start_discovery(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct session_req *req;
> struct btd_adapter *adapter = data;
> const char *sender = dbus_message_get_sender(msg);
> int err;
>
> if (!adapter->up)
> return btd_error_not_ready(msg);
>
> req = find_session(adapter->disc_sessions, sender);
> if (req) {
> session_ref(req);
> return dbus_message_new_method_return(msg);
> }
>
> if (adapter->disc_sessions)
> goto done;
>
> g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
> g_slist_free(adapter->found_devices);
> adapter->found_devices = NULL;
>
> g_slist_free(adapter->oor_devices);
> adapter->oor_devices = NULL;
>
> err = start_discovery(adapter);
> if (err < 0 && err != -EINPROGRESS)
> return btd_error_failed(msg, strerror(-err));
>
> done:
> req = create_session(adapter, conn, msg, 0,
> session_owner_exit);
>
> adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
>
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *adapter_stop_discovery(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct session_req *req;
> const char *sender = dbus_message_get_sender(msg);
>
> if (!adapter->up)
> return btd_error_not_ready(msg);
>
> req = find_session(adapter->disc_sessions, sender);
> if (!req)
> return btd_error_failed(msg, "Invalid discovery session");
>
> session_unref(req);
> info("Stopping discovery");
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *get_properties(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> const char *property;
> DBusMessage *reply;
> DBusMessageIter iter;
> DBusMessageIter dict;
> char str[MAX_NAME_LENGTH + 1], srcaddr[18];
> gboolean value;
> char **devices, **uuids;
> int i;
> GSList *l;
> sdp_list_t *list;
>
> ba2str(&adapter->bdaddr, srcaddr);
>
> if (check_address(srcaddr) < 0)
> return btd_error_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);
>
> /* Address */
> property = srcaddr;
> dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &property);
>
> /* Name */
> memset(str, 0, sizeof(str));
> strncpy(str, (char *) adapter->name, MAX_NAME_LENGTH);
> property = str;
>
> dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property);
>
> /* Class */
> dict_append_entry(&dict, "Class",
> DBUS_TYPE_UINT32, &adapter->dev_class);
>
> /* Powered */
> value = (adapter->up && !adapter->off_requested) ? TRUE : FALSE;
> dict_append_entry(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
>
> /* Discoverable */
> value = adapter->scan_mode & SCAN_INQUIRY ? TRUE : FALSE;
> dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &value);
>
> /* Pairable */
> dict_append_entry(&dict, "Pairable", DBUS_TYPE_BOOLEAN,
> &adapter->pairable);
>
> /* DiscoverableTimeout */
> dict_append_entry(&dict, "DiscoverableTimeout",
> DBUS_TYPE_UINT32, &adapter->discov_timeout);
>
> /* PairableTimeout */
> dict_append_entry(&dict, "PairableTimeout",
> DBUS_TYPE_UINT32, &adapter->pairable_timeout);
>
>
> if (adapter->state == STATE_DISCOV)
> value = TRUE;
> else
> value = FALSE;
>
> /* Discovering */
> dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN, &value);
>
> /* Devices */
> devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
> for (i = 0, l = adapter->devices; l; l = l->next, i++) {
> struct btd_device *dev = l->data;
> devices[i] = (char *) device_get_path(dev);
> }
> dict_append_array(&dict, "Devices", DBUS_TYPE_OBJECT_PATH,
> &devices, i);
> g_free(devices);
>
> /* UUIDs */
> uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
>
> for (i = 0, list = adapter->services; list; list = list->next) {
> sdp_record_t *rec = list->data;
> char *uuid;
>
> uuid = bt_uuid2string(&rec->svclass);
> if (uuid)
> uuids[i++] = uuid;
> }
>
> dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
>
> g_strfreev(uuids);
>
> dbus_message_iter_close_container(&iter, &dict);
>
> return reply;
> }
>
> static DBusMessage *set_property(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> DBusMessageIter iter;
> DBusMessageIter sub;
> const char *property;
> char srcaddr[18];
>
> ba2str(&adapter->bdaddr, srcaddr);
>
> if (!dbus_message_iter_init(msg, &iter))
> return btd_error_invalid_args(msg);
>
> if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> return btd_error_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 btd_error_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 btd_error_invalid_args(msg);
> dbus_message_iter_get_basic(&sub, &name);
>
> return set_name(conn, msg, name, data);
> } else if (g_str_equal("Powered", property)) {
> gboolean powered;
>
> if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
> return btd_error_invalid_args(msg);
>
> dbus_message_iter_get_basic(&sub, &powered);
>
> return set_powered(conn, msg, powered, data);
> } else if (g_str_equal("Discoverable", property)) {
> gboolean discoverable;
>
> if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
> return btd_error_invalid_args(msg);
>
> dbus_message_iter_get_basic(&sub, &discoverable);
>
> return set_discoverable(conn, msg, discoverable, data);
> } else if (g_str_equal("DiscoverableTimeout", property)) {
> uint32_t timeout;
>
> if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
> return btd_error_invalid_args(msg);
>
> dbus_message_iter_get_basic(&sub, &timeout);
>
> return set_discoverable_timeout(conn, msg, timeout, data);
> } else if (g_str_equal("Pairable", property)) {
> gboolean pairable;
>
> if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
> return btd_error_invalid_args(msg);
>
> dbus_message_iter_get_basic(&sub, &pairable);
>
> return set_pairable(conn, msg, pairable, data);
> } else if (g_str_equal("PairableTimeout", property)) {
> uint32_t timeout;
>
> if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
> return btd_error_invalid_args(msg);
>
> dbus_message_iter_get_basic(&sub, &timeout);
>
> return set_pairable_timeout(conn, msg, timeout, data);
> }
>
> return btd_error_invalid_args(msg);
> }
>
> static DBusMessage *request_session(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct session_req *req;
> const char *sender = dbus_message_get_sender(msg);
> uint8_t new_mode;
> int err;
>
> if (!adapter->agent)
> return btd_error_agent_not_available(msg);
>
> if (!adapter->mode_sessions)
> adapter->global_mode = adapter->mode;
>
> new_mode = get_mode(&adapter->bdaddr, "on");
>
> req = find_session(adapter->mode_sessions, sender);
> if (req) {
> session_ref(req);
> return dbus_message_new_method_return(msg);
> } else {
> req = create_session(adapter, conn, msg, new_mode,
> session_owner_exit);
> adapter->mode_sessions = g_slist_append(adapter->mode_sessions,
> req);
> }
>
> /* No need to change mode */
> if (adapter->mode >= new_mode)
> return dbus_message_new_method_return(msg);
>
> err = agent_confirm_mode_change(adapter->agent, mode2str(new_mode),
> confirm_mode_cb, req, NULL);
> if (err < 0) {
> session_unref(req);
> return btd_error_failed(msg, strerror(-err));
> }
>
> return NULL;
> }
>
> static DBusMessage *release_session(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct session_req *req;
> const char *sender = dbus_message_get_sender(msg);
>
> req = find_session(adapter->mode_sessions, sender);
> if (!req)
> return btd_error_failed(msg, "Invalid Session");
>
> session_unref(req);
>
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *list_devices(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> DBusMessage *reply;
> GSList *l;
> DBusMessageIter iter;
> DBusMessageIter array_iter;
> const gchar *dev_path;
>
> if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
> return btd_error_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 btd_device *device = l->data;
>
> dev_path = device_get_path(device);
>
> dbus_message_iter_append_basic(&array_iter,
> DBUS_TYPE_OBJECT_PATH, &dev_path);
> }
>
> dbus_message_iter_close_container(&iter, &array_iter);
>
> return reply;
> }
>
> static DBusMessage *cancel_device_creation(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> const gchar *address, *sender = dbus_message_get_sender(msg);
> struct btd_device *device;
>
> if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
> DBUS_TYPE_INVALID) == FALSE)
> return btd_error_invalid_args(msg);
>
> if (check_address(address) < 0)
> return btd_error_invalid_args(msg);
>
> device = adapter_find_device(adapter, address);
> if (!device || !device_is_creating(device, NULL))
> return btd_error_does_not_exist(msg);
>
> if (!device_is_creating(device, sender))
> return btd_error_not_authorized(msg);
>
> device_set_temporary(device, TRUE);
>
> if (device_is_connected(device)) {
> device_request_disconnect(device, msg);
> return NULL;
> }
>
> adapter_remove_device(conn, adapter, device, TRUE);
>
> return dbus_message_new_method_return(msg);
> }
>
> static struct btd_device *create_device_internal(DBusConnection *conn,
> struct btd_adapter *adapter,
> const gchar *address, int *err)
> {
> struct remote_dev_info *dev, match;
> struct btd_device *device;
> device_type_t type;
>
> memset(&match, 0, sizeof(struct remote_dev_info));
> str2ba(address, &match.bdaddr);
> match.name_status = NAME_ANY;
>
> dev = adapter_search_found_devices(adapter, &match);
> if (dev && dev->le)
> type = DEVICE_TYPE_LE;
> else
> type = DEVICE_TYPE_BREDR;
>
> device = adapter_create_device(conn, adapter, address, type);
> if (!device && err)
> *err = -ENOMEM;
>
> return device;
> }
>
> static DBusMessage *create_device(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct btd_device *device;
> const gchar *address;
> DBusMessage *reply;
> int err;
>
> if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
> DBUS_TYPE_INVALID) == FALSE)
> return btd_error_invalid_args(msg);
>
> if (check_address(address) < 0)
> return btd_error_invalid_args(msg);
>
> if (!adapter->up)
> return btd_error_not_ready(msg);
>
> if (adapter_find_device(adapter, address))
> return btd_error_already_exists(msg);
>
> DBG("%s", address);
>
> device = create_device_internal(conn, adapter, address, &err);
> if (!device)
> goto failed;
>
> if (device_get_type(device) != DEVICE_TYPE_LE)
> err = device_browse_sdp(device, conn, msg, NULL, FALSE);
> else
> err = device_browse_primary(device, conn, msg, FALSE);
>
> if (err < 0) {
> adapter_remove_device(conn, adapter, device, TRUE);
> return btd_error_failed(msg, strerror(-err));
> }
>
> return NULL;
>
> failed:
> if (err == -ENOTCONN) {
> /* Device is not connectable */
> const char *path = device_get_path(device);
>
> reply = dbus_message_new_method_return(msg);
>
> dbus_message_append_args(reply,
> DBUS_TYPE_OBJECT_PATH, &path,
> DBUS_TYPE_INVALID);
> } else
> reply = btd_error_failed(msg, strerror(-err));
>
> return reply;
> }
>
> 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, "NoInputNoOutput"))
> return IO_CAPABILITY_NOINPUTNOOUTPUT;
> return IO_CAPABILITY_INVALID;
> }
>
> static DBusMessage *create_paired_device(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct btd_device *device;
> const gchar *address, *agent_path, *capability, *sender;
> uint8_t cap;
> int err;
>
> 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 btd_error_invalid_args(msg);
>
> if (check_address(address) < 0)
> return btd_error_invalid_args(msg);
>
> if (!adapter->up)
> return btd_error_not_ready(msg);
>
> sender = dbus_message_get_sender(msg);
> if (adapter->agent &&
> agent_matches(adapter->agent, sender, agent_path)) {
> error("Refusing adapter agent usage as device specific one");
> return btd_error_invalid_args(msg);
> }
>
> cap = parse_io_capability(capability);
> if (cap == IO_CAPABILITY_INVALID)
> return btd_error_invalid_args(msg);
>
> device = adapter_find_device(adapter, address);
> if (!device) {
> device = create_device_internal(conn, adapter, address, &err);
> if (!device)
> return btd_error_failed(msg, strerror(-err));
> }
>
> if (device_get_type(device) != DEVICE_TYPE_LE)
> return device_create_bonding(device, conn, msg,
> agent_path, cap);
>
> err = device_browse_primary(device, conn, msg, TRUE);
> if (err < 0)
> return btd_error_failed(msg, strerror(-err));
>
> return NULL;
> }
>
> static gint device_path_cmp(struct btd_device *device, const gchar *path)
> {
> const gchar *dev_path = device_get_path(device);
>
> return strcasecmp(dev_path, path);
> }
>
> static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg,
> void *data)
> {
> struct btd_adapter *adapter = data;
> struct btd_device *device;
> const char *path;
> GSList *l;
>
> if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
> DBUS_TYPE_INVALID) == FALSE)
> return btd_error_invalid_args(msg);
>
> l = g_slist_find_custom(adapter->devices,
> path, (GCompareFunc) device_path_cmp);
> if (!l)
> return btd_error_does_not_exist(msg);
>
> device = l->data;
>
> if (device_is_temporary(device) || device_is_busy(device))
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".DoesNotExist",
> "Device creation in progress");
>
> device_set_temporary(device, TRUE);
>
> if (!device_is_connected(device)) {
> adapter_remove_device(conn, adapter, device, TRUE);
> return dbus_message_new_method_return(msg);
> }
>
> device_request_disconnect(device, msg);
> return NULL;
> }
>
> static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
> void *data)
> {
> struct btd_adapter *adapter = data;
> struct btd_device *device;
> DBusMessage *reply;
> const gchar *address;
> GSList *l;
> const gchar *dev_path;
>
> if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
> DBUS_TYPE_INVALID))
> return btd_error_invalid_args(msg);
>
> l = g_slist_find_custom(adapter->devices,
> address, (GCompareFunc) device_address_cmp);
> if (!l)
> return btd_error_does_not_exist(msg);
>
> device = l->data;
>
> reply = dbus_message_new_method_return(msg);
> if (!reply)
> return NULL;
>
> dev_path = device_get_path(device);
>
> dbus_message_append_args(reply,
> DBUS_TYPE_OBJECT_PATH, &dev_path,
> DBUS_TYPE_INVALID);
>
> return reply;
> }
>
> static void agent_removed(struct agent *agent, struct btd_adapter *adapter)
> {
> adapter_ops->set_io_capability(adapter->dev_id,
> IO_CAPABILITY_NOINPUTNOOUTPUT);
>
> adapter->agent = NULL;
> }
>
> static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
> void *data)
> {
> const char *path, *name, *capability;
> struct agent *agent;
> struct btd_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 btd_error_already_exists(msg);
>
> cap = parse_io_capability(capability);
> if (cap == IO_CAPABILITY_INVALID)
> return btd_error_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 btd_error_failed(msg, "Failed to create a new agent");
>
> adapter->agent = agent;
>
> DBG("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
> path);
>
> adapter_ops->set_io_capability(adapter->dev_id, cap);
>
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
> void *data)
> {
> const char *path, *name;
> struct btd_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 btd_error_does_not_exist(msg);
>
> agent_free(adapter->agent);
> adapter->agent = NULL;
>
> return dbus_message_new_method_return(msg);
> }
>
> static sdp_record_t *create_rfcomm_record(struct btd_adapter *adapter,
> const char *name, uuid_t uuid, uint8_t channel)
> {
> uuid_t root_uuid, l2cap_uuid, rfcomm_uuid;
> sdp_list_t *svclass, *root, *proto;
> sdp_record_t *record;
>
> record = sdp_record_alloc();
> if (!record)
> return NULL;
>
> 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));
>
> svclass = sdp_list_append(NULL, &uuid);
> sdp_set_service_classes(record, svclass);
>
> sdp_set_info_attr(record, name, NULL, NULL);
>
> return record;
> }
>
> static DBusMessage *add_rfcomm_service_record(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> uuid_t uuid;
> const char *name;
> uint8_t channel;
> uint32_t *uuid_p;
> uint32_t uuid_net[4]; // network order
> union {
> uint64_t u64[2]; // host
> uint32_t u32[4];
> } uuid_host;
> sdp_record_t *record;
> struct btd_adapter *adapter = data;
>
> DBusMessage *reply;
>
> if (!dbus_message_get_args(msg, NULL,
> DBUS_TYPE_STRING, &name,
> DBUS_TYPE_UINT64, &uuid_host.u64[0],
> DBUS_TYPE_UINT64, &uuid_host.u64[1],
> DBUS_TYPE_UINT16, &channel,
> DBUS_TYPE_INVALID))
> return btd_error_invalid_args(msg);
>
> uuid_p = uuid_host.u32;
> uuid_net[1] = htonl(*uuid_p++);
> uuid_net[0] = htonl(*uuid_p++);
> uuid_net[3] = htonl(*uuid_p++);
> uuid_net[2] = htonl(*uuid_p++);
>
> sdp_uuid128_create(&uuid, (void *)uuid_net);
>
> record = create_rfcomm_record(adapter, name, uuid, channel);
>
> if (!record)
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".Failed",
> "Failed to create sdp record");
>
> if (add_record_to_server(&adapter->bdaddr, record))
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".Failed",
> "Failed to register sdp record");
>
> reply = dbus_message_new_method_return(msg);
> dbus_message_append_args(reply,
> DBUS_TYPE_UINT32, &record->handle,
> DBUS_TYPE_INVALID);
>
> return reply;
> }
>
> static DBusMessage *remove_service_record(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> dbus_uint32_t handle;
>
> if (!dbus_message_get_args(msg, NULL,
> DBUS_TYPE_UINT32, &handle,
> DBUS_TYPE_INVALID))
> return btd_error_invalid_args(msg);
>
> if (remove_record_from_server(handle))
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".Failed",
> "Failed to remove sdp record");
>
> return dbus_message_new_method_return(msg);
> }
>
> static int add_headset_ag_record(struct btd_adapter* adapter)
> {
> 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 = 11;
> sdp_data_t *channel;
> uint8_t netid = 0x01; // ???? profile document
> sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
> int ret = 0;
>
> record = sdp_record_alloc();
> if (!record) return -1;
>
> 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]);
>
> aproto = sdp_list_append(0, apseq);
> sdp_set_access_protos(record, aproto);
>
> sdp_set_info_attr(record, "Voice Gateway", 0, 0);
>
> if (add_record_to_server(&adapter->bdaddr, record) < 0)
> ret = -1;
>
> 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);
>
> if (!ret)
> return record->handle;
> return ret;
> }
>
> static int add_handsfree_ag_record(struct btd_adapter* adapter) {
> 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 = 10;
> uint16_t u16 = 0x17;
> #ifdef ANDROID
> u16 = 0x07;
> #endif
> sdp_data_t *channel, *features;
> uint8_t netid = 0x01; // ???? profile document
> sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
> int ret = 0;
>
> record = sdp_record_alloc();
> if (!record) return -1;
>
> 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 (add_record_to_server(&adapter->bdaddr, record) < 0)
> ret = -1;
>
> 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);
>
> if (!ret)
> return record->handle;
> return ret;
> }
>
> static int add_pbap_pse_record(struct btd_adapter *adapter)
> {
> sdp_list_t *svclass_id, *pfseq, *apseq, *root;
> uuid_t root_uuid, pbap_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 = 19;
> sdp_data_t *channel;
> uint8_t formats[] = {0x01};
> uint8_t dtd = SDP_UINT8;
> sdp_data_t *sflist;
> int ret = 0;
>
> record = sdp_record_alloc();
> if (!record) return -1;
>
> sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
> root = sdp_list_append(0, &root_uuid);
> sdp_set_browse_groups(record, root);
>
> sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
> svclass_id = sdp_list_append(0, &pbap_uuid);
> sdp_set_service_classes(record, svclass_id);
>
> sdp_uuid16_create(&profile[0].uuid, PBAP_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, &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);
>
> sflist = sdp_data_alloc(dtd,formats);
> sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
>
> sdp_set_info_attr(record, "OBEX Phonebook Access Server", 0, 0);
>
> if (add_record_to_server(&adapter->bdaddr, record) < 0)
> ret = -1;
>
> 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);
>
> if (!ret)
> return record->handle;
> return ret;
> }
>
> static int add_opush_record(struct btd_adapter *adapter)
> {
> 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 u8 = 12;
> sdp_data_t *channel;
> #ifdef ANDROID
> uint8_t formats[] = { 0x01, 0x02, 0xff };
> #else
> uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
> #endif
> void *dtds[sizeof(formats)], *values[sizeof(formats)];
> unsigned int i;
> uint8_t dtd = SDP_UINT8;
> sdp_data_t *sflist;
> int ret = 0;
>
> record = sdp_record_alloc();
> if (!record) return -1;
>
> 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, &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);
>
> 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 (add_record_to_server(&adapter->bdaddr, record) < 0)
> ret = -1;
>
> 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);
>
> if (!ret)
> return record->handle;
> return ret;
> }
>
> static DBusMessage *add_reserved_service_records(DBusConnection *conn,
> DBusMessage *msg, void *data) {
> DBusMessage *reply;
> struct btd_adapter *adapter = data;
> uint32_t *svc_classes;
> uint32_t *handles;
> uint32_t len, i;
> int ret;
>
> if (dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
> &svc_classes, &len, DBUS_TYPE_INVALID) == FALSE)
> return btd_error_invalid_args(msg);
>
> handles = g_malloc0(sizeof(uint32_t) * len);
>
> for (i = 0; i < len; i++) {
> switch (svc_classes[i]) {
> case PBAP_PSE_SVCLASS_ID:
> ret = add_pbap_pse_record(adapter);
> break;
> case HEADSET_AGW_SVCLASS_ID:
> ret = add_headset_ag_record(adapter);
> break;
> case HANDSFREE_AGW_SVCLASS_ID:
> ret = add_handsfree_ag_record(adapter);
> break;
> case OBEX_OBJPUSH_SVCLASS_ID:
> ret = add_opush_record(adapter);
> break;
> }
> if (ret < 0) {
> g_free(handles);
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".Failed", "Failed to add sdp record");
> } else
> handles[i] = ret;
> }
>
> reply = dbus_message_new_method_return(msg);
> dbus_message_append_args(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
> &handles, len, DBUS_TYPE_INVALID);
>
> g_free(handles);
> return reply;
> }
>
> static DBusMessage *remove_reserved_service_records(DBusConnection *conn,
> DBusMessage *msg, void *data) {
> uint32_t *handles;
> uint32_t len, i;
>
> if (dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
> &handles, &len, DBUS_TYPE_INVALID) == FALSE)
> return btd_error_invalid_args(msg);
>
> for (i = 0; i < len; i++)
> if (remove_record_from_server(handles[i]))
> return g_dbus_create_error(msg,
> ERROR_INTERFACE ".Failed", "Failed to remove sdp record");
>
> return dbus_message_new_method_return(msg);
> }
>
> static DBusMessage *set_link_timeout(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> struct btd_adapter *adapter = data;
> struct btd_device *device;
> const char *path;
> GSList *l;
> uint32_t num_slots;
> int dd, err;
> bdaddr_t bdaddr;
>
> if (!dbus_message_get_args(msg, NULL,
> DBUS_TYPE_OBJECT_PATH, &path,
> DBUS_TYPE_UINT32, &num_slots,
> DBUS_TYPE_INVALID))
> return btd_error_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_get_address(l->data, &bdaddr);
>
> err = adapter_ops->set_link_timeout(adapter->dev_id, &bdaddr,
> num_slots);
> if (err < 0)
> return btd_error_failed(msg, strerror(-err));
>
> return dbus_message_new_method_return(msg);
> }
>
> static GDBusMethodTable adapter_methods[] = {
> { "GetProperties", "", "a{sv}",get_properties },
> { "SetProperty", "sv", "", set_property,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "RequestSession", "", "", request_session,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "ReleaseSession", "", "", release_session },
> { "StartDiscovery", "", "", adapter_start_discovery },
> { "StopDiscovery", "", "", adapter_stop_discovery,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "ListDevices", "", "ao", list_devices,
> G_DBUS_METHOD_FLAG_DEPRECATED},
> { "CreateDevice", "s", "o", create_device,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "CreatePairedDevice", "sos", "o", create_paired_device,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "CancelDeviceCreation","s", "", cancel_device_creation,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "RemoveDevice", "o", "", remove_device,
> G_DBUS_METHOD_FLAG_ASYNC},
> { "FindDevice", "s", "o", find_device },
> { "RegisterAgent", "os", "", register_agent },
> { "UnregisterAgent", "o", "", unregister_agent },
> { "AddRfcommServiceRecord", "sttq", "u", add_rfcomm_service_record },
> { "RemoveServiceRecord", "u", "", remove_service_record },
> { "SetLinkTimeout", "ou", "", set_link_timeout },
> { "AddReservedServiceRecords", "au", "au", add_reserved_service_records },
> { "RemoveReservedServiceRecords", "au", "", remove_reserved_service_records },
> { }
> };
>
> static GDBusSignalTable adapter_signals[] = {
> { "PropertyChanged", "sv" },
> { "DeviceCreated", "o" },
> { "DeviceRemoved", "o" },
> { "DeviceFound", "sa{sv}" },
> { "DeviceDisappeared", "s" },
> { }
> };
>
> static void create_stored_device_from_profiles(char *key, char *value,
> void *user_data)
> {
> struct btd_adapter *adapter = user_data;
> GSList *list, *uuids = bt_string2list(value);
> struct btd_device *device;
>
> if (g_slist_find_custom(adapter->devices,
> key, (GCompareFunc) device_address_cmp))
> return;
>
> device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
> if (!device)
> return;
>
> device_set_temporary(device, FALSE);
> adapter->devices = g_slist_append(adapter->devices, device);
>
> device_probe_drivers(device, uuids);
> list = device_services_from_record(device, uuids);
> if (list)
> device_register_services(connection, device, list, ATT_PSM);
>
> g_slist_foreach(uuids, (GFunc) g_free, NULL);
> g_slist_free(uuids);
> }
>
> struct adapter_keys {
> struct btd_adapter *adapter;
> GSList *keys;
> };
>
> static struct link_key_info *get_key_info(const char *addr, const char *value)
> {
> struct link_key_info *info;
> char tmp[3];
> long int l;
> int i;
>
> if (strlen(value) < 36) {
> error("Unexpectedly short (%zu) link key line", strlen(value));
> return NULL;
> }
>
> info = g_new0(struct link_key_info, 1);
>
> str2ba(addr, &info->bdaddr);
>
> memset(tmp, 0, sizeof(tmp));
>
> for (i = 0; i < 16; i++) {
> memcpy(tmp, value + (i * 2), 2);
> info->key[i] = (uint8_t) strtol(tmp, NULL, 16);
> }
>
> memcpy(tmp, value + 33, 2);
> info->type = (uint8_t) strtol(tmp, NULL, 10);
>
> memcpy(tmp, value + 35, 2);
> l = strtol(tmp, NULL, 10);
> if (l < 0)
> l = 0;
> info->pin_len = l;
>
> return info;
> }
>
> static void create_stored_device_from_linkkeys(char *key, char *value,
> void *user_data)
> {
> struct adapter_keys *keys = user_data;
> struct btd_adapter *adapter = keys->adapter;
> struct btd_device *device;
> struct link_key_info *info;
>
> info = get_key_info(key, value);
> if (info)
> keys->keys = g_slist_append(keys->keys, info);
>
> if (g_slist_find_custom(adapter->devices, key,
> (GCompareFunc) device_address_cmp))
> return;
>
> device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
> if (device) {
> device_set_temporary(device, FALSE);
> adapter->devices = g_slist_append(adapter->devices, device);
> }
> }
>
> static void create_stored_device_from_blocked(char *key, char *value,
> void *user_data)
> {
> struct btd_adapter *adapter = user_data;
> struct btd_device *device;
>
> if (g_slist_find_custom(adapter->devices,
> key, (GCompareFunc) device_address_cmp))
> return;
>
> device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
> if (device) {
> device_set_temporary(device, FALSE);
> adapter->devices = g_slist_append(adapter->devices, device);
> }
> }
>
> static void create_stored_device_from_types(char *key, char *value,
> void *user_data)
> {
> GSList *l;
> struct btd_adapter *adapter = user_data;
> struct btd_device *device;
> uint8_t type;
>
> type = strtol(value, NULL, 16);
>
> l = g_slist_find_custom(adapter->devices,
> key, (GCompareFunc) device_address_cmp);
> if (l) {
> device = l->data;
> device_set_type(device, type);
> return;
> }
>
> device = device_create(connection, adapter, key, type);
> if (device) {
> device_set_temporary(device, FALSE);
> adapter->devices = g_slist_append(adapter->devices, device);
> }
> }
>
> static GSList *string_to_primary_list(char *str)
> {
> GSList *l = NULL;
> char **services;
> int i;
>
> if (str == NULL)
> return NULL;
>
> services = g_strsplit(str, " ", 0);
> if (services == NULL)
> return NULL;
>
> for (i = 0; services[i]; i++) {
> struct att_primary *prim;
> int ret;
>
> prim = g_new0(struct att_primary, 1);
>
> ret = sscanf(services[i], "%04hX#%04hX#%s", &prim->start,
> &prim->end, prim->uuid);
>
> if (ret < 3) {
> g_free(prim);
> continue;
> }
>
> l = g_slist_append(l, prim);
> }
>
> g_strfreev(services);
>
> return l;
> }
>
> static void create_stored_device_from_primary(char *key, char *value,
> void *user_data)
> {
> struct btd_adapter *adapter = user_data;
> struct btd_device *device;
> GSList *services, *uuids, *l;
>
> if (g_slist_find_custom(adapter->devices,
> key, (GCompareFunc) device_address_cmp))
> return;
>
> device = device_create(connection, adapter, key, DEVICE_TYPE_LE);
> if (!device)
> return;
>
> device_set_temporary(device, FALSE);
> adapter->devices = g_slist_append(adapter->devices, device);
>
> services = string_to_primary_list(value);
> if (services == NULL)
> return;
>
> for (l = services, uuids = NULL; l; l = l->next) {
> struct att_primary *prim = l->data;
> uuids = g_slist_append(uuids, prim->uuid);
> }
>
> device_probe_drivers(device, uuids);
> device_register_services(connection, device, services, -1);
>
> g_slist_free(uuids);
> }
>
> static void load_devices(struct btd_adapter *adapter)
> {
> char filename[PATH_MAX + 1];
> char srcaddr[18];
> struct adapter_keys keys = { adapter, NULL };
> int err;
>
> ba2str(&adapter->bdaddr, srcaddr);
>
> create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "profiles");
> textfile_foreach(filename, create_stored_device_from_profiles,
> adapter);
>
> create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "primary");
> textfile_foreach(filename, create_stored_device_from_primary,
> adapter);
>
> create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
> textfile_foreach(filename, create_stored_device_from_linkkeys, &keys);
>
> err = adapter_ops->load_keys(adapter->dev_id, keys.keys,
> main_opts.debug_keys);
> if (err < 0) {
> error("Unable to load keys to adapter_ops: %s (%d)",
> strerror(-err), -err);
> g_slist_foreach(keys.keys, (GFunc) g_free, NULL);
> g_slist_free(keys.keys);
> }
>
> create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
> textfile_foreach(filename, create_stored_device_from_blocked, adapter);
>
> create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "types");
> textfile_foreach(filename, create_stored_device_from_types, adapter);
> }
>
> int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->block_device(adapter->dev_id, bdaddr);
> }
>
> int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->unblock_device(adapter->dev_id, bdaddr);
> }
>
> static void clear_blocked(struct btd_adapter *adapter)
> {
> int err;
>
> err = adapter_ops->unblock_device(adapter->dev_id, BDADDR_ANY);
> if (err < 0)
> error("Clearing blocked list failed: %s (%d)",
> strerror(-err), -err);
> }
>
> static void probe_driver(struct btd_adapter *adapter, gpointer user_data)
> {
> struct btd_adapter_driver *driver = user_data;
> int err;
>
> if (!adapter->up)
> return;
>
> if (driver->probe == NULL)
> return;
>
> err = driver->probe(adapter);
> if (err < 0) {
> error("%s: %s (%d)", driver->name, strerror(-err), -err);
> return;
> }
>
> adapter->loaded_drivers = g_slist_prepend(adapter->loaded_drivers,
> driver);
> }
>
> static void load_drivers(struct btd_adapter *adapter)
> {
> GSList *l;
>
> for (l = adapter_drivers; l; l = l->next)
> probe_driver(adapter, l->data);
> }
>
> static void load_connections(struct btd_adapter *adapter)
> {
> GSList *l, *conns;
> int err;
>
> err = adapter_ops->get_conn_list(adapter->dev_id, &conns);
> if (err < 0) {
> error("Unable to fetch existing connections: %s (%d)",
> strerror(-err), -err);
> return;
> }
>
> for (l = conns; l != NULL; l = g_slist_next(l)) {
> bdaddr_t *bdaddr = l->data;
> struct btd_device *device;
> char address[18];
>
> ba2str(bdaddr, address);
> DBG("Adding existing connection to %s", address);
>
> device = adapter_get_device(connection, adapter, address);
> if (device)
> adapter_add_connection(adapter, device);
> }
>
> g_slist_foreach(conns, (GFunc) g_free, NULL);
> g_slist_free(conns);
> }
>
> static int get_discoverable_timeout(const char *src)
> {
> int timeout;
>
> if (read_discoverable_timeout(src, &timeout) == 0)
> return timeout;
>
> return main_opts.discovto;
> }
>
> static int get_pairable_timeout(const char *src)
> {
> int timeout;
>
> if (read_pairable_timeout(src, &timeout) == 0)
> return timeout;
>
> return main_opts.pairto;
> }
>
> static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
> gboolean powered)
> {
> GSList *l;
>
> for (l = adapter->powered_callbacks; l; l = l->next) {
> btd_adapter_powered_cb cb = l->data;
>
> cb(adapter, powered);
> }
> }
>
> static void emit_device_disappeared(gpointer data, gpointer user_data)
> {
> struct remote_dev_info *dev = data;
> struct btd_adapter *adapter = user_data;
> char address[18];
> const char *paddr = address;
>
> ba2str(&dev->bdaddr, address);
>
> g_dbus_emit_signal(connection, adapter->path,
> ADAPTER_INTERFACE, "DeviceDisappeared",
> DBUS_TYPE_STRING, &paddr,
> DBUS_TYPE_INVALID);
>
> adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
> }
>
> static void update_oor_devices(struct btd_adapter *adapter)
> {
> g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
> g_slist_foreach(adapter->oor_devices, (GFunc) dev_info_free, NULL);
> g_slist_free(adapter->oor_devices);
> adapter->oor_devices = g_slist_copy(adapter->found_devices);
> }
>
> void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
> uint8_t *on_mode, gboolean *pairable)
> {
> char str[14], address[18];
>
> ba2str(&adapter->bdaddr, address);
>
> if (mode) {
> if (main_opts.remember_powered == FALSE)
> *mode = main_opts.mode;
> else if (read_device_mode(address, str, sizeof(str)) == 0)
> *mode = get_mode(&adapter->bdaddr, str);
> else
> *mode = main_opts.mode;
> }
>
> if (on_mode) {
> if (main_opts.remember_powered == FALSE)
> *on_mode = get_mode(&adapter->bdaddr, "on");
> else if (read_on_mode(address, str, sizeof(str)) == 0)
> *on_mode = get_mode(&adapter->bdaddr, str);
> else
> *on_mode = main_opts.mode;
> }
>
> if (pairable)
> *pairable = adapter->pairable;
> }
>
> void btd_adapter_start(struct btd_adapter *adapter)
> {
> char address[18];
> uint8_t cls[3];
> gboolean powered;
>
> ba2str(&adapter->bdaddr, address);
>
> adapter->dev_class = 0;
> adapter->off_requested = FALSE;
> adapter->up = TRUE;
> adapter->discov_timeout = get_discoverable_timeout(address);
> adapter->pairable_timeout = get_pairable_timeout(address);
> adapter->state = STATE_IDLE;
> adapter->mode = MODE_CONNECTABLE;
>
> if (main_opts.le)
> adapter_ops->enable_le(adapter->dev_id);
>
> adapter_ops->set_name(adapter->dev_id, adapter->name);
>
> if (read_local_class(&adapter->bdaddr, cls) < 0) {
> uint32_t class = htobl(main_opts.class);
> memcpy(cls, &class, 3);
> }
>
> btd_adapter_set_class(adapter, cls[1], cls[0]);
>
> powered = TRUE;
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Powered",
> DBUS_TYPE_BOOLEAN, &powered);
>
> call_adapter_powered_callbacks(adapter, TRUE);
>
> adapter_ops->disable_cod_cache(adapter->dev_id);
>
> info("Adapter %s has been enabled", adapter->path);
> }
>
> static void reply_pending_requests(struct btd_adapter *adapter)
> {
> GSList *l;
>
> if (!adapter)
> return;
>
> /* pending bonding */
> for (l = adapter->devices; l; l = l->next) {
> struct btd_device *device = l->data;
>
> if (device_is_bonding(device, NULL))
> device_cancel_bonding(device,
> HCI_OE_USER_ENDED_CONNECTION);
> }
> }
>
> static void remove_driver(gpointer data, gpointer user_data)
> {
> struct btd_adapter_driver *driver = data;
> struct btd_adapter *adapter = user_data;
>
> if (driver->remove)
> driver->remove(adapter);
> }
>
> static void unload_drivers(struct btd_adapter *adapter)
> {
> g_slist_foreach(adapter->loaded_drivers, remove_driver, adapter);
> g_slist_free(adapter->loaded_drivers);
> adapter->loaded_drivers = NULL;
> }
>
> static void set_mode_complete(struct btd_adapter *adapter)
> {
> struct session_req *pending;
> const char *modestr;
> int err;
>
> DBG("");
>
> /*
> * g_slist_free is not called after g_slist_foreach because the list is
> * updated using g_slist_remove in session_remove which is called by
> * session_free, which is called for each element by g_slist_foreach.
> */
> if (adapter->mode == MODE_OFF)
> g_slist_foreach(adapter->mode_sessions, (GFunc) session_free,
> NULL);
>
> if (adapter->pending_mode == NULL)
> return;
>
> pending = adapter->pending_mode;
> adapter->pending_mode = NULL;
>
> err = (pending->mode != adapter->mode) ? -EINVAL : 0;
>
> if (pending->msg != NULL) {
> DBusMessage *msg = pending->msg;
> DBusMessage *reply;
>
> if (err < 0)
> reply = btd_error_failed(msg, strerror(-err));
> else {
> if (strcmp(dbus_message_get_member(msg),
> "SetProperty") == 0)
> adapter->global_mode = adapter->mode;
> reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> }
>
> g_dbus_send_message(connection, reply);
> }
>
> modestr = mode2str(adapter->mode);
>
> DBG("%s", modestr);
>
> /* restore if the mode doesn't matches the pending */
> if (err != 0) {
> write_device_mode(&adapter->bdaddr, modestr);
> error("unable to set mode: %s", mode2str(pending->mode));
> }
>
> session_unref(pending);
> }
>
> int btd_adapter_stop(struct btd_adapter *adapter)
> {
> gboolean powered, discoverable, pairable;
>
> /* cancel pending timeout */
> if (adapter->discov_timeout_id) {
> g_source_remove(adapter->discov_timeout_id);
> adapter->discov_timeout_id = 0;
> }
>
> /* check pending requests */
> reply_pending_requests(adapter);
>
> stop_discovery(adapter);
>
> if (adapter->disc_sessions) {
> g_slist_foreach(adapter->disc_sessions, (GFunc) session_free,
> NULL);
> g_slist_free(adapter->disc_sessions);
> adapter->disc_sessions = NULL;
> }
>
> while (adapter->connections) {
> struct btd_device *device = adapter->connections->data;
> adapter_remove_connection(adapter, device);
> }
>
> if (adapter->scan_mode == (SCAN_PAGE | SCAN_INQUIRY)) {
> discoverable = FALSE;
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Discoverable",
> DBUS_TYPE_BOOLEAN, &discoverable);
> }
>
> if ((adapter->scan_mode & SCAN_PAGE) && adapter->pairable == TRUE) {
> pairable = FALSE;
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Pairable",
> DBUS_TYPE_BOOLEAN, &pairable);
> }
>
> powered = FALSE;
> emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE,
> "Powered", DBUS_TYPE_BOOLEAN, &powered);
>
> adapter->up = 0;
> adapter->scan_mode = SCAN_DISABLED;
> adapter->mode = MODE_OFF;
> adapter->state = STATE_IDLE;
> adapter->off_requested = FALSE;
> adapter->name_stored = FALSE;
>
> call_adapter_powered_callbacks(adapter, FALSE);
>
> info("Adapter %s has been disabled", adapter->path);
>
> set_mode_complete(adapter);
>
> return 0;
> }
>
> static void adapter_free(gpointer user_data)
> {
> struct btd_adapter *adapter = user_data;
>
> agent_free(adapter->agent);
> adapter->agent = NULL;
>
> DBG("%p", adapter);
>
> if (adapter->auth_idle_id)
> g_source_remove(adapter->auth_idle_id);
>
> sdp_list_free(adapter->services, NULL);
>
> g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
> g_slist_free(adapter->found_devices);
>
> g_slist_free(adapter->oor_devices);
>
> g_free(adapter->path);
> g_free(adapter);
> }
>
> struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
> {
> adapter->ref++;
>
> DBG("%p: ref=%d", adapter, adapter->ref);
>
> return adapter;
> }
>
> void btd_adapter_unref(struct btd_adapter *adapter)
> {
> gchar *path;
>
> adapter->ref--;
>
> DBG("%p: ref=%d", adapter, adapter->ref);
>
> if (adapter->ref > 0)
> return;
>
> path = g_strdup(adapter->path);
>
> g_dbus_unregister_interface(connection, path, ADAPTER_INTERFACE);
>
> g_free(path);
> }
>
> gboolean adapter_init(struct btd_adapter *adapter)
> {
> int err;
>
> /* adapter_ops makes sure that newly registered adapters always
> * start off as powered */
> adapter->up = TRUE;
>
> adapter_ops->read_bdaddr(adapter->dev_id, &adapter->bdaddr);
>
> if (bacmp(&adapter->bdaddr, BDADDR_ANY) == 0) {
> error("No address available for hci%d", adapter->dev_id);
> return FALSE;
> }
>
> err = adapter_ops->read_local_features(adapter->dev_id,
> adapter->features);
> if (err < 0) {
> error("Can't read features for hci%d: %s (%d)",
> adapter->dev_id, strerror(-err), -err);
> return FALSE;
> }
>
> if (read_local_name(&adapter->bdaddr, adapter->name) < 0)
> expand_name(adapter->name, MAX_NAME_LENGTH, main_opts.name,
> adapter->dev_id);
>
> if (main_opts.attrib_server)
> attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
> (const uint8_t *) adapter->name, strlen(adapter->name));
>
> sdp_init_services_list(&adapter->bdaddr);
> load_drivers(adapter);
> clear_blocked(adapter);
> load_devices(adapter);
>
> /* Set pairable mode */
> if (read_device_pairable(&adapter->bdaddr, &adapter->pairable) < 0)
> adapter->pairable = TRUE;
>
> /* retrieve the active connections: address the scenario where
> * the are active connections before the daemon've started */
> load_connections(adapter);
>
> adapter->initialized = TRUE;
>
> return TRUE;
> }
>
> struct btd_adapter *adapter_create(DBusConnection *conn, int id)
> {
> char path[MAX_PATH_LENGTH];
> struct btd_adapter *adapter;
> const char *base_path = manager_get_base_path();
>
> if (!connection)
> connection = conn;
>
> adapter = g_try_new0(struct btd_adapter, 1);
> if (!adapter) {
> error("adapter_create: failed to alloc memory for hci%d", id);
> return NULL;
> }
>
> adapter->dev_id = id;
>
> snprintf(path, sizeof(path), "%s/hci%d", base_path, id);
> adapter->path = g_strdup(path);
>
> if (!g_dbus_register_interface(conn, path, ADAPTER_INTERFACE,
> adapter_methods, adapter_signals, NULL,
> adapter, adapter_free)) {
> error("Adapter interface init failed on path %s", path);
> adapter_free(adapter);
> return NULL;
> }
>
> return btd_adapter_ref(adapter);
> }
>
> void adapter_remove(struct btd_adapter *adapter)
> {
> GSList *l;
>
> DBG("Removing adapter %s", adapter->path);
>
> for (l = adapter->devices; l; l = l->next)
> device_remove(l->data, FALSE);
> g_slist_free(adapter->devices);
>
> unload_drivers(adapter);
>
> /* Return adapter to down state if it was not up on init */
> adapter_ops->restore_powered(adapter->dev_id);
>
> btd_adapter_unref(adapter);
> }
>
> uint16_t adapter_get_dev_id(struct btd_adapter *adapter)
> {
> return adapter->dev_id;
> }
>
> const gchar *adapter_get_path(struct btd_adapter *adapter)
> {
> if (!adapter)
> return NULL;
>
> return adapter->path;
> }
>
> void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> bacpy(bdaddr, &adapter->bdaddr);
> }
>
> static inline void suspend_discovery(struct btd_adapter *adapter)
> {
> if (adapter->state != STATE_SUSPENDED)
> return;
>
> if (adapter->oor_devices) {
> g_slist_free(adapter->oor_devices);
> adapter->oor_devices = NULL;
> }
>
> if (adapter->scheduler_id) {
> g_source_remove(adapter->scheduler_id);
> adapter->scheduler_id = 0;
> }
>
> adapter_ops->stop_discovery(adapter->dev_id);
> }
>
> static inline void resolve_names(struct btd_adapter *adapter)
> {
> int err;
>
> if (adapter->state != STATE_RESOLVNAME)
> return;
>
> err = adapter_resolve_names(adapter);
> if (err < 0)
> adapter_set_state(adapter, STATE_IDLE);
> }
>
> void adapter_set_state(struct btd_adapter *adapter, int state)
> {
> const char *path = adapter->path;
> gboolean discov_active;
>
> if (adapter->state == state)
> return;
>
> adapter->state = state;
>
> DBG("hci%d: new state %d", adapter->dev_id, adapter->state);
>
> switch (adapter->state) {
> case STATE_IDLE:
> update_oor_devices(adapter);
>
> discov_active = FALSE;
> emit_property_changed(connection, path,
> ADAPTER_INTERFACE, "Discovering",
> DBUS_TYPE_BOOLEAN, &discov_active);
>
> if (adapter_has_discov_sessions(adapter)) {
> adapter->scheduler_id = g_timeout_add_seconds(
> main_opts.discov_interval,
> discovery_cb, adapter);
> }
> break;
> case STATE_DISCOV:
> discov_active = TRUE;
> emit_property_changed(connection, path,
> ADAPTER_INTERFACE, "Discovering",
> DBUS_TYPE_BOOLEAN, &discov_active);
> break;
> case STATE_RESOLVNAME:
> resolve_names(adapter);
> break;
> case STATE_SUSPENDED:
> suspend_discovery(adapter);
> break;
> }
> }
>
> int adapter_get_state(struct btd_adapter *adapter)
> {
> return adapter->state;
> }
>
> struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
> struct remote_dev_info *match)
> {
> GSList *l;
>
> l = g_slist_find_custom(adapter->found_devices, match,
> (GCompareFunc) found_device_cmp);
> if (l)
> return l->data;
>
> return NULL;
> }
>
> static 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;
> }
>
> static void append_dict_valist(DBusMessageIter *iter,
> const char *first_key,
> va_list var_args)
> {
> DBusMessageIter dict;
> const char *key;
> int type;
> int n_elements;
> 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 *);
> if (type == DBUS_TYPE_ARRAY) {
> n_elements = va_arg(var_args, int);
> if (n_elements > 0)
> dict_append_array(&dict, key, DBUS_TYPE_STRING,
> val, n_elements);
> } else
> dict_append_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);
>
> g_dbus_send_message(connection, signal);
> }
>
> static char **strlist2array(GSList *list)
> {
> unsigned int i, n;
> char **array;
>
> if (list == NULL)
> return NULL;
>
> n = g_slist_length(list);
> array = g_new0(char *, n + 1);
>
> for (i = 0; list; list = list->next, i++)
> array[i] = g_strdup((const gchar *) list->data);
>
> return array;
> }
>
> void adapter_emit_device_found(struct btd_adapter *adapter,
> struct remote_dev_info *dev)
> {
> struct btd_device *device;
> char peer_addr[18], local_addr[18];
> const char *icon, *paddr = peer_addr;
> dbus_bool_t paired = FALSE;
> dbus_int16_t rssi = dev->rssi;
> char *alias;
> size_t uuid_count;
>
> ba2str(&dev->bdaddr, peer_addr);
> ba2str(&adapter->bdaddr, local_addr);
>
> device = adapter_find_device(adapter, paddr);
> if (device)
> paired = device_is_paired(device);
>
> /* The uuids string array is updated only if necessary */
> uuid_count = g_slist_length(dev->services);
> if (dev->services && dev->uuid_count != uuid_count) {
> g_strfreev(dev->uuids);
> dev->uuids = strlist2array(dev->services);
> dev->uuid_count = uuid_count;
> }
>
> if (dev->le) {
> gboolean broadcaster;
>
> if (dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))
> broadcaster = FALSE;
> else
> broadcaster = TRUE;
>
> emit_device_found(adapter->path, paddr,
> "Address", DBUS_TYPE_STRING, &paddr,
> "RSSI", DBUS_TYPE_INT16, &rssi,
> "Name", DBUS_TYPE_STRING, &dev->name,
> "Paired", DBUS_TYPE_BOOLEAN, &paired,
> "Broadcaster", DBUS_TYPE_BOOLEAN, &broadcaster,
> "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
> NULL);
> return;
> }
>
> icon = class_to_icon(dev->class);
>
> if (!dev->alias) {
> if (!dev->name) {
> alias = g_strdup(peer_addr);
> g_strdelimit(alias, ":", '-');
> } else
> alias = g_strdup(dev->name);
> } else
> alias = g_strdup(dev->alias);
>
> emit_device_found(adapter->path, paddr,
> "Address", DBUS_TYPE_STRING, &paddr,
> "Class", DBUS_TYPE_UINT32, &dev->class,
> "Icon", DBUS_TYPE_STRING, &icon,
> "RSSI", DBUS_TYPE_INT16, &rssi,
> "Name", DBUS_TYPE_STRING, &dev->name,
> "Alias", DBUS_TYPE_STRING, &alias,
> "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
> "Paired", DBUS_TYPE_BOOLEAN, &paired,
> "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
> NULL);
>
> g_free(alias);
> }
>
> static struct remote_dev_info *found_device_new(const bdaddr_t *bdaddr,
> gboolean le, const char *name,
> const char *alias, uint32_t class,
> gboolean legacy, name_status_t status,
> int flags)
> {
> struct remote_dev_info *dev;
>
> dev = g_new0(struct remote_dev_info, 1);
> bacpy(&dev->bdaddr, bdaddr);
> dev->le = le;
> dev->name = g_strdup(name);
> dev->alias = g_strdup(alias);
> dev->class = class;
> dev->legacy = legacy;
> dev->name_status = status;
> if (flags >= 0)
> dev->flags = flags;
>
> return dev;
> }
>
> static void remove_same_uuid(gpointer data, gpointer user_data)
> {
> struct remote_dev_info *dev = user_data;
> GSList *l;
>
> for (l = dev->services; l; l = l->next) {
> char *current_uuid = l->data;
> char *new_uuid = data;
>
> if (strcmp(current_uuid, new_uuid) == 0) {
> g_free(current_uuid);
> dev->services = g_slist_delete_link(dev->services, l);
> break;
> }
> }
> }
>
> static void dev_prepend_uuid(gpointer data, gpointer user_data)
> {
> struct remote_dev_info *dev = user_data;
> char *new_uuid = data;
>
> dev->services = g_slist_prepend(dev->services, g_strdup(new_uuid));
> }
>
> static gboolean pairing_is_legacy(bdaddr_t *local, bdaddr_t *peer,
> const uint8_t *eir, const char *name)
> {
> unsigned char features[8];
>
> if (eir)
> return FALSE;
>
> if (name == NULL)
> return TRUE;
>
> if (read_remote_features(local, peer, NULL, features) < 0)
> return TRUE;
>
> if (features[0] & 0x01)
> return FALSE;
> else
> return TRUE;
> }
>
> static char *read_stored_data(bdaddr_t *local, bdaddr_t *peer, const char *file)
> {
> char local_addr[18], peer_addr[18], filename[PATH_MAX + 1];
>
> ba2str(local, local_addr);
> ba2str(peer, peer_addr);
>
> create_name(filename, PATH_MAX, STORAGEDIR, local_addr, file);
>
> return textfile_get(filename, peer_addr);
> }
>
> void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> uint32_t class, int8_t rssi,
> uint8_t *data)
> {
> struct remote_dev_info *dev, match;
> struct eir_data eir_data;
> char *alias, *name;
> gboolean legacy, le;
> name_status_t name_status;
> int err;
>
> memset(&eir_data, 0, sizeof(eir_data));
> err = eir_parse(&eir_data, data);
> if (err < 0) {
> error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
> return;
> }
>
> if (eir_data.name != NULL && eir_data.name_complete)
> write_device_name(&adapter->bdaddr, bdaddr, eir_data.name);
>
> /* Device already seen in the discovery session ? */
> memset(&match, 0, sizeof(struct remote_dev_info));
> bacpy(&match.bdaddr, bdaddr);
> match.name_status = NAME_ANY;
>
> dev = adapter_search_found_devices(adapter, &match);
> if (dev) {
> adapter->oor_devices = g_slist_remove(adapter->oor_devices,
> dev);
> if (dev->rssi != rssi)
> goto done;
>
> eir_data_free(&eir_data);
>
> return;
> }
>
> /* New device in the discovery session */
>
> name = read_stored_data(&adapter->bdaddr, bdaddr, "names");
>
> if (eir_data.flags < 0) {
> le = FALSE;
>
> legacy = pairing_is_legacy(&adapter->bdaddr, bdaddr, data,
> name);
>
> if (!name && main_opts.name_resolv &&
> adapter_has_discov_sessions(adapter))
> name_status = NAME_REQUIRED;
> else
> name_status = NAME_NOT_REQUIRED;
> } else {
> le = TRUE;
> legacy = FALSE;
> name_status = NAME_NOT_REQUIRED;
> }
>
> alias = read_stored_data(&adapter->bdaddr, bdaddr, "aliases");
>
> dev = found_device_new(bdaddr, le, name, alias, class, legacy,
> name_status, eir_data.flags);
> free(name);
> free(alias);
>
> adapter->found_devices = g_slist_prepend(adapter->found_devices, dev);
>
> done:
> dev->rssi = rssi;
>
> adapter->found_devices = g_slist_sort(adapter->found_devices,
> (GCompareFunc) dev_rssi_cmp);
>
> g_slist_foreach(eir_data.services, remove_same_uuid, dev);
> g_slist_foreach(eir_data.services, dev_prepend_uuid, dev);
>
> adapter_emit_device_found(adapter, dev);
>
> eir_data_free(&eir_data);
> }
>
> int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> struct remote_dev_info *dev, match;
>
> memset(&match, 0, sizeof(struct remote_dev_info));
> bacpy(&match.bdaddr, bdaddr);
>
> dev = adapter_search_found_devices(adapter, &match);
> if (!dev)
> return -1;
>
> dev->name_status = NAME_NOT_REQUIRED;
>
> return 0;
> }
>
> void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode)
> {
> const gchar *path = adapter_get_path(adapter);
> gboolean discoverable, pairable;
>
> DBG("old 0x%02x new 0x%02x", adapter->scan_mode, scan_mode);
>
> if (adapter->scan_mode == scan_mode)
> return;
>
> adapter_remove_discov_timeout(adapter);
>
> switch (scan_mode) {
> case SCAN_DISABLED:
> adapter->mode = MODE_OFF;
> discoverable = FALSE;
> pairable = FALSE;
> break;
> case SCAN_PAGE:
> adapter->mode = MODE_CONNECTABLE;
> discoverable = FALSE;
> pairable = adapter->pairable;
> break;
> case (SCAN_PAGE | SCAN_INQUIRY):
> adapter->mode = MODE_DISCOVERABLE;
> discoverable = TRUE;
> pairable = adapter->pairable;
> if (adapter->discov_timeout != 0)
> adapter_set_discov_timeout(adapter,
> adapter->discov_timeout);
> break;
> case SCAN_INQUIRY:
> /* Address the scenario where a low-level application like
> * hciconfig changed the scan mode */
> if (adapter->discov_timeout != 0)
> adapter_set_discov_timeout(adapter,
> adapter->discov_timeout);
>
> /* ignore, this event should not be sent */
> default:
> /* ignore, reserved */
> return;
> }
>
> /* If page scanning gets toggled emit the Pairable property */
> if ((adapter->scan_mode & SCAN_PAGE) != (scan_mode & SCAN_PAGE))
> emit_property_changed(connection, adapter->path,
> ADAPTER_INTERFACE, "Pairable",
> DBUS_TYPE_BOOLEAN, &pairable);
>
> if (!discoverable)
> adapter_set_limited_discoverable(adapter, FALSE);
>
> emit_property_changed(connection, path,
> ADAPTER_INTERFACE, "Discoverable",
> DBUS_TYPE_BOOLEAN, &discoverable);
>
> adapter->scan_mode = scan_mode;
>
> set_mode_complete(adapter);
> }
>
> struct agent *adapter_get_agent(struct btd_adapter *adapter)
> {
> if (!adapter)
> return NULL;
>
> return adapter->agent;
> }
>
> void adapter_add_connection(struct btd_adapter *adapter,
> struct btd_device *device)
> {
> if (g_slist_find(adapter->connections, device)) {
> error("Device is already marked as connected");
> return;
> }
>
> device_add_connection(device, connection);
>
> adapter->connections = g_slist_append(adapter->connections, device);
> }
>
> void adapter_remove_connection(struct btd_adapter *adapter,
> struct btd_device *device)
> {
> DBG("");
>
> if (!g_slist_find(adapter->connections, device)) {
> error("No matching connection for device");
> return;
> }
>
> device_remove_connection(device, connection);
>
> adapter->connections = g_slist_remove(adapter->connections, device);
>
> if (device_is_authenticating(device))
> device_cancel_authentication(device, TRUE);
>
> if (device_is_temporary(device)) {
> const char *path = device_get_path(device);
>
> DBG("Removing temporary device %s", path);
> adapter_remove_device(connection, adapter, device, TRUE);
> }
> }
>
> gboolean adapter_has_discov_sessions(struct btd_adapter *adapter)
> {
> if (!adapter || !adapter->disc_sessions)
> return FALSE;
>
> return TRUE;
> }
>
> void adapter_suspend_discovery(struct btd_adapter *adapter)
> {
> if (adapter->disc_sessions == NULL ||
> adapter->state == STATE_SUSPENDED)
> return;
>
> DBG("Suspending discovery");
>
> adapter_set_state(adapter, STATE_SUSPENDED);
> }
>
> void adapter_resume_discovery(struct btd_adapter *adapter)
> {
> DBG("Resuming discovery");
>
> adapter_set_state(adapter, STATE_IDLE);
> }
>
> int btd_register_adapter_driver(struct btd_adapter_driver *driver)
> {
> adapter_drivers = g_slist_append(adapter_drivers, driver);
>
> if (driver->probe == NULL)
> return 0;
>
> manager_foreach_adapter(probe_driver, driver);
>
> return 0;
> }
>
> static void unload_driver(struct btd_adapter *adapter, gpointer data)
> {
> adapter->loaded_drivers = g_slist_remove(adapter->loaded_drivers, data);
> }
>
> void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
> {
> adapter_drivers = g_slist_remove(adapter_drivers, driver);
>
> manager_foreach_adapter(unload_driver, driver);
> }
>
> static void agent_auth_cb(struct agent *agent, DBusError *derr,
> void *user_data)
> {
> struct service_auth *auth = user_data;
>
> device_set_authorizing(auth->device, FALSE);
>
> auth->cb(derr, auth->user_data);
> }
>
> static gboolean auth_idle_cb(gpointer user_data)
> {
> struct service_auth *auth = user_data;
> struct btd_adapter *adapter = auth->adapter;
>
> adapter->auth_idle_id = 0;
>
> auth->cb(NULL, auth->user_data);
>
> return FALSE;
> }
>
> static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
> const char *uuid, service_auth_cb cb,
> void *user_data)
> {
> struct service_auth *auth;
> struct btd_device *device;
> struct agent *agent;
> char address[18];
> const gchar *dev_path;
> int err;
>
> ba2str(dst, address);
> device = adapter_find_device(adapter, address);
> if (!device)
> return -EPERM;
>
> /* Device connected? */
> if (!g_slist_find(adapter->connections, device))
> return -ENOTCONN;
>
> if (adapter->auth_idle_id)
> return -EBUSY;
>
> auth = g_try_new0(struct service_auth, 1);
> if (!auth)
> return -ENOMEM;
>
> auth->cb = cb;
> auth->user_data = user_data;
> auth->device = device;
> auth->adapter = adapter;
>
> if (device_is_trusted(device) == TRUE) {
> adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
> auth_idle_cb, auth,
> g_free);
> return 0;
> }
>
> agent = device_get_agent(device);
> if (!agent) {
> g_free(auth);
> return -EPERM;
> }
>
> dev_path = device_get_path(device);
>
> err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, auth, g_free);
> if (err < 0)
> g_free(auth);
> else
> device_set_authorizing(device, TRUE);
>
> return err;
> }
>
> int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
> const char *uuid, service_auth_cb cb,
> void *user_data)
> {
> struct btd_adapter *adapter;
> GSList *l;
>
> if (bacmp(src, BDADDR_ANY) != 0) {
> adapter = manager_find_adapter(src);
> if (!adapter)
> return -EPERM;
>
> return adapter_authorize(adapter, dst, uuid, cb, user_data);
> }
>
> for (l = manager_get_adapters(); l != NULL; l = g_slist_next(l)) {
> int err;
>
> adapter = l->data;
>
> err = adapter_authorize(adapter, dst, uuid, cb, user_data);
> if (err == 0)
> return 0;
> }
>
> return -EPERM;
> }
>
> int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
> {
> struct btd_adapter *adapter = manager_find_adapter(src);
> struct btd_device *device;
> struct agent *agent;
> char address[18];
> int err;
>
> if (!adapter)
> return -EPERM;
>
> ba2str(dst, address);
> device = adapter_find_device(adapter, address);
> if (!device)
> return -EPERM;
>
> if (adapter->auth_idle_id) {
> g_source_remove(adapter->auth_idle_id);
> adapter->auth_idle_id = 0;
> return 0;
> }
>
> /*
> * FIXME: Cancel fails if authorization is requested to adapter's
> * agent and in the meanwhile CreatePairedDevice is called.
> */
>
> agent = device_get_agent(device);
> if (!agent)
> return -EPERM;
>
> err = agent_cancel(agent);
>
> if (err == 0)
> device_set_authorizing(device, FALSE);
>
> return err;
> }
>
> static gchar *adapter_any_path = NULL;
> static int adapter_any_refcount = 0;
>
> const char *adapter_any_get_path(void)
> {
> return adapter_any_path;
> }
>
> const char *btd_adapter_any_request_path(void)
> {
> if (adapter_any_refcount++ > 0)
> return adapter_any_path;
>
> adapter_any_path = g_strdup_printf("%s/any", manager_get_base_path());
>
> return adapter_any_path;
> }
>
> void btd_adapter_any_release_path(void)
> {
> adapter_any_refcount--;
>
> if (adapter_any_refcount > 0)
> return;
>
> g_free(adapter_any_path);
> adapter_any_path = NULL;
> }
>
> gboolean adapter_is_pairable(struct btd_adapter *adapter)
> {
> return adapter->pairable;
> }
>
> gboolean adapter_powering_down(struct btd_adapter *adapter)
> {
> return adapter->off_requested;
> }
>
> int btd_adapter_restore_powered(struct btd_adapter *adapter)
> {
> char mode[14], address[18];
>
> if (!adapter_ops)
> return -EINVAL;
>
> if (!main_opts.remember_powered)
> return -EINVAL;
>
> if (adapter->up)
> return 0;
>
> ba2str(&adapter->bdaddr, address);
> if (read_device_mode(address, mode, sizeof(mode)) == 0 &&
> g_str_equal(mode, "off"))
> return 0;
>
> return adapter_ops->set_powered(adapter->dev_id, TRUE);
> }
>
> int btd_adapter_switch_online(struct btd_adapter *adapter)
> {
> if (!adapter_ops)
> return -EINVAL;
>
> if (adapter->up)
> return 0;
>
> return adapter_ops->set_powered(adapter->dev_id, TRUE);
> }
>
> int btd_adapter_switch_offline(struct btd_adapter *adapter)
> {
> if (!adapter_ops)
> return -EINVAL;
>
> if (!adapter->up)
> return 0;
>
> return adapter_ops->set_powered(adapter->dev_id, FALSE);
> }
>
> int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority)
> {
> if (ops->setup == NULL)
> return -EINVAL;
>
> if (priority)
> ops_candidates = g_slist_prepend(ops_candidates, ops);
> else
> ops_candidates = g_slist_append(ops_candidates, ops);
>
> return 0;
> }
>
> void btd_adapter_cleanup_ops(struct btd_adapter_ops *ops)
> {
> ops_candidates = g_slist_remove(ops_candidates, ops);
> ops->cleanup();
>
> if (adapter_ops == ops)
> adapter_ops = NULL;
> }
>
> int adapter_ops_setup(void)
> {
> GSList *l;
> int ret;
>
> if (!ops_candidates)
> return -EINVAL;
>
> for (l = ops_candidates; l != NULL; l = g_slist_next(l)) {
> struct btd_adapter_ops *ops = l->data;
>
> ret = ops->setup();
> if (ret < 0)
> continue;
>
> adapter_ops = ops;
> break;
> }
>
> return ret;
> }
>
> void btd_adapter_register_powered_callback(struct btd_adapter *adapter,
> btd_adapter_powered_cb cb)
> {
> adapter->powered_callbacks =
> g_slist_append(adapter->powered_callbacks, cb);
> }
>
> void btd_adapter_unregister_powered_callback(struct btd_adapter *adapter,
> btd_adapter_powered_cb cb)
> {
> adapter->powered_callbacks =
> g_slist_remove(adapter->powered_callbacks, cb);
> }
>
> int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
> gboolean enable)
> {
> if (!adapter_ops)
> return -EINVAL;
>
> if (!adapter->up)
> return -EINVAL;
>
> return adapter_ops->set_fast_connectable(adapter->dev_id, enable);
> }
>
> int btd_adapter_read_clock(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> int which, int timeout, uint32_t *clock,
> uint16_t *accuracy)
> {
> if (!adapter_ops)
> return -EINVAL;
>
> if (!adapter->up)
> return -EINVAL;
>
> return adapter_ops->read_clock(adapter->dev_id, bdaddr, which,
> timeout, clock, accuracy);
> }
>
> int btd_adapter_disconnect_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->disconnect(adapter->dev_id, bdaddr);
> }
>
> int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->remove_bonding(adapter->dev_id, bdaddr);
> }
>
> int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> const char *pin, size_t pin_len)
> {
> return adapter_ops->pincode_reply(adapter->dev_id, bdaddr, pin,
> pin_len);
> }
>
> int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> gboolean success)
> {
> return adapter_ops->confirm_reply(adapter->dev_id, bdaddr, success);
> }
>
> int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> uint32_t passkey)
> {
> return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, passkey);
> }
>
> int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> bt_hci_result_t cb, gpointer user_data)
> {
> return adapter_ops->encrypt_link(adapter->dev_id, bdaddr, cb, user_data);
> }
>
> int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
> uint16_t product, uint16_t version)
> {
> return adapter_ops->set_did(adapter->dev_id, vendor, product, version);
> }
>
> int btd_adapter_retry_authentication(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->retry_authentication(adapter->dev_id, bdaddr);
> }
>
> int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
> uint8_t io_cap)
> {
> return adapter_ops->create_bonding(adapter->dev_id, bdaddr, io_cap);
> }
>
> int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
> {
> return adapter_ops->cancel_bonding(adapter->dev_id, bdaddr);
> }
>
> int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
> {
> return adapter_ops->read_local_oob_data(adapter->dev_id);
> }
>
> int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
> bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer)
> {
> return adapter_ops->add_remote_oob_data(adapter->dev_id, bdaddr, hash,
> randomizer);
> }
>
> int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
> bdaddr_t *bdaddr)
> {
> return adapter_ops->remove_remote_oob_data(adapter->dev_id, bdaddr);
> }
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: object.c
URL: <http://lists.freedesktop.org/archives/dbus/attachments/20130917/6c199007/attachment.c>
------------------------------
_______________________________________________
dbus mailing list
dbus at lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dbus
End of dbus Digest, Vol 98, Issue 11
************************************
More information about the dbus
mailing list