* Re: [PATCH 3/3] obex: Use XDG_RUNTIME_DIR as a default root
From: Bastien Nocera @ 2013-11-10 13:46 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384016598.3880.38.camel@nuvo>
On Sat, 2013-11-09 at 18:03 +0100, Bastien Nocera wrote:
> It's per-user, so we won't try to overwrite somebody else's
> files in /tmp when that happens.
There might be another useful directory instead. We could use
g_get_user_cache_dir() instead of the run-time dir. This would save
cross-partition moves by default, making the move to the Downloads dir
atomic (a single rename, which can fail, iterate until we find a "free"
filename).
Is that better for you?
> ---
> obexd/src/main.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/obexd/src/main.c b/obexd/src/main.c
> index 61a06b2..15777f5 100644
> --- a/obexd/src/main.c
> +++ b/obexd/src/main.c
> @@ -50,8 +50,6 @@
> #include "obexd.h"
> #include "server.h"
>
> -#define DEFAULT_ROOT_PATH "/tmp"
> -
> #define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml"
>
> static GMainLoop *main_loop = NULL;
> @@ -286,7 +284,7 @@ int main(int argc, char *argv[])
> }
>
> if (option_root == NULL)
> - option_root = g_strdup(DEFAULT_ROOT_PATH);
> + option_root = g_build_filename (g_get_user_runtime_dir (), "obexd", NULL);
>
> if (option_root[0] != '/') {
> char *old_root = option_root, *home = getenv("HOME");
^ permalink raw reply
* Reading RSSI value from an RFCOMM connection
From: Michael Kehm @ 2013-11-10 6:31 UTC (permalink / raw)
To: linux-bluetooth@vger.kernel.org
Hi folks,
I am trying to get the RSSI value from an established RFCOMM connection.
While searching on the Internet for hints I found the following discussions:
http://comments.gmane.org/gmane.linux.bluez.user/9986
http://stackoverflow.com/questions/2149295/android-2-1-how-do-i-poll-the-rssi-value-of-an-existing-bluetooth-connection
http://sourceforge.net/mailarchive/forum.php?forum_name=bluez-devel&style=nested&viewmonth=200505&viewday=17
Most suggestions point to the hci_read_rssi API (int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to);).
I am using Python. As a basic skeleton I am using Albert Huang's rfcomm-client.py and rfcomm-server.py scripts.
Looking into bluez.py and inquiry-with-rssi.py samples, it helped me to understand how to access the hci layer but I am stuck in execution.
This is my script based on rfcomm-server.py:
import os, sys, struct, fcntl, array, binascii
import btcommon
import bluetooth._bluetooth as _bt
from bluetooth import *
#################################################################
# Added code
def get_acl_conn_handle (hci_sock, addr):
hci_fd = hci_sock.fileno ()
reqstr = struct.pack ("6sB17s", _bt.str2ba (addr), _bt.ACL_LINK, "\0" * 17)
request = array.array ("c", reqstr)
print "ACL_LINK:%s" % _bt.ACL_LINK
print "hci_fd:%s" % hci_fd
print "HCIGETCONNINFO:%s" % _bt.HCIGETCONNINFO
print "request:%s" % request
fcntl.ioctl (hci_fd, _bt.HCIGETCONNINFO, request, 1)
try:
fcntl.ioctl (hci_fd, _bt.HCIGETCONNINFO, request, 1)
except IOError, e:
raise BluetoothError ("There is no ACL connection to %s" % addr)
# XXX should this be "<8xH14x"?
handle = struct.unpack ("8xH14x", request.tostring ())[0]
return handle
#################################################################
server_sock=BluetoothSocket( RFCOMM )
server_sock.bind(("",PORT_ANY))
server_sock.listen(1)
port = server_sock.getsockname()[1]
uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee"
advertise_service( server_sock, "SampleServer",
service_id = uuid,
service_classes = [ uuid, SERIAL_PORT_CLASS ],
profiles = [ SERIAL_PORT_PROFILE ],
# protocols = [ OBEX_UUID ]
)
print "Waiting for connection on RFCOMM channel %d" % port
client_sock, client_info = server_sock.accept()
print "Accepted connection from ", client_info
#################################################################
# Added code
rssi = 0
handle = get_acl_conn_handle(server_sock._sock, client_info[0])
rssi = bluez.hci_read_rssi(server_sock._sock, handle, rssi, 1000)
#################################################################
try:
client_sock.send(rssi)
print "sent [%s]" % rssi
except IOError:
pass
print "disconnected"
client_sock.close()
server_sock.close()
print "all done"
I have added/copied the get_acl_conn_handle function and the call to hci_read_rssi.
The problem is that I run into the following exception:
Traceback (most recent call last):
File "rfcomm-rssi.py", line 48, in <module>
handle = get_acl_conn_handle(server_sock._sock, client_info[0])
File "rfcomm-rssi.py", line 16, in get_acl_conn_handle
fcntl.ioctl (hci_fd, _bt.HCIGETCONNINFO, request, 1)
IOError: [Errno 22] Invalid argument
The print statements in get_acl_conn_handle return the following information:
ACL_LINK:1
hci_fd:3
HCIGETCONNINFO:-2147202859
request:array('c', '9\x91\x97g\x1d\x1c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
I am bit lost on how to fix this.
If anyone can help me with my script above or if anyone has sample code in Python or C how to get the RSSI data from a RFCOMM connection then I would highly appreciate it.
Thanks in advance,
Michael
^ permalink raw reply
* Re: obexd broken for absolute paths
From: Bastien Nocera @ 2013-11-10 0:59 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZJyKT65rUxdhtkQit3ok4Y7khHF0qjROL1-0YzZUUbMiA@mail.gmail.com>
On Sun, 2013-11-10 at 02:49 +0200, Luiz Augusto von Dentz wrote:
> Hi Bastian,
>
> On Sat, Nov 9, 2013 at 11:00 PM, Bastien Nocera <hadess@hadess.net> wrote:
> > On Sat, 2013-11-09 at 20:35 +0200, Luiz Augusto von Dentz wrote:
> >> Hi Bastian,
> >>
> >> On Sat, Nov 9, 2013 at 7:17 PM, Bastien Nocera <hadess@hadess.net> wrote:
> >> > On Fri, 2013-11-08 at 20:21 +0100, Bastien Nocera wrote:
> >> >> Heya,
> >> >>
> >> >> I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
> >> >> and was quite surprised it didn't work one bit, with transfers failing
> >> >> as soon as they were created.
> >> >>
> >> >> I made this simple change to test/simple-obex-agent so you could
> >> >> replicate the failure. Obviously, change the download path to exist on
> >> >> your system:
> >> >> - return properties['Name']
> >> >> + return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
> >> >>
> >> >> This will see OBEX Push transfers fail as soon as accepted.
> >> >
> >> > Turns out this is a feature of filesystem plugin in obexd, and a bit of
> >> > a problem as well:
> >> > - There's no way to change the folder without changing the service file
> >>
> >> Yep, I remember discussing with Gustavo Padovan that this should
> >> probably be set by the agent upon registration.
> >
> > I thought about that, but it's really a security issue. obexd might be
> > running in a different context than the "application" telling it where
> > to write. For example, my share application might be restricted to write
> > new files in ~/Downloads, but could tell obexd to write to ~/.ssh/etc.
>
> If obexd is running with a different user then yes, but I don't think
> this is the case otherwise we should pass fd not a path since the
> files should really belong to the agent not obexd.
Not a different user, a different security context, which is going to
happen when applications are sandboxed within the session.
> >> > - It doesn't default to use the XDG_RUNTIME_DIR
> >>
> >> That is a good default considering we don't implement the change
> >> above, otherwise for auto accept I believe tmp is usually a better
> >> option.
> >
> > This is what I intend to use in gnome-user-share. obexd would write
> > files with unique filenames to /run/user/<id>/obexd and move it
> > ~/Downloads after uniquifying the name (eg. sending 2 files called
> > "foo.jpg" should give me 2 files, not overwrite the first one as it does
> > now).
>
> Not sure what is the point of doing this, except if you want to use
> tmpfs while downloading and only really store on disk when the
> transfer is complete, overwrite is agent problem to select different
> path if the file already exist or alert the user it will overwrite.
There's no way of doing this in a race-free way. The right way to do
this would be for the agent to return a file descriptor, not a path.
A file with that path might be created between the time the filename is
considered "free" by the agent and obexd actually writing it. It might
even be racy when receiving multiple files with the same name.
^ permalink raw reply
* Re: obexd broken for absolute paths
From: Luiz Augusto von Dentz @ 2013-11-10 0:49 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1384030812.3880.47.camel@nuvo>
Hi Bastian,
On Sat, Nov 9, 2013 at 11:00 PM, Bastien Nocera <hadess@hadess.net> wrote:
> On Sat, 2013-11-09 at 20:35 +0200, Luiz Augusto von Dentz wrote:
>> Hi Bastian,
>>
>> On Sat, Nov 9, 2013 at 7:17 PM, Bastien Nocera <hadess@hadess.net> wrote:
>> > On Fri, 2013-11-08 at 20:21 +0100, Bastien Nocera wrote:
>> >> Heya,
>> >>
>> >> I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
>> >> and was quite surprised it didn't work one bit, with transfers failing
>> >> as soon as they were created.
>> >>
>> >> I made this simple change to test/simple-obex-agent so you could
>> >> replicate the failure. Obviously, change the download path to exist on
>> >> your system:
>> >> - return properties['Name']
>> >> + return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
>> >>
>> >> This will see OBEX Push transfers fail as soon as accepted.
>> >
>> > Turns out this is a feature of filesystem plugin in obexd, and a bit of
>> > a problem as well:
>> > - There's no way to change the folder without changing the service file
>>
>> Yep, I remember discussing with Gustavo Padovan that this should
>> probably be set by the agent upon registration.
>
> I thought about that, but it's really a security issue. obexd might be
> running in a different context than the "application" telling it where
> to write. For example, my share application might be restricted to write
> new files in ~/Downloads, but could tell obexd to write to ~/.ssh/etc.
If obexd is running with a different user then yes, but I don't think
this is the case otherwise we should pass fd not a path since the
files should really belong to the agent not obexd.
>> > - It doesn't default to use the XDG_RUNTIME_DIR
>>
>> That is a good default considering we don't implement the change
>> above, otherwise for auto accept I believe tmp is usually a better
>> option.
>
> This is what I intend to use in gnome-user-share. obexd would write
> files with unique filenames to /run/user/<id>/obexd and move it
> ~/Downloads after uniquifying the name (eg. sending 2 files called
> "foo.jpg" should give me 2 files, not overwrite the first one as it does
> now).
Not sure what is the point of doing this, except if you want to use
tmpfs while downloading and only really store on disk when the
transfer is complete, overwrite is agent problem to select different
path if the file already exist or alert the user it will overwrite.
--
Luiz Augusto von Dentz
^ permalink raw reply
* Debugging broken audio stream
From: Stanisław Pitucha @ 2013-11-09 22:33 UTC (permalink / raw)
To: linux-bluetooth
Hi all,
I'm having issues with using a remote audio sink device and I can't
find any clear indications of a problem.
My current config is:
- kernel 3.11.6 (x86_64)
- bluez 5.10
- pulseaudio 4.0
- ID 0a5c:21e6 Broadcom Corp. BCM20702 Bluetooth 4.0 [ThinkPad] (btusb driver)
The receiving device is Logitech's wireless audio adapter.
When sending data from android devices (4.1, 4.3) everything works
without issues, so I think the receiver seems ok.
On the laptop side which is having problems, the initial connection /
pairing works without issues, but the sound stream is very unstable
(unusable really: 1 second of sound, 5 of silence, repeat...) and
sometimes the connection drops / reconnects on its own. Sometimes it
works for a couple of seconds before failing.
Sometimes there's a stacktrace reported from the kernel (see
https://bugs.archlinux.org/task/37697 ), but not always.
The debug output from bluetoothd doesn't report any specific issues
(as far as I can tell from the log) until it disconnects completely
("Abort: Connection timed out (110)")
A full session in debug mode (connect, try to play some music,
connection times out, tries playing again, killing bluez) is available
at http://pastebin.com/n7qfumYR
Syslog contains pages of repeated messages like this (different
time/sizes in each line, chosen smallest/biggest I can see):
Nov 09 20:14:00 gumby pulseaudio[19585]: [bluetooth]
module-bluez5-device.c: Skipping 116 us (= 20 bytes) in audio stream
...
Nov 09 20:14:03 gumby pulseaudio[19585]: [bluetooth]
module-bluez5-device.c: Skipping 564192 us (= 99520 bytes) in audio
stream
I'm not sure how to prove the issue is definitely on either the bluez,
or pulseaudio's side...
What more can I do in this case? What other debugging options do I
have? Would capturing the bluetooth packets be useful at all?
I assume the bluetooth in laptop is not damaged/unsupported, because I
can exchange files between it and the android devices.
Any/all help appreciated.
--
KTHXBYE,
Stanisław Pitucha
^ permalink raw reply
* Re: obexd broken for absolute paths
From: Bastien Nocera @ 2013-11-09 21:00 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZJtvdd0iDmcuUe7S9mi68U1nQOLTy577vu0T-kpa9wMBA@mail.gmail.com>
On Sat, 2013-11-09 at 20:35 +0200, Luiz Augusto von Dentz wrote:
> Hi Bastian,
>
> On Sat, Nov 9, 2013 at 7:17 PM, Bastien Nocera <hadess@hadess.net> wrote:
> > On Fri, 2013-11-08 at 20:21 +0100, Bastien Nocera wrote:
> >> Heya,
> >>
> >> I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
> >> and was quite surprised it didn't work one bit, with transfers failing
> >> as soon as they were created.
> >>
> >> I made this simple change to test/simple-obex-agent so you could
> >> replicate the failure. Obviously, change the download path to exist on
> >> your system:
> >> - return properties['Name']
> >> + return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
> >>
> >> This will see OBEX Push transfers fail as soon as accepted.
> >
> > Turns out this is a feature of filesystem plugin in obexd, and a bit of
> > a problem as well:
> > - There's no way to change the folder without changing the service file
>
> Yep, I remember discussing with Gustavo Padovan that this should
> probably be set by the agent upon registration.
I thought about that, but it's really a security issue. obexd might be
running in a different context than the "application" telling it where
to write. For example, my share application might be restricted to write
new files in ~/Downloads, but could tell obexd to write to ~/.ssh/etc.
> > - It doesn't default to use the XDG_RUNTIME_DIR
>
> That is a good default considering we don't implement the change
> above, otherwise for auto accept I believe tmp is usually a better
> option.
This is what I intend to use in gnome-user-share. obexd would write
files with unique filenames to /run/user/<id>/obexd and move it
~/Downloads after uniquifying the name (eg. sending 2 files called
"foo.jpg" should give me 2 files, not overwrite the first one as it does
now).
Cheers
^ permalink raw reply
* Re: obexd broken for absolute paths
From: Luiz Augusto von Dentz @ 2013-11-09 18:35 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1384017476.3880.40.camel@nuvo>
Hi Bastian,
On Sat, Nov 9, 2013 at 7:17 PM, Bastien Nocera <hadess@hadess.net> wrote:
> On Fri, 2013-11-08 at 20:21 +0100, Bastien Nocera wrote:
>> Heya,
>>
>> I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
>> and was quite surprised it didn't work one bit, with transfers failing
>> as soon as they were created.
>>
>> I made this simple change to test/simple-obex-agent so you could
>> replicate the failure. Obviously, change the download path to exist on
>> your system:
>> - return properties['Name']
>> + return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
>>
>> This will see OBEX Push transfers fail as soon as accepted.
>
> Turns out this is a feature of filesystem plugin in obexd, and a bit of
> a problem as well:
> - There's no way to change the folder without changing the service file
Yep, I remember discussing with Gustavo Padovan that this should
probably be set by the agent upon registration.
> - It doesn't default to use the XDG_RUNTIME_DIR
That is a good default considering we don't implement the change
above, otherwise for auto accept I believe tmp is usually a better
option.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH] obex: Use GLib helper function to manipulate paths
From: Bastien Nocera @ 2013-11-09 17:18 UTC (permalink / raw)
To: linux-bluetooth
Instead of trying to do it by hand. This also makes sure that
relative paths aren't used by the agent.
---
obexd/src/manager.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/obexd/src/manager.c b/obexd/src/manager.c
index cec8a39..f18896e 100644
--- a/obexd/src/manager.c
+++ b/obexd/src/manager.c
@@ -651,14 +651,14 @@ static void agent_reply(DBusPendingCall *call, void *user_data)
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID)) {
/* Splits folder and name */
- const char *slash = strrchr(name, '/');
+ gboolean is_relative = !g_path_is_absolute(name);
DBG("Agent replied with %s", name);
- if (!slash) {
- agent->new_name = g_strdup(name);
+ if (is_relative) {
+ agent->new_name = g_path_get_basename(name);
agent->new_folder = NULL;
} else {
- agent->new_name = g_strdup(slash + 1);
- agent->new_folder = g_strndup(name, slash - name);
+ agent->new_name = g_path_get_basename(name);
+ agent->new_folder = g_path_get_dirname(name);
}
}
--
1.8.4.2
^ permalink raw reply related
* Re: obexd broken for absolute paths
From: Bastien Nocera @ 2013-11-09 17:17 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383938500.3880.23.camel@nuvo>
On Fri, 2013-11-08 at 20:21 +0100, Bastien Nocera wrote:
> Heya,
>
> I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
> and was quite surprised it didn't work one bit, with transfers failing
> as soon as they were created.
>
> I made this simple change to test/simple-obex-agent so you could
> replicate the failure. Obviously, change the download path to exist on
> your system:
> - return properties['Name']
> + return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
>
> This will see OBEX Push transfers fail as soon as accepted.
Turns out this is a feature of filesystem plugin in obexd, and a bit of
a problem as well:
- There's no way to change the folder without changing the service file
- It doesn't default to use the XDG_RUNTIME_DIR
> I must also mention the dreadful code in agent_reply() in obexd/src/manager.c:
> const char *slash = strrchr(name, '/');
> DBG("Agent replied with %s", name);
> if (!slash) {
> agent->new_name = g_strdup(name);
> agent->new_folder = NULL;
> } else {
> agent->new_name = g_strdup(slash + 1);
> agent->new_folder = g_strndup(name, slash - name);
> }
>
> Please use g_path_get_basename() and g_path_get_dirname(). This is even
> a security issue because I could pass relative paths and expect some
> system file to get overwritten.
I've sent a patch for this.
Cheers
^ permalink raw reply
* Re: [PATCH 1/3] gdbus: Don't try to remove removed sources
From: Bastien Nocera @ 2013-11-09 17:04 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384016577.3880.36.camel@nuvo>
On Sat, 2013-11-09 at 18:02 +0100, Bastien Nocera wrote:
> When we return FALSE from idle handlers, the source is removed.
> This will be causing warnings in glib 2.40.
>
> See https://bugzilla.gnome.org/show_bug.cgi?id=710724
Note that there's plenty of other places where this should be fixed.
I'll leave those up to somebody who doesn't get RSI ;)
^ permalink raw reply
* [PATCH 3/3] obex: Use XDG_RUNTIME_DIR as a default root
From: Bastien Nocera @ 2013-11-09 17:03 UTC (permalink / raw)
To: linux-bluetooth
It's per-user, so we won't try to overwrite somebody else's
files in /tmp when that happens.
---
obexd/src/main.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/obexd/src/main.c b/obexd/src/main.c
index 61a06b2..15777f5 100644
--- a/obexd/src/main.c
+++ b/obexd/src/main.c
@@ -50,8 +50,6 @@
#include "obexd.h"
#include "server.h"
-#define DEFAULT_ROOT_PATH "/tmp"
-
#define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml"
static GMainLoop *main_loop = NULL;
@@ -286,7 +284,7 @@ int main(int argc, char *argv[])
}
if (option_root == NULL)
- option_root = g_strdup(DEFAULT_ROOT_PATH);
+ option_root = g_build_filename (g_get_user_runtime_dir (), "obexd", NULL);
if (option_root[0] != '/') {
char *old_root = option_root, *home = getenv("HOME");
--
1.8.4.2
^ permalink raw reply related
* [PATCH 2/3] all: Use G_SOURCE_REMOVE/G_SOURCE_CONTINUE macros
From: Bastien Nocera @ 2013-11-09 17:03 UTC (permalink / raw)
To: linux-bluetooth
Instead of TRUE/FALSE. This makes the source more readable.
---
android/main.c | 4 ++--
attrib/gattrib.c | 2 +-
attrib/gatttool.c | 2 +-
gdbus/mainloop.c | 6 +++---
gdbus/object.c | 2 +-
gdbus/watch.c | 2 +-
gobex/gobex.c | 4 ++--
obexd/client/session.c | 4 ++--
obexd/client/transfer.c | 6 +++---
obexd/plugins/messages-dummy.c | 2 +-
obexd/plugins/messages-tracker.c | 2 +-
obexd/plugins/phonebook-dummy.c | 6 +++---
plugins/policy.c | 6 +++---
profiles/audio/a2dp.c | 16 ++++++++--------
profiles/audio/avctp.c | 12 ++++++------
profiles/audio/avdtp.c | 14 +++++++-------
profiles/audio/avrcp.c | 2 +-
profiles/audio/player.c | 4 ++--
profiles/cyclingspeed/cyclingspeed.c | 2 +-
profiles/health/hdp.c | 4 ++--
profiles/health/mcap.c | 2 +-
profiles/health/mcap_sync.c | 18 +++++++++---------
profiles/input/device.c | 10 +++++-----
profiles/network/connection.c | 2 +-
profiles/proximity/monitor.c | 4 ++--
profiles/sap/server.c | 4 ++--
src/adapter.c | 20 ++++++++++----------
src/device.c | 16 ++++++++--------
src/main.c | 4 ++--
src/sdp-client.c | 2 +-
src/shared/hciemu.c | 2 +-
src/shared/tester.c | 18 +++++++++---------
tools/btiotest.c | 6 +++---
unit/test-gdbus-client.c | 4 ++--
unit/test-gobex-transfer.c | 2 +-
unit/test-gobex.c | 4 ++--
unit/test-sdp.c | 2 +-
37 files changed, 111 insertions(+), 111 deletions(-)
diff --git a/android/main.c b/android/main.c
index 75004cf..a36269c 100644
--- a/android/main.c
+++ b/android/main.c
@@ -377,7 +377,7 @@ static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
static gboolean quit_eventloop(gpointer user_data)
{
g_main_loop_quit(event_loop);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
@@ -524,7 +524,7 @@ static gboolean adapter_timeout_handler(gpointer user_data)
adapter_timeout = 0;
g_main_loop_quit(event_loop);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void read_index_list_complete(uint8_t status, uint16_t length,
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 609b908..4d93902 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -284,7 +284,7 @@ done:
g_attrib_unref(attrib);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index f211dcd..7ef26aa 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -116,7 +116,7 @@ static gboolean listen_start(gpointer user_data)
g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
events_handler, attrib, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
index 099b67f..d00db33 100644
--- a/gdbus/mainloop.c
+++ b/gdbus/mainloop.c
@@ -77,7 +77,7 @@ static gboolean message_dispatch(void *data)
dbus_connection_unref(conn);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static inline void queue_dispatch(DBusConnection *conn,
@@ -186,11 +186,11 @@ static gboolean timeout_handler_dispatch(gpointer data)
/* if not enabled should not be polled by the main loop */
if (!dbus_timeout_get_enabled(handler->timeout))
- return FALSE;
+ return G_SOURCE_REMOVE;
dbus_timeout_handle(handler->timeout);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void timeout_handler_free(void *data)
diff --git a/gdbus/object.c b/gdbus/object.c
index b248cbb..72c542e 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1008,7 +1008,7 @@ static gboolean process_changes(gpointer user_data)
data->process_id = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void generic_unregister(DBusConnection *connection, void *user_data)
diff --git a/gdbus/watch.c b/gdbus/watch.c
index 0f99f4f..f357cd1 100644
--- a/gdbus/watch.c
+++ b/gdbus/watch.c
@@ -599,7 +599,7 @@ static gboolean update_service(void *user_data)
dbus_connection_unref(conn);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void service_reply(DBusPendingCall *call, void *user_data)
diff --git a/gobex/gobex.c b/gobex/gobex.c
index 8c08b1e..e107c0c 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -253,7 +253,7 @@ static gboolean req_timeout(gpointer user_data)
g_error_free(err);
pending_pkt_free(p);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean write_stream(GObex *obex, GError **err)
@@ -720,7 +720,7 @@ static gboolean cancel_complete(gpointer user_data)
pending_pkt_free(p);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
diff --git a/obexd/client/session.c b/obexd/client/session.c
index 8138b1e..72fcc6d 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -435,7 +435,7 @@ static gboolean connection_complete(gpointer data)
g_free(cb);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static int session_connect(struct obc_session *session,
@@ -730,7 +730,7 @@ static gboolean session_process(gpointer data)
session_process_queue(session);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void session_queue(struct pending_request *p)
diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c
index 5a8d4f2..54713a9 100644
--- a/obexd/client/transfer.c
+++ b/obexd/client/transfer.c
@@ -680,13 +680,13 @@ static gboolean report_progress(gpointer data)
struct obc_transfer *transfer = data;
if (transfer->transferred == transfer->progress)
- return TRUE;
+ return G_SOURCE_CONTINUE;
transfer->progress = transfer->transferred;
if (transfer->transferred == transfer->size) {
transfer->progress_id = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
if (transfer->status != TRANSFER_STATUS_ACTIVE)
@@ -695,7 +695,7 @@ static gboolean report_progress(gpointer data)
g_dbus_emit_property_changed(transfer->conn, transfer->path,
TRANSFER_INTERFACE, "Transferred");
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
diff --git a/obexd/plugins/messages-dummy.c b/obexd/plugins/messages-dummy.c
index bb0627f..439ea95 100644
--- a/obexd/plugins/messages-dummy.c
+++ b/obexd/plugins/messages-dummy.c
@@ -198,7 +198,7 @@ static gboolean get_folder_listing(void *d)
return_folder_listing(fld, list);
g_slist_free_full(list, g_free);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
int messages_init(void)
diff --git a/obexd/plugins/messages-tracker.c b/obexd/plugins/messages-tracker.c
index 60f3a80..6df57ad 100644
--- a/obexd/plugins/messages-tracker.c
+++ b/obexd/plugins/messages-tracker.c
@@ -282,7 +282,7 @@ static gboolean async_get_folder_listing(void *s)
g_free(path);
g_free(session->name);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
int messages_get_folder_listing(void *s, const char *name,
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index 6b9d040..ca266ef 100644
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
@@ -252,7 +252,7 @@ done:
g_string_free(buffer, TRUE);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void entry_notify(const char *filename, VObject *v, void *user_data)
@@ -326,7 +326,7 @@ static gboolean create_cache(void *user_data)
query->ready_cb(query->user_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean read_entry(void *user_data)
@@ -348,7 +348,7 @@ static gboolean read_entry(void *user_data)
dummy->cb(buffer, count, 1, 0, TRUE, dummy->user_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean is_dir(const char *dir)
diff --git a/plugins/policy.c b/plugins/policy.c
index 0292482..787371f 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -90,7 +90,7 @@ static gboolean policy_connect_ct(gpointer user_data)
if (service != NULL)
policy_connect(data, service);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void policy_set_ct_timer(struct policy_data *data)
@@ -163,7 +163,7 @@ static gboolean policy_connect_sink(gpointer user_data)
if (service != NULL)
policy_connect(data, service);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void policy_set_sink_timer(struct policy_data *data)
@@ -272,7 +272,7 @@ static gboolean policy_connect_source(gpointer user_data)
if (service != NULL)
policy_connect(data, service);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void policy_set_source_timer(struct policy_data *data)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 8477b5d..1299171 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -224,7 +224,7 @@ static gboolean finalize_config(gpointer data)
setup_cb_free(cb);
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean finalize_resume(gpointer data)
@@ -244,7 +244,7 @@ static gboolean finalize_resume(gpointer data)
setup_cb_free(cb);
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean finalize_suspend(gpointer data)
@@ -264,7 +264,7 @@ static gboolean finalize_suspend(gpointer data)
setup_cb_free(cb);
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void finalize_select(struct a2dp_setup *s)
@@ -384,7 +384,7 @@ static gboolean auto_config(gpointer data)
/* Check if configuration was aborted */
if (setup->sep->stream == NULL)
- return FALSE;
+ return G_SOURCE_REMOVE;
if (setup->err != NULL)
goto done;
@@ -413,7 +413,7 @@ done:
setup_unref(setup);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
@@ -727,7 +727,7 @@ static gboolean suspend_timeout(struct a2dp_sep *sep)
avdtp_unref(sep->session);
sep->session = NULL;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -928,11 +928,11 @@ static gboolean a2dp_reconfigure(gpointer data)
goto failed;
}
- return FALSE;
+ return G_SOURCE_REMOVE;
failed:
finalize_setup_errno(setup, posix_err, finalize_config, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index dac7a66..fb1a167 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -299,7 +299,7 @@ static gboolean auto_release(gpointer user_data)
send_key(session->uinput, session->key.op, 0);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static size_t handle_panel_passthrough(struct avctp *session,
@@ -697,7 +697,7 @@ static gboolean req_timeout(gpointer user_data)
if (chan->process_id == 0)
chan->process_id = g_idle_add(process_queue, chan);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static int process_control(void *data)
@@ -727,7 +727,7 @@ static gboolean process_queue(void *user_data)
chan->process_id = 0;
if (p != NULL)
- return FALSE;
+ return G_SOURCE_REMOVE;
while ((p = g_queue_pop_head(chan->queue))) {
@@ -738,12 +738,12 @@ static gboolean process_queue(void *user_data)
}
if (p == NULL)
- return FALSE;
+ return G_SOURCE_REMOVE;
chan->p = p;
p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
@@ -1653,7 +1653,7 @@ static gboolean repeat_timeout(gpointer user_data)
avctp_passthrough_release(session, session->key.op);
avctp_passthrough_press(session, session->key.op);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static void release_pressed(struct avctp *session)
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index dab8f1c..ccbd117 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -630,7 +630,7 @@ static gboolean stream_close_timeout(gpointer user_data)
close_stream(stream);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean stream_open_timeout(gpointer user_data)
@@ -645,7 +645,7 @@ static gboolean stream_open_timeout(gpointer user_data)
avdtp_abort(stream->session, stream);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
@@ -1181,18 +1181,18 @@ static gboolean disconnect_timeout(gpointer user_data)
service = btd_device_get_service(session->device, A2DP_SINK_UUID);
if (service && stream_setup) {
sink_setup_stream(service, session);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
service = btd_device_get_service(session->device, A2DP_SOURCE_UUID);
if (service && stream_setup) {
source_setup_stream(service, session);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
connection_lost(session, ETIMEDOUT);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void set_disconnect_timer(struct avdtp *session)
@@ -2675,7 +2675,7 @@ static gboolean request_timeout(gpointer user_data)
cancel_request(session, ETIMEDOUT);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static int send_req(struct avdtp *session, gboolean priority,
@@ -3332,7 +3332,7 @@ static gboolean process_discover(gpointer data)
finalize_discovery(session, 0);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index cd027c6..b3a1873 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -3323,7 +3323,7 @@ static gboolean connect_browsing(gpointer user_data)
avctp_connect_browsing(session->conn);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void avrcp_connect_browsing(struct avrcp *session)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 6150c8a..bce72e9 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1306,13 +1306,13 @@ static gboolean process_metadata_changed(void *user_data)
item = g_hash_table_lookup(mp->track, "Item");
if (item == NULL)
- return FALSE;
+ return G_SOURCE_REMOVE;
g_dbus_emit_property_changed(btd_get_dbus_connection(),
item, MEDIA_ITEM_INTERFACE,
"Metadata");
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void media_player_set_metadata(struct media_player *mp,
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 6ecc985..3ad9e75 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -307,7 +307,7 @@ static gboolean controlpoint_timeout(gpointer user_data)
req->csc->pending_req = NULL;
g_free(req);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c
index 7b4e799..d7d0699 100644
--- a/profiles/health/hdp.c
+++ b/profiles/health/hdp.c
@@ -1536,12 +1536,12 @@ static gboolean echo_timeout(gpointer data)
fd = mcap_mdl_get_fd(chan->mdl);
if (fd < 0)
- return FALSE;
+ return G_SOURCE_REMOVE;
io = g_io_channel_unix_new(fd);
g_io_channel_shutdown(io, TRUE, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err,
diff --git a/profiles/health/mcap.c b/profiles/health/mcap.c
index 6d821f3..37b5789 100644
--- a/profiles/health/mcap.c
+++ b/profiles/health/mcap.c
@@ -463,7 +463,7 @@ static gboolean wait_response_timer(gpointer data)
mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
mcap_cache_mcl(mcl);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
gboolean mcap_create_mdl(struct mcap_mcl *mcl,
diff --git a/profiles/health/mcap_sync.c b/profiles/health/mcap_sync.c
index 0d9f17d..aea0202 100644
--- a/profiles/health/mcap_sync.c
+++ b/profiles/health/mcap_sync.c
@@ -517,15 +517,15 @@ static gboolean sync_send_indication(gpointer user_data)
int sent;
if (!user_data)
- return FALSE;
+ return G_SOURCE_REMOVE;
mcl = user_data;
if (!caps(mcl))
- return FALSE;
+ return G_SOURCE_REMOVE;
if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp))
- return FALSE;
+ return G_SOURCE_REMOVE;
cmd = g_new0(mcap_md_sync_info_ind, 1);
@@ -557,12 +557,12 @@ static gboolean proc_sync_set_req_phase2(gpointer user_data)
int delay;
if (!user_data)
- return FALSE;
+ return G_SOURCE_REMOVE;
mcl = user_data;
if (!mcl->csp->set_data)
- return FALSE;
+ return G_SOURCE_REMOVE;
data = mcl->csp->set_data;
update = data->update;
@@ -573,17 +573,17 @@ static gboolean proc_sync_set_req_phase2(gpointer user_data)
if (!caps(mcl)) {
send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) {
send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
if (get_btrole(mcl) != role) {
send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET);
@@ -624,7 +624,7 @@ static gboolean proc_sync_set_req_phase2(gpointer user_data)
if (update)
sync_send_indication(mcl);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
diff --git a/profiles/input/device.c b/profiles/input/device.c
index 6523161..bd0886e 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -668,23 +668,23 @@ static gboolean input_device_auto_reconnect(gpointer user_data)
* or is marked for removal. */
if (device_is_temporary(idev->device) ||
device_is_connected(idev->device))
- return FALSE;
+ return G_SOURCE_REMOVE;
/* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */
if (idev->reconnect_attempt >= 6)
- return FALSE;
+ return G_SOURCE_REMOVE;
/* Check if the profile is already connected. */
if (idev->ctrl_io)
- return FALSE;
+ return G_SOURCE_REMOVE;
if (is_connected(idev))
- return FALSE;
+ return G_SOURCE_REMOVE;
idev->reconnect_attempt++;
dev_connect(idev);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static const char * const _reconnect_mode_str[] = {
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index 960a1fe..5008ea1 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -354,7 +354,7 @@ static gboolean bnep_conn_req_to(gpointer user_data)
cancel_connection(nc, -ETIMEDOUT);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static int bnep_connect(struct network_conn *nc)
diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c
index eaa5b0d..a894235 100644
--- a/profiles/proximity/monitor.c
+++ b/profiles/proximity/monitor.c
@@ -304,7 +304,7 @@ static gboolean immediate_timeout(gpointer user_data)
monitor->immediateto = 0;
if (g_strcmp0(monitor->immediatelevel, "none") == 0)
- return FALSE;
+ return G_SOURCE_REMOVE;
if (monitor->attrib) {
uint8_t value = ALERT_NONE;
@@ -319,7 +319,7 @@ static gboolean immediate_timeout(gpointer user_data)
g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
PROXIMITY_INTERFACE, "ImmediateAlertLevel");
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void immediate_written(gpointer user_data)
diff --git a/profiles/sap/server.c b/profiles/sap/server.c
index 63314a7..81a5c58 100644
--- a/profiles/sap/server.c
+++ b/profiles/sap/server.c
@@ -583,7 +583,7 @@ static gboolean guard_timeout(gpointer data)
struct sap_connection *conn = server->conn;
if (!conn)
- return FALSE;
+ return G_SOURCE_REMOVE;
DBG("conn %p state %d pr 0x%02x", conn, conn->state,
conn->processing_req);
@@ -613,7 +613,7 @@ static gboolean guard_timeout(gpointer data)
break;
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void sap_set_connected(struct sap_server *server)
diff --git a/src/adapter.c b/src/adapter.c
index d904a56..80e7903 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -542,7 +542,7 @@ static gboolean pairable_timeout_handler(gpointer user_data)
set_mode(adapter, MGMT_OP_SET_PAIRABLE, 0x00);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void trigger_pairable_timeout(struct btd_adapter *adapter)
@@ -1125,7 +1125,7 @@ static gboolean passive_scanning_timeout(gpointer user_data)
adapter->dev_id, sizeof(cp), &cp,
passive_scanning_complete, adapter, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void trigger_passive_scanning(struct btd_adapter *adapter)
@@ -1300,12 +1300,12 @@ static gboolean start_discovery_timeout(gpointer user_data)
*/
if (adapter->discovery_type == new_type) {
if (adapter->discovering)
- return FALSE;
+ return G_SOURCE_REMOVE;
adapter->discovering = true;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/*
@@ -1328,7 +1328,7 @@ static gboolean start_discovery_timeout(gpointer user_data)
adapter->dev_id, sizeof(cp), &cp,
start_discovery_complete, adapter, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
@@ -1534,7 +1534,7 @@ static gboolean remove_temp_devices(gpointer user_data)
adapter_remove_device(adapter, dev);
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void discovery_destroy(void *user_data)
@@ -2381,7 +2381,7 @@ static gboolean load_ltks_timeout(gpointer user_data)
mgmt_cancel(adapter->mgmt, adapter->load_ltks_id);
adapter->load_ltks_id = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void load_ltks_complete(uint8_t status, uint16_t length,
@@ -4006,7 +4006,7 @@ static gboolean confirm_name_timeout(gpointer user_data)
mgmt_cancel(adapter->mgmt, adapter->confirm_name_id);
adapter->confirm_name_id = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void confirm_name_complete(uint8_t status, uint16_t length,
@@ -4412,7 +4412,7 @@ next:
dbus_error_free(&err);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
@@ -5029,7 +5029,7 @@ static gboolean pair_device_timeout(gpointer user_data)
adapter_cancel_bonding(adapter, &data->bdaddr, data->addr_type);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void pair_device_complete(uint8_t status, uint16_t length,
diff --git a/src/device.c b/src/device.c
index 77f2322..60fef83 100644
--- a/src/device.c
+++ b/src/device.c
@@ -364,7 +364,7 @@ static gboolean store_device_info_cb(gpointer user_data)
g_key_file_free(key_file);
g_free(uuids);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static bool device_address_is_private(struct btd_device *dev)
@@ -966,7 +966,7 @@ static gboolean do_disconnect(gpointer user_data)
btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
device->bdaddr_type);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
int device_block(struct btd_device *device, gboolean update_only)
@@ -3670,7 +3670,7 @@ static gboolean start_discovery(gpointer user_data)
device->discov_timer = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void device_set_paired(struct btd_device *device, gboolean value)
@@ -3784,7 +3784,7 @@ static gboolean svc_idle_cb(gpointer user_data)
g_free(cb);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
unsigned int device_wait_for_svc_complete(struct btd_device *dev,
@@ -3857,7 +3857,7 @@ static gboolean device_bonding_retry(gpointer data)
int err;
if (!bonding)
- return FALSE;
+ return G_SOURCE_REMOVE;
DBG("retrying bonding");
bonding->retry_timer = 0;
@@ -3878,7 +3878,7 @@ static gboolean device_bonding_retry(gpointer data)
if (err < 0)
device_bonding_complete(device, bonding->status);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
int device_bonding_attempt_retry(struct btd_device *device)
@@ -4371,13 +4371,13 @@ static gboolean notify_attios(gpointer user_data)
struct btd_device *device = user_data;
if (device->attrib == NULL)
- return FALSE;
+ return G_SOURCE_REMOVE;
g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
device->attios = g_slist_concat(device->attios, device->attios_offline);
device->attios_offline = NULL;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
guint btd_device_add_attio_callback(struct btd_device *device,
diff --git a/src/main.c b/src/main.c
index 91d90b4..d88b1d5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -298,7 +298,7 @@ void btd_exit(void)
static gboolean quit_eventloop(gpointer user_data)
{
btd_exit();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
@@ -446,7 +446,7 @@ static gboolean watchdog_callback(gpointer user_data)
{
sd_notify(0, "WATCHDOG=1");
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static gboolean parse_debug(const char *key, const char *value,
diff --git a/src/sdp-client.c b/src/sdp-client.c
index 51f3048..35e2bfd 100644
--- a/src/sdp-client.c
+++ b/src/sdp-client.c
@@ -59,7 +59,7 @@ static gboolean cached_session_expired(gpointer user_data)
g_free(cached);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
diff --git a/src/shared/hciemu.c b/src/shared/hciemu.c
index 0ea191f..5cae951 100644
--- a/src/shared/hciemu.c
+++ b/src/shared/hciemu.c
@@ -284,7 +284,7 @@ static gboolean start_stack(gpointer user_data)
bthost_start(hciemu->host_stack);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
struct hciemu *hciemu_new(enum hciemu_type type)
diff --git a/src/shared/tester.c b/src/shared/tester.c
index f3edd74..24d5bb9 100644
--- a/src/shared/tester.c
+++ b/src/shared/tester.c
@@ -311,7 +311,7 @@ static gboolean teardown_callback(gpointer user_data)
print_progress(test->name, COLOR_MAGENTA, "teardown");
test->teardown_func(test->test_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean test_timeout(gpointer user_data)
@@ -321,14 +321,14 @@ static gboolean test_timeout(gpointer user_data)
test->timeout_id = 0;
if (!test_current)
- return FALSE;
+ return G_SOURCE_REMOVE;
test->result = TEST_RESULT_TIMED_OUT;
print_progress(test->name, COLOR_RED, "test timed out");
g_idle_add(teardown_callback, test);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void next_test_case(void)
@@ -372,7 +372,7 @@ static gboolean setup_callback(gpointer user_data)
print_progress(test->name, COLOR_BLUE, "setup");
test->setup_func(test->test_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean run_callback(gpointer user_data)
@@ -384,7 +384,7 @@ static gboolean run_callback(gpointer user_data)
print_progress(test->name, COLOR_BLACK, "run");
test->test_func(test->test_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean done_callback(gpointer user_data)
@@ -396,7 +396,7 @@ static gboolean done_callback(gpointer user_data)
print_progress(test->name, COLOR_BLACK, "done");
next_test_case();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void tester_pre_setup_complete(void)
@@ -585,7 +585,7 @@ static gboolean start_tester(gpointer user_data)
next_test_case();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
struct wait_data {
@@ -605,7 +605,7 @@ static gboolean wait_callback(gpointer user_data)
if (wait->seconds > 0) {
print_progress(test->name, COLOR_BLACK, "%u seconds left",
wait->seconds);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
print_progress(test->name, COLOR_BLACK, "waiting done");
@@ -614,7 +614,7 @@ static gboolean wait_callback(gpointer user_data)
g_free(wait);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void tester_wait(unsigned int seconds, tester_wait_func_t func,
diff --git a/tools/btiotest.c b/tools/btiotest.c
index a77eba1..562de58 100644
--- a/tools/btiotest.c
+++ b/tools/btiotest.c
@@ -96,7 +96,7 @@ static gboolean disconn_timeout(gpointer user_data)
g_io_channel_shutdown(data->io, TRUE, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void update_sec_level(struct io_data *data)
@@ -199,7 +199,7 @@ static gboolean confirm_timeout(gpointer user_data)
if (data->reject >= 0) {
printf("Rejecting connection\n");
g_io_channel_shutdown(data->io, TRUE, NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
printf("Accepting connection\n");
@@ -215,7 +215,7 @@ static gboolean confirm_timeout(gpointer user_data)
io_data_unref(data);
}
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void confirm_cb(GIOChannel *io, gpointer user_data)
diff --git a/unit/test-gdbus-client.c b/unit/test-gdbus-client.c
index 685729a..6353b59 100644
--- a/unit/test-gdbus-client.c
+++ b/unit/test-gdbus-client.c
@@ -715,7 +715,7 @@ static gboolean timeout_test(gpointer user_data)
g_assert_not_reached();
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gboolean emit_string_change(void *user_data)
@@ -730,7 +730,7 @@ static gboolean emit_string_change(void *user_data)
context->timeout_source = g_timeout_add_seconds(2, timeout_test,
context);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void proxy_string_changed(GDBusProxy *proxy, void *user_data)
diff --git a/unit/test-gobex-transfer.c b/unit/test-gobex-transfer.c
index ef05047..171e663 100644
--- a/unit/test-gobex-transfer.c
+++ b/unit/test-gobex-transfer.c
@@ -464,7 +464,7 @@ static gboolean cancel_transfer(gpointer user_data)
if (d->id > 0)
g_obex_cancel_transfer(d->id, transfer_complete, user_data);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static gssize abort_data(void *buf, gsize len, gpointer user_data)
diff --git a/unit/test-gobex.c b/unit/test-gobex.c
index 66307c2..cd51009 100644
--- a/unit/test-gobex.c
+++ b/unit/test-gobex.c
@@ -92,13 +92,13 @@ static gboolean timeout(gpointer user_data)
GError **err = user_data;
if (!g_main_loop_is_running(mainloop))
- return FALSE;
+ return G_SOURCE_REMOVE;
g_set_error(err, TEST_ERROR, TEST_ERROR_TIMEOUT, "Timed out");
g_main_loop_quit(mainloop);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index 6d699e2..03046d7 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -209,7 +209,7 @@ static gboolean send_pdu(gpointer user_data)
g_assert(len == pdu_len);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void context_increment(struct context *context)
--
1.8.4.2
^ permalink raw reply related
* [PATCH 1/3] gdbus: Don't try to remove removed sources
From: Bastien Nocera @ 2013-11-09 17:02 UTC (permalink / raw)
To: linux-bluetooth
When we return FALSE from idle handlers, the source is removed.
This will be causing warnings in glib 2.40.
See https://bugzilla.gnome.org/show_bug.cgi?id=710724
---
gdbus/object.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/gdbus/object.c b/gdbus/object.c
index 268fed5..b248cbb 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1006,6 +1006,8 @@ static gboolean process_changes(gpointer user_data)
if (data->removed != NULL)
emit_interfaces_removed(data);
+ data->process_id = 0;
+
return FALSE;
}
@@ -1019,6 +1021,7 @@ static void generic_unregister(DBusConnection *connection, void *user_data)
if (data->process_id > 0) {
g_source_remove(data->process_id);
+ data->process_id = 0;
process_changes(data);
}
--
1.8.4.2
^ permalink raw reply related
* obexd broken for absolute paths
From: Bastien Nocera @ 2013-11-08 19:21 UTC (permalink / raw)
To: linux-bluetooth
Heya,
I was trying to test gnome-user-share's Bluetooth support for BlueZ 5,
and was quite surprised it didn't work one bit, with transfers failing
as soon as they were created.
I made this simple change to test/simple-obex-agent so you could
replicate the failure. Obviously, change the download path to exist on
your system:
- return properties['Name']
+ return ("%s/%s" % ("/home/hadess/Downloads/", properties['Name']))
This will see OBEX Push transfers fail as soon as accepted.
I must also mention the dreadful code in agent_reply() in obexd/src/manager.c:
const char *slash = strrchr(name, '/');
DBG("Agent replied with %s", name);
if (!slash) {
agent->new_name = g_strdup(name);
agent->new_folder = NULL;
} else {
agent->new_name = g_strdup(slash + 1);
agent->new_folder = g_strndup(name, slash - name);
}
Please use g_path_get_basename() and g_path_get_dirname(). This is even
a security issue because I could pass relative paths and expect some
system file to get overwritten.
Cheers
^ permalink raw reply
* How do you do OBEX testing?
From: Bastien Nocera @ 2013-11-08 16:11 UTC (permalink / raw)
To: linux-bluetooth
Heya,
I've been testing and fixing OBEX client and server side bugs for a
while, usually in GNOME, but I recently realised how outdated my testing
techniques were, relying on what would be described as "legacy"
phones[1].
I've asked this question on IRC, but I guess I might not be the only one
that needs to test things like this.
So, what do you use for testing ObexFTP, OBEX Push or related protocols
like PBAP?
- Adding an additional dongle on your computer and assigning it to a VM?
- Using software on a newer smartphone (seems Android has more of those
than the badly rated iOS ones)?
- Another computer? a dumb phone?
I'm looking for something that I could run on a laptop with minimal
additional hardware, so any hints would be welcome.
Cheers
[1]: a Sony Ericsson one that refuses to get out of demo mode because
the SIM slot is broken, and a 10-year candybar Nokia phone that can't
hold charge.
^ permalink raw reply
* [PATCH 3/3] android: Change TODO with explaining comment
From: Lukasz Rymanowski @ 2013-11-08 15:48 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, Lukasz Rymanowski
In-Reply-To: <1383925711-26374-1-git-send-email-lukasz.rymanowski@tieto.com>
---
android/adapter.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/android/adapter.c b/android/adapter.c
index cd820df..cb25954 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -509,7 +509,10 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
- /* TODO name and CoD of remote devices should probably be cached */
+ /* It is ok to have empty name and CoD of remote devices here since
+ * those information has been already provided on device_connected event
+ * or during device scaning. Android will use that instead.
+ */
memset(&hal_ev, 0, sizeof(hal_ev));
bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
@@ -522,7 +525,10 @@ static void send_ssp_request(const bdaddr_t *addr, uint8_t variant,
{
struct hal_ev_ssp_request ev;
- /* TODO name and CoD of remote devices should probably be cached */
+ /* It is ok to have empty name and CoD of remote devices here since
+ * those information has been already provided on device_connected event
+ * or during device scaning. Android will use that instead.
+ */
memset(&ev, 0, sizeof(ev));
bdaddr2android(addr, ev.bdaddr);
ev.pairing_variant = variant;
--
1.8.4
^ permalink raw reply related
* [PATCH 2/3] android: Update HAL with device info on incoming connection
From: Lukasz Rymanowski @ 2013-11-08 15:48 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, Lukasz Rymanowski
In-Reply-To: <1383925711-26374-1-git-send-email-lukasz.rymanowski@tieto.com>
Make sure Android have information about connecting remote device. This
is needed for example to show device name on incoming bonding request.
---
android/adapter.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/android/adapter.c b/android/adapter.c
index d63352e..cd820df 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -842,8 +842,9 @@ static void mgmt_device_connected_event(uint16_t index, uint16_t length,
return;
}
- /* TODO: Update device */
-
+ /* Update HAL library with device info */
+ update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false,
+ &ev->eir[0], btohs(ev->eir_len));
hal_ev.status = HAL_STATUS_SUCCESS;
hal_ev.state = HAL_ACL_STATE_CONNECTED;
bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
--
1.8.4
^ permalink raw reply related
* [PATCH 1/3] android: Update bond state on incoming bonding
From: Lukasz Rymanowski @ 2013-11-08 15:48 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, Lukasz Rymanowski
In-Reply-To: <1383925711-26374-1-git-send-email-lukasz.rymanowski@tieto.com>
Before sending any ssp request or pin code request up to HAL library we
need to send bond state change with bonding state. Otherwise incoming
bonding is impossible.
---
android/adapter.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/android/adapter.c b/android/adapter.c
index 65b3170..d63352e 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -501,6 +501,10 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
return;
}
+ /* Update bonding state */
+ send_bond_state_change(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+ HAL_BOND_STATE_BONDING);
+
ba2str(&ev->addr.bdaddr, dst);
DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
@@ -542,6 +546,10 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
ba2str(&ev->addr.bdaddr, dst);
DBG("%s confirm_hint %u", dst, ev->confirm_hint);
+ /* Update bonding state */
+ send_bond_state_change(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+ HAL_BOND_STATE_BONDING);
+
if (ev->confirm_hint)
send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0);
else
@@ -563,6 +571,10 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length,
ba2str(&ev->addr.bdaddr, dst);
DBG("%s", dst);
+ /* Update bonding state */
+ send_bond_state_change(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+ HAL_BOND_STATE_BONDING);
+
send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_ENTRY, 0);
}
@@ -581,9 +593,14 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
DBG("%s entered %u", dst, ev->entered);
/* HAL seems to not support entered characters */
- if (!ev->entered)
+ if (!ev->entered) {
+ /* Update bonding state */
+ send_bond_state_change(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
+ HAL_BOND_STATE_BONDING);
+
send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF,
ev->passkey);
+ }
}
static void mgmt_discovering_event(uint16_t index, uint16_t length,
@@ -827,8 +844,6 @@ static void mgmt_device_connected_event(uint16_t index, uint16_t length,
/* TODO: Update device */
- /* TODO: Check Set bonding state */
-
hal_ev.status = HAL_STATUS_SUCCESS;
hal_ev.state = HAL_ACL_STATE_CONNECTED;
bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
--
1.8.4
^ permalink raw reply related
* [PATCH 0/3] Fixes for incoming connection and bonding
From: Lukasz Rymanowski @ 2013-11-08 15:48 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, Lukasz Rymanowski
With those patches it is possible to bond from remote device and in addition
you will see the name of remote device on Android pairing request pop up.
Lukasz Rymanowski (3):
android: Update bond state on incoming bonding
android: Update HAL with device info on incoming connection
android: Change TODO with explaining comment
android/adapter.c | 36 +++++++++++++++++++++++++++++-------
1 file changed, 29 insertions(+), 7 deletions(-)
--
1.8.4
^ permalink raw reply
* [RFC v3 8/8] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
This is a temporary patch where user can manually enable or
disable BT 6LoWPAN functionality between devices.
Eventually the connection is established automatically if
the devices are advertising suitable capability and this patch
can be removed.
Before connecting the devices do this
echo 1 > /sys/kernel/debug/bluetooth/hci0/6lowpan
This enables 6LoWPAN support and creates the bt0 interface
automatically when devices are finally connected.
Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/bluetooth/6lowpan.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.h | 1 +
net/bluetooth/hci_core.c | 4 ++
3 files changed, 110 insertions(+)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index e70302a..a8891f5 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -26,6 +26,8 @@
*/
#include <linux/version.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
@@ -1607,6 +1609,109 @@ static struct notifier_block bt_6lowpan_dev_notifier = {
.notifier_call = device_event,
};
+static LIST_HEAD(user_enabled);
+DEFINE_RWLOCK(enabled_lock);
+
+struct lowpan_enabled {
+ __u8 dev_name[HCI_MAX_NAME_LENGTH];
+ bool enabled;
+ struct list_head list;
+};
+
+static int debugfs_show(struct seq_file *f, void *p)
+{
+ struct lowpan_enabled *entry, *tmp;
+ bool found = false;
+
+ write_lock(&enabled_lock);
+ list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+ seq_printf(f, "%d\n", entry->enabled);
+ found = true;
+ }
+ write_unlock(&enabled_lock);
+
+ if (!found)
+ seq_printf(f, "0\n");
+
+ return 0;
+}
+
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show, inode->i_private);
+}
+
+static ssize_t writer(struct file *fp, const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ struct hci_dev *hdev = fp->f_inode->i_private;
+ struct lowpan_enabled *entry = NULL, *tmp;
+ bool new_value, old_value;
+ char buf[3] = { 0 };
+ ssize_t ret;
+
+ BT_DBG("dev %s count %zd", hdev->dev_name, count);
+
+ ret = simple_write_to_buffer(buf, 2, position, user_buffer, count);
+ if (ret <= 0)
+ return ret;
+
+ if (strtobool(buf, &new_value) < 0)
+ return -EINVAL;
+
+ ret = -ENOENT;
+
+ write_lock(&enabled_lock);
+ list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+ if (!strncmp(entry->dev_name, hdev->dev_name,
+ HCI_MAX_NAME_LENGTH)) {
+ old_value = entry->enabled;
+ entry->enabled = new_value;
+ ret = 0;
+ break;
+ }
+ }
+ write_unlock(&enabled_lock);
+
+ if (ret == 0 && old_value == new_value)
+ return count;
+
+ if (ret < 0) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ strncpy(entry->dev_name, hdev->dev_name, HCI_MAX_NAME_LENGTH);
+ entry->enabled = new_value;
+
+ write_lock(&enabled_lock);
+ INIT_LIST_HEAD(&entry->list);
+ list_add(&entry->list, &user_enabled);
+ write_unlock(&enabled_lock);
+ }
+
+ if (new_value == true)
+ set_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+
+ return count;
+}
+
+static const struct file_operations ble_6lowpan_debugfs_fops = {
+ .open = debugfs_open,
+ .read = seq_read,
+ .write = writer,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void bt_6lowpan_add_debugfs(struct hci_dev *hdev)
+{
+ debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
+ &ble_6lowpan_debugfs_fops);
+}
+
int bt_6lowpan_init(void)
{
return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
index 680eac8..5f60cf2 100644
--- a/net/bluetooth/6lowpan.h
+++ b/net/bluetooth/6lowpan.h
@@ -22,5 +22,6 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn);
int bt_6lowpan_del_conn(struct l2cap_conn *conn);
int bt_6lowpan_init(void);
void bt_6lowpan_cleanup(void);
+void bt_6lowpan_add_debugfs(struct hci_dev *hdev);
#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 03e8355..a229ce0 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -34,6 +34,8 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "6lowpan.h"
+
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
static void hci_tx_work(struct work_struct *work);
@@ -1408,6 +1410,8 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &conn_max_interval_fops);
}
+ bt_6lowpan_add_debugfs(hdev);
+
return 0;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v3 7/8] Bluetooth: Set route to peer for 6LoWPAN
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/bluetooth/6lowpan.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index d33a32e..e70302a 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -1395,6 +1395,38 @@ static void set_dev_addr(struct net_device *net, bdaddr_t *addr,
net->dev_addr[0] ^= 2;
}
+static int add_peer_route(struct peer *dev, struct net_device *net,
+ bdaddr_t *addr, u8 addr_type)
+{
+ struct in6_addr peer_addr;
+ struct fib6_config cfg = {
+ .fc_table = RT6_TABLE_MAIN,
+ .fc_metric = IP6_RT_PRIO_ADDRCONF,
+ .fc_ifindex = net->ifindex,
+ .fc_dst_len = 128,
+ .fc_flags = RTF_ADDRCONF | RTF_UP |
+ RTF_PREF(ICMPV6_ROUTER_PREF_MEDIUM),
+ .fc_nlinfo.portid = 0,
+ .fc_nlinfo.nlh = NULL,
+ .fc_nlinfo.nl_net = dev_net(net),
+ };
+
+ memset(&peer_addr, 0, sizeof(struct in6_addr));
+
+ /* RFC 2464 ch. 5 */
+ peer_addr.s6_addr[0] = 0xFE;
+ peer_addr.s6_addr[1] = 0x80;
+ set_addr((u8 *)&peer_addr.s6_addr + 8, addr->b, addr_type);
+
+ memcpy(&dev->eui64_addr, (u8 *)&peer_addr.s6_addr + 8, EUI64_ADDR_LEN);
+
+ BT_DBG("peer address %pI6c", (u8 *)&peer_addr.s6_addr);
+
+ cfg.fc_dst = dev->peer_addr = peer_addr;
+
+ return ip6_route_add(&cfg);
+}
+
static void ifup(struct net_device *net)
{
int err;
@@ -1492,6 +1524,11 @@ add_peer:
peer_add(dev, peer);
write_unlock(&devices_lock);
+ err = add_peer_route(peer, dev->net, &conn->hcon->dst,
+ conn->hcon->dst_type);
+ if (err < 0)
+ BT_INFO("Setting route failed %d", err);
+
out:
return err;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v3 6/8] route: Exporting ip6_route_add() so that Bluetooth 6LoWPAN can use it
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/ipv6/route.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index c979dd9..d7c200f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1662,6 +1662,7 @@ out:
dst_free(&rt->dst);
return err;
}
+EXPORT_SYMBOL_GPL(ip6_route_add);
static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
{
--
1.8.3.1
^ permalink raw reply related
* [RFC v3 5/8] Bluetooth: Enable 6LoWPAN if device supports it
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_event.c | 3 +++
net/bluetooth/l2cap_core.c | 3 +++
4 files changed, 8 insertions(+)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 1784c48..f8f8447 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -131,6 +131,7 @@ enum {
HCI_PERIODIC_INQ,
HCI_FAST_CONNECTABLE,
HCI_BREDR_ENABLED,
+ HCI_6LOWPAN_ENABLED,
};
/* A mask for the flags that are supposed to remain when a reset happens
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 8c0ab3d..e689052 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -449,6 +449,7 @@ enum {
HCI_CONN_SSP_ENABLED,
HCI_CONN_POWER_SAVE,
HCI_CONN_REMOTE_OOB,
+ HCI_CONN_6LOWPAN,
};
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5fb3df6..5f81245 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+ if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+ set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+
hci_conn_add_sysfs(conn);
hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 155485f..df475c7 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6517,6 +6517,9 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
static bool is_bt_6lowpan(struct hci_conn *hcon)
{
+ if (hcon->type == LE_LINK && test_bit(HCI_CONN_6LOWPAN, &hcon->flags))
+ return true;
+
return false;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v3 4/8] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/bluetooth/6lowpan.c | 1039 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1037 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 2982a4e..d33a32e 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -11,6 +11,20 @@
GNU General Public License for more details.
*/
+/*
+ * The compression, uncompression and IPv6 packet code
+ * is from net/ieee802154/6lowpan.c with these copyrights
+ *
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ */
+
#include <linux/version.h>
#include <linux/bitops.h>
#include <linux/if_arp.h>
@@ -176,8 +190,578 @@ static inline void raw_dump_table(const char *caller, char *msg,
#endif /* DEBUG */
}
-static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
+static inline bool fetch_skb(struct sk_buff *skb,
+ void *data, const unsigned int len)
+{
+ if (unlikely(!pskb_may_pull(skb, len)))
+ return true;
+
+ skb_copy_from_linear_data(skb, data, len);
+ skb_pull(skb, len);
+
+ return false;
+}
+
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 address_mode,
+ const u8 *lladdr)
+{
+ bool fail;
+
+ switch (address_mode) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* for global link addresses */
+ fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* fe:80::XXXX:XXXX:XXXX:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+ break;
+ case LOWPAN_IPHC_ADDR_02:
+ /* fe:80::ff:fe00:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+ break;
+ case LOWPAN_IPHC_ADDR_03:
+ fail = false;
+ /* XXX: support only normal addr (IEEE802154_ADDR_LONG) atm */
+
+ /* fe:80::XXXX:XXXX:XXXX:XXXX
+ * \_________________/
+ * hwaddr
+ */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ memcpy(&ipaddr->s6_addr[8], lladdr, EUI64_ADDR_LEN);
+ break;
+ default:
+ pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/*
+ * Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 sam)
+{
+ switch (sam) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* unspec address ::
+ * Do nothing, address is already ::
+ */
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_02:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_03:
+ /* TODO */
+ netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+ return -EINVAL;
+ default:
+ pr_debug("Invalid sam value: 0x%x\n", sam);
+ return -EINVAL;
+ }
+
+ raw_dump_inline(NULL,
+ "Reconstructed context based ipv6 src addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/*
+ * This func is called when the packet from BT LE device
+ * needs to be sent upper layers.
+ */
+static int give_skb_to_upper(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct sk_buff *skb_cp;
+ int ret = NET_RX_SUCCESS;
+
+ skb_cp = skb_copy(skb, GFP_ATOMIC);
+ if (!skb_cp) {
+ ret = -ENOMEM;
+ } else {
+ ret = netif_rx(skb_cp);
+
+ BT_DBG("receive skb %d", ret);
+ if (ret < 0)
+ ret = NET_RX_DROP;
+ }
+
+ return ret;
+}
+
+static inline int fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 1)))
+ return -EINVAL;
+
+ *val = skb->data[0];
+ skb_pull(skb, 1);
+
+ return 0;
+}
+
+static inline int fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 2)))
+ return -EINVAL;
+
+ *val = (skb->data[0] << 8) | skb->data[1];
+ skb_pull(skb, 2);
+
+ return 0;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct net_device *dev)
+{
+ struct sk_buff *new;
+ int stat;
+
+ new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
+ GFP_ATOMIC);
+ kfree_skb(skb);
+
+ if (!new)
+ return -ENOMEM;
+
+ skb_push(new, sizeof(struct ipv6hdr));
+ skb_reset_network_header(new);
+ skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+
+ new->protocol = htons(ETH_P_IPV6);
+ new->pkt_type = PACKET_HOST;
+ new->dev = dev;
+
+ raw_dump_table(__func__,
+ "raw skb data dump before receiving",
+ new->data, new->len);
+
+ stat = give_skb_to_upper(new, dev);
+
+ kfree_skb(new);
+
+ return stat;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 dam)
+{
+ bool fail;
+
+ switch (dam) {
+ case LOWPAN_IPHC_DAM_00:
+ /* 00: 128 bits. The full address
+ * is carried in-line.
+ */
+ fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_DAM_01:
+ /* 01: 48 bits. The address takes
+ * the form ffXX::00XX:XXXX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+ break;
+ case LOWPAN_IPHC_DAM_10:
+ /* 10: 32 bits. The address takes
+ * the form ffXX::00XX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+ break;
+ case LOWPAN_IPHC_DAM_11:
+ /* 11: 8 bits. The address takes
+ * the form ff02::00XX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ ipaddr->s6_addr[1] = 0x02;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+ break;
+ default:
+ pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL,
+ "Reconstructed ipv6 multicast addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+static int
+uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+ u8 tmp;
+
+ if (!uh)
+ goto err;
+
+ if (fetch_skb_u8(skb, &tmp))
+ goto err;
+
+ if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+ pr_debug("UDP header uncompression\n");
+ switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+ case LOWPAN_NHC_UDP_CS_P_00:
+ memcpy(&uh->source, &skb->data[0], 2);
+ memcpy(&uh->dest, &skb->data[2], 2);
+ skb_pull(skb, 4);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_01:
+ memcpy(&uh->source, &skb->data[0], 2);
+ uh->dest =
+ skb->data[2] + LOWPAN_NHC_UDP_8BIT_PORT;
+ skb_pull(skb, 3);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_10:
+ uh->source = skb->data[0] + LOWPAN_NHC_UDP_8BIT_PORT;
+ memcpy(&uh->dest, &skb->data[1], 2);
+ skb_pull(skb, 3);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_11:
+ uh->source =
+ LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] >> 4);
+ uh->dest =
+ LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] & 0x0f);
+ skb_pull(skb, 1);
+ break;
+ default:
+ pr_debug("ERROR: unknown UDP format\n");
+ goto err;
+ break;
+ }
+
+ pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+ uh->source, uh->dest);
+
+ /* copy checksum */
+ memcpy(&uh->check, &skb->data[0], 2);
+ skb_pull(skb, 2);
+
+ /*
+ * UDP lenght needs to be infered from the lower layers
+ * here, we obtain the hint from the remaining size of the
+ * frame
+ */
+ uh->len = htons(skb->len + sizeof(struct udphdr));
+ pr_debug("uncompressed UDP length: src = %d", uh->len);
+ } else {
+ pr_debug("ERROR: unsupported NH format\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+static int process_data(struct sk_buff *skb, struct net_device *dev,
+ struct l2cap_conn *conn)
+{
+ struct ipv6hdr hdr = {};
+ u8 tmp, iphc0, iphc1, num_context = 0;
+ const u8 *_saddr = NULL, *_daddr = NULL;
+ struct lowpan_info *info;
+ struct peer *peer;
+ int err;
+
+ raw_dump_table(__func__, "raw skb data dump uncompressed",
+ skb->data, skb->len);
+
+ /* at least two bytes will be used for the encoding */
+ if (skb->len < 2)
+ goto drop;
+
+ if (fetch_skb_u8(skb, &iphc0))
+ goto drop;
+
+ info = lowpan_info(dev);
+
+ if (fetch_skb_u8(skb, &iphc1))
+ goto drop;
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_conn(info, conn);
+ write_unlock(&devices_lock);
+ if (!peer)
+ goto drop;
+
+ _saddr = peer->eui64_addr;
+ _daddr = info->net->dev_addr;
+
+ /* another if the CID flag is set */
+ if (iphc1 & LOWPAN_IPHC_CID) {
+ pr_debug("CID flag is set, increase header with one\n");
+ if (fetch_skb_u8(skb, &num_context))
+ goto drop;
+ }
+
+ hdr.version = 6;
+
+ /* Traffic Class and Flow Label */
+ switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+ /*
+ * Traffic Class and FLow Label carried in-line
+ * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+ */
+ case 0: /* 00b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+ skb_pull(skb, 3);
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+ (hdr.flow_lbl[0] & 0x0f);
+ break;
+ /*
+ * Traffic class carried in-line
+ * ECN + DSCP (1 byte), Flow Label is elided
+ */
+ case 1: /* 10b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+ break;
+ /*
+ * Flow Label carried in-line
+ * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+ */
+ case 2: /* 01b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+ memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+ skb_pull(skb, 2);
+ break;
+ /* Traffic Class and Flow Label are elided */
+ case 3: /* 11b */
+ break;
+ default:
+ break;
+ }
+
+ /* Next Header */
+ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+ /* Next header is carried inline */
+ if (fetch_skb_u8(skb, &(hdr.nexthdr)))
+ goto drop;
+
+ pr_debug("NH flag is set, next header carried inline: %02x\n",
+ hdr.nexthdr);
+ }
+
+ /* Hop Limit */
+ if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
+ hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+ else {
+ if (fetch_skb_u8(skb, &(hdr.hop_limit)))
+ goto drop;
+ }
+
+ /* Extract SAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+ if (iphc1 & LOWPAN_IPHC_SAC) {
+ /* Source address context based uncompression */
+ pr_debug("SAC bit is set. Handle context based source address.\n");
+ err = uncompress_context_based_src_addr(
+ skb, &hdr.saddr, tmp);
+ } else {
+ /* Source address uncompression */
+ pr_debug("source address stateless compression\n");
+ err = uncompress_addr(skb, &hdr.saddr, tmp, _saddr);
+ }
+
+ /* Check on error of previous branch */
+ if (err)
+ goto drop;
+
+ /* Extract DAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+ /* check for Multicast Compression */
+ if (iphc1 & LOWPAN_IPHC_M) {
+ if (iphc1 & LOWPAN_IPHC_DAC) {
+ pr_debug("dest: context-based mcast compression\n");
+ /* TODO: implement this */
+ } else {
+ err = lowpan_uncompress_multicast_daddr(
+ skb, &hdr.daddr, tmp);
+ if (err)
+ goto drop;
+ }
+ } else {
+ pr_debug("dest: stateless compression\n");
+ err = uncompress_addr(skb, &hdr.daddr, tmp, _daddr);
+ if (err)
+ goto drop;
+ }
+
+ /* UDP data uncompression */
+ if (iphc0 & LOWPAN_IPHC_NH_C) {
+ struct udphdr uh;
+ struct sk_buff *new;
+ if (uncompress_udp_header(skb, &uh))
+ goto drop;
+
+ /*
+ * replace the compressed UDP head by the uncompressed UDP
+ * header
+ */
+ new = skb_copy_expand(skb, sizeof(struct udphdr),
+ skb_tailroom(skb), GFP_ATOMIC);
+ kfree_skb(skb);
+
+ if (!new)
+ return -ENOMEM;
+
+ skb = new;
+
+ skb_push(skb, sizeof(struct udphdr));
+ skb_reset_transport_header(skb);
+ skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+ raw_dump_table(__func__, "raw UDP header dump",
+ (u8 *)&uh, sizeof(uh));
+
+ hdr.nexthdr = UIP_PROTO_UDP;
+ }
+
+ hdr.payload_len = htons(skb->len);
+
+ pr_debug("skb headroom size = %d, data length = %d\n",
+ skb_headroom(skb), skb->len);
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t"
+ "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+ hdr.hop_limit, &hdr.daddr);
+
+ raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+ sizeof(hdr));
+
+ return skb_deliver(skb, &hdr, dev);
+
+drop:
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
+ struct l2cap_conn *conn)
{
+ struct sk_buff *local_skb;
+
+ if (!netif_running(dev))
+ goto drop;
+
+ if (dev->type != ARPHRD_RAWIP)
+ goto drop;
+
+ raw_dump_table(__func__, "raw recv dump", skb->head,
+ skb->len);
+
+ /* check that it's our buffer */
+ if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+ /* Copy the packet so that the IPv6 header is
+ * properly aligned.
+ */
+ local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
+ skb_tailroom(skb), GFP_ATOMIC);
+ if (!local_skb)
+ goto drop;
+
+ local_skb->protocol = htons(ETH_P_IPV6);
+ local_skb->pkt_type = PACKET_HOST;
+
+ skb_reset_network_header(local_skb);
+ skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
+
+ if (give_skb_to_upper(local_skb,
+ dev) != NET_RX_SUCCESS) {
+ kfree_skb(local_skb);
+ goto drop;
+ }
+
+ dev->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+
+ kfree_skb(local_skb);
+ kfree_skb(skb);
+ } else {
+ switch (skb->data[0] & 0xe0) {
+ case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
+ local_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!local_skb)
+ goto drop;
+ if (process_data(local_skb, dev, conn)
+ != NET_RX_SUCCESS)
+ goto drop;
+
+ dev->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+
+ kfree_skb(skb);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NET_RX_SUCCESS;
+
+drop:
kfree_skb(skb);
return NET_RX_DROP;
}
@@ -206,19 +790,465 @@ static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
skb->priority);
+ hci_send_acl(conn->hchan, skb, ACL_START);
return;
}
+static inline int skbuff_copy(void *msg, int len,
+ int count, int mtu,
+ struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct sk_buff **frag;
+ int sent = 0;
+
+ memcpy(skb_put(skb, count), msg, count);
+
+ sent += count;
+ msg += count;
+ len -= count;
+
+ dev->stats.tx_bytes += count;
+ dev->stats.tx_packets++;
+
+ raw_dump_table(__func__, "Sending", skb->data, skb->len);
+
+ /* Continuation fragments (no L2CAP header) */
+ frag = &skb_shinfo(skb)->frag_list;
+ while (len > 0) {
+ struct sk_buff *tmp;
+
+ count = min_t(unsigned int, mtu, len);
+
+ tmp = bt_skb_alloc(count, GFP_KERNEL);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ *frag = tmp;
+
+ memcpy(skb_put(*frag, count), msg, count);
+
+ raw_dump_table(__func__, "Sending fragment",
+ (*frag)->data, count);
+
+ (*frag)->priority = skb->priority;
+
+ sent += count;
+ msg += count;
+ len -= count;
+
+ skb->len += (*frag)->len;
+ skb->data_len += (*frag)->len;
+
+ frag = &(*frag)->next;
+
+ dev->stats.tx_bytes += count;
+ dev->stats.tx_packets++;
+ }
+
+ return sent;
+}
+
+static struct sk_buff *create_pdu(struct l2cap_conn *conn,
+ void *msg, size_t len,
+ u32 priority,
+ struct net_device *dev)
+{
+ struct sk_buff *skb;
+ int err, count;
+ struct l2cap_hdr *lh;
+
+ if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+ /* XXX: This should be not needed and atm is only used for
+ * testing purposes */
+ conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE;
+
+ count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
+
+ BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count);
+
+ skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_KERNEL);
+ if (IS_ERR(skb))
+ return skb;
+
+ skb->priority = priority;
+
+ lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN);
+ lh->len = cpu_to_le16(len);
+
+ err = skbuff_copy(msg, len, count, conn->mtu, skb, dev);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ BT_DBG("skbuff copy %d failed", err);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
static int conn_send(struct l2cap_conn *conn,
void *msg, size_t len, u32 priority,
struct net_device *dev)
{
- struct sk_buff *skb = {0};
+ struct sk_buff *skb;
+
+ skb = create_pdu(conn, msg, len, priority, dev);
+ if (IS_ERR(skb))
+ return -EINVAL;
do_send(conn, skb);
return 0;
}
+static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
+ const struct in6_addr *ipaddr,
+ const unsigned char *lladdr)
+{
+ u8 val = 0;
+
+ if (is_addr_mac_addr_based(ipaddr, lladdr))
+ val = 3; /* 0-bits */
+ else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+ /* compress IID to 16 bits xxxx::XXXX */
+ memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
+ *hc06_ptr += 2;
+ val = 2; /* 16-bits */
+ } else {
+ /* do not compress IID => xxxx::IID */
+ memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+ *hc06_ptr += 8;
+ val = 1; /* 64-bits */
+ }
+
+ return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT) &&
+ ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT)) {
+ pr_debug("UDP header: both ports compression to 4 bits\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_11;
+ **(hc06_ptr + 1) = /* subtraction is faster */
+ (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) +
+ ((uh->source & LOWPAN_NHC_UDP_4BIT_PORT) << 4));
+ *hc06_ptr += 2;
+ } else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of dest\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_01;
+ memcpy(*hc06_ptr + 1, &uh->source, 2);
+ **(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT);
+ *hc06_ptr += 4;
+ } else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of source\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_10;
+ memcpy(*hc06_ptr + 1, &uh->dest, 2);
+ **(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT);
+ *hc06_ptr += 4;
+ } else {
+ pr_debug("UDP header: can't compress\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_00;
+ memcpy(*hc06_ptr + 1, &uh->source, 2);
+ memcpy(*hc06_ptr + 3, &uh->dest, 2);
+ *hc06_ptr += 5;
+ }
+
+ /* checksum is always inline */
+ memcpy(*hc06_ptr, &uh->check, 2);
+ *hc06_ptr += 2;
+
+ /* skip the UDP header */
+ skb_pull(skb, sizeof(struct udphdr));
+}
+
+static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 1)))
+ return -EINVAL;
+
+ *val = skb->data[0];
+ skb_pull(skb, 1);
+
+ return 0;
+}
+
+static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 2)))
+ return -EINVAL;
+
+ *val = (skb->data[0] << 8) | skb->data[1];
+ skb_pull(skb, 2);
+
+ return 0;
+}
+
+static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
+ bdaddr_t *addr, u8 *addr_type)
+{
+ u8 *eui64;
+
+ eui64 = ip6_daddr->s6_addr + 8;
+
+ addr->b[0] = eui64[7];
+ addr->b[1] = eui64[6];
+ addr->b[2] = eui64[5];
+ addr->b[3] = eui64[2];
+ addr->b[4] = eui64[1];
+ addr->b[5] = eui64[0];
+
+ addr->b[5] ^= 2;
+
+ /* Set universal/local bit to 0 */
+ if (addr->b[5] & 1) {
+ addr->b[5] &= ~1;
+ *addr_type = BDADDR_LE_PUBLIC;
+ } else
+ *addr_type = BDADDR_LE_RANDOM;
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *_daddr,
+ const void *_saddr, unsigned int len)
+{
+ u8 tmp, iphc0, iphc1, *hc06_ptr;
+ struct lowpan_info *info;
+ struct peer *peer;
+ struct ipv6hdr *hdr;
+ bdaddr_t addr, *any = BDADDR_ANY;
+ u8 *saddr, *daddr = any->b;
+ u8 addr_type;
+ u8 head[100];
+
+ if (type != ETH_P_IPV6)
+ return -EINVAL;
+
+ hdr = ipv6_hdr(skb);
+ hc06_ptr = head + 2;
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n"
+ "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+ hdr->hop_limit, &hdr->daddr);
+
+ raw_dump_table(__func__, "raw skb network header dump",
+ skb_network_header(skb), sizeof(struct ipv6hdr));
+
+ info = lowpan_info(dev);
+
+ if (ipv6_addr_is_multicast(&hdr->daddr)) {
+ memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+ sizeof(struct in6_addr));
+ lowpan_cb(skb)->conn = NULL;
+ } else {
+ /*
+ * Get destination BT device from skb.
+ * If there is no such peer then discard the packet.
+ */
+ get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+
+ BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_ba(info, addr_type, &addr);
+ write_unlock(&devices_lock);
+
+ if (!peer) {
+ BT_DBG("no such peer %pMR found", &addr);
+ return -ENOENT;
+ }
+
+ daddr = peer->eui64_addr;
+
+ memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+ sizeof(struct in6_addr));
+ lowpan_cb(skb)->conn = peer->conn;
+ }
+
+ saddr = info->net->dev_addr;
+
+ raw_dump_inline(__func__, "saddr",
+ (unsigned char *)saddr, 8);
+
+ /*
+ * As we copy some bit-length fields, in the IPHC encoding bytes,
+ * we sometimes use |=
+ * If the field is 0, and the current bit value in memory is 1,
+ * this does not work. We therefore reset the IPHC encoding here
+ */
+ iphc0 = LOWPAN_DISPATCH_IPHC;
+ iphc1 = 0;
+
+ /* TODO: context lookup */
+
+ raw_dump_inline(__func__, "daddr",
+ (unsigned char *)daddr, 8);
+
+ raw_dump_table(__func__,
+ "sending raw skb network uncompressed packet",
+ skb->data, skb->len);
+
+ /*
+ * Traffic class, flow label
+ * If flow label is 0, compress it. If traffic class is 0, compress it
+ * We have to process both in the same time as the offset of traffic
+ * class depends on the presence of version and flow label
+ */
+
+ /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
+ tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+ tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+ if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+ (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+ /* flow label can be compressed */
+ iphc0 |= LOWPAN_IPHC_FL_C;
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress (elide) all */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ } else {
+ /* compress only the flow label */
+ *hc06_ptr = tmp;
+ hc06_ptr += 1;
+ }
+ } else {
+ /* Flow label cannot be compressed */
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress only traffic class */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+ memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
+ hc06_ptr += 3;
+ } else {
+ /* compress nothing */
+ memcpy(hc06_ptr, &hdr, 4);
+ /* replace the top byte with new ECN | DSCP format */
+ *hc06_ptr = tmp;
+ hc06_ptr += 4;
+ }
+ }
+
+ /* NOTE: payload length is always compressed */
+
+ /* Next Header is compress if UDP */
+ if (hdr->nexthdr == UIP_PROTO_UDP)
+ iphc0 |= LOWPAN_IPHC_NH_C;
+
+ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+ *hc06_ptr = hdr->nexthdr;
+ hc06_ptr += 1;
+ }
+
+ /*
+ * Hop limit
+ * if 1: compress, encoding is 01
+ * if 64: compress, encoding is 10
+ * if 255: compress, encoding is 11
+ * else do not compress
+ */
+ switch (hdr->hop_limit) {
+ case 1:
+ iphc0 |= LOWPAN_IPHC_TTL_1;
+ break;
+ case 64:
+ iphc0 |= LOWPAN_IPHC_TTL_64;
+ break;
+ case 255:
+ iphc0 |= LOWPAN_IPHC_TTL_255;
+ break;
+ default:
+ *hc06_ptr = hdr->hop_limit;
+ hc06_ptr += 1;
+ break;
+ }
+
+ /* source address compression */
+ if (is_addr_unspecified(&hdr->saddr)) {
+ pr_debug("source address is unspecified, setting SAC\n");
+ iphc1 |= LOWPAN_IPHC_SAC;
+ /* TODO: context lookup */
+ } else if (is_addr_link_local(&hdr->saddr)) {
+ pr_debug("source address is link-local\n");
+ iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+ LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr);
+ } else {
+ pr_debug("send the full source address\n");
+ memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
+ hc06_ptr += 16;
+ }
+
+ /* destination address compression */
+ if (is_addr_mcast(&hdr->daddr)) {
+ pr_debug("destination address is multicast: ");
+ iphc1 |= LOWPAN_IPHC_M;
+ if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+ pr_debug("compressed to 1 octet\n");
+ iphc1 |= LOWPAN_IPHC_DAM_11;
+ /* use last byte */
+ *hc06_ptr = hdr->daddr.s6_addr[15];
+ hc06_ptr += 1;
+ } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+ pr_debug("compressed to 4 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_10;
+ /* second byte + the last three */
+ *hc06_ptr = hdr->daddr.s6_addr[1];
+ memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
+ hc06_ptr += 4;
+ } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+ pr_debug("compressed to 6 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_01;
+ /* second byte + the last five */
+ *hc06_ptr = hdr->daddr.s6_addr[1];
+ memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
+ hc06_ptr += 6;
+ } else {
+ pr_debug("using full address\n");
+ iphc1 |= LOWPAN_IPHC_DAM_00;
+ memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
+ hc06_ptr += 16;
+ }
+ } else {
+ /* TODO: context lookup */
+ if (is_addr_link_local(&hdr->daddr)) {
+ pr_debug("dest address is unicast and link-local\n");
+ iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+ LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr);
+ } else {
+ pr_debug("dest address is unicast: using full one\n");
+ memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
+ hc06_ptr += 16;
+ }
+ }
+
+ /* UDP header compression */
+ if (hdr->nexthdr == UIP_PROTO_UDP)
+ compress_udp_header(&hc06_ptr, skb);
+
+ head[0] = iphc0;
+ head[1] = iphc1;
+
+ skb_pull(skb, sizeof(struct ipv6hdr));
+
+ memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+
+ BT_DBG("header len %d skb %u", (int)(hc06_ptr - head), skb->len);
+
+ skb_reset_network_header(skb);
+
+ raw_dump_table(__func__, "raw skb data dump compressed",
+ skb->data, skb->len);
+ return 0;
+}
+
/* Packet to BT LE device */
static int send_pkt(struct l2cap_conn *conn, const void *saddr,
const void *daddr, struct sk_buff *skb,
@@ -309,6 +1339,10 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = xmit,
};
+static struct header_ops header_ops = {
+ .create = header_create,
+};
+
static void netdev_setup(struct net_device *dev)
{
dev->addr_len = EUI64_ADDR_LEN;
@@ -322,6 +1356,7 @@ static void netdev_setup(struct net_device *dev)
dev->watchdog_timeo = 0;
dev->netdev_ops = &netdev_ops;
+ dev->header_ops = &header_ops;
dev->destructor = free_netdev;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v3 3/8] Bluetooth: Initial skeleton code for BT 6LoWPAN
From: Jukka Rissanen @ 2013-11-08 15:23 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1383924195-7936-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/6lowpan.c | 546 ++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.h | 26 ++
net/bluetooth/Makefile | 2 +-
net/bluetooth/l2cap_core.c | 22 +-
5 files changed, 595 insertions(+), 2 deletions(-)
create mode 100644 net/bluetooth/6lowpan.c
create mode 100644 net/bluetooth/6lowpan.h
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 5132990..c28ac0d 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -133,6 +133,7 @@ struct l2cap_conninfo {
#define L2CAP_FC_L2CAP 0x02
#define L2CAP_FC_CONNLESS 0x04
#define L2CAP_FC_A2MP 0x08
+#define L2CAP_FC_6LOWPAN 0x3e
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
new file mode 100644
index 0000000..2982a4e
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,546 @@
+/*
+ Copyright (c) 2013 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ 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.
+*/
+
+#include <linux/version.h>
+#include <linux/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <net/af_ieee802154.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "../ieee802154/6lowpan.h" /* for the compression defines */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+#define EUI64_ADDR_LEN 8
+
+struct skb_cb {
+ struct in6_addr addr;
+ struct l2cap_conn *conn;
+};
+#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
+
+/*
+ * The devices list contains those devices that we are acting
+ * as a proxy. The BT 6LoWPAN device is a virtual device that
+ * connects to the Bluetooth LE device. The real connection to
+ * BT device is done via l2cap layer. There exists one
+ * virtual device / one BT 6LoWPAN network (=hciX device).
+ * The list contains struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+static DEFINE_RWLOCK(devices_lock);
+
+struct lowpan_dev {
+ struct net_device *dev;
+ struct work_struct delete_netdev;
+ struct list_head list;
+};
+
+struct peer {
+ struct list_head list;
+ struct l2cap_conn *conn;
+
+ /* peer addresses in various formats */
+ unsigned char eui64_addr[EUI64_ADDR_LEN];
+ struct in6_addr peer_addr;
+};
+
+struct lowpan_info {
+ struct net_device *net;
+ struct list_head peers;
+ int peer_count;
+};
+
+static inline struct lowpan_info *lowpan_info(const struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+static inline void peer_add(struct lowpan_info *info, struct peer *peer)
+{
+ list_add(&peer->list, &info->peers);
+ info->peer_count++;
+}
+
+static inline void peer_del(struct lowpan_info *info, struct peer *peer)
+{
+ list_del(&peer->list);
+ info->peer_count--;
+ if (info->peer_count < 0) {
+ BT_ERR("peer count underflow");
+ info->peer_count = 0;
+ }
+}
+
+static inline struct peer *peer_lookup_ba(struct lowpan_info *info,
+ __u8 type, bdaddr_t *ba)
+{
+ struct peer *peer, *tmp;
+
+ BT_DBG("peers %d addr %pMR type %d", info->peer_count, ba, type);
+
+ list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+ BT_DBG("addr %pMR type %d",
+ &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+
+ if (!bacmp(&peer->conn->hcon->dst, ba))
+ return peer;
+ }
+
+ return NULL;
+}
+
+static inline struct peer *peer_lookup_conn(struct lowpan_info *info,
+ struct l2cap_conn *conn)
+{
+ struct peer *peer, *tmp;
+
+ list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+ if (peer->conn == conn)
+ return peer;
+ }
+
+ return NULL;
+}
+
+static struct peer *lookup_peer(struct l2cap_conn *conn,
+ struct lowpan_info **dev)
+{
+ struct lowpan_dev *entry, *tmp;
+ struct peer *peer = NULL;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ struct lowpan_info *info = lowpan_info(entry->dev);
+
+ if (dev)
+ *dev = info;
+
+ peer = peer_lookup_conn(info, conn);
+ if (peer)
+ break;
+ }
+
+ write_unlock(&devices_lock);
+
+ return peer;
+}
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+ unsigned char *buf, int len)
+{
+#ifdef DEBUG
+ if (msg)
+ pr_debug("%s():%s: ", caller, msg);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE,
+ 16, 1, buf, len, false);
+#endif /* DEBUG */
+}
+
+/*
+ * print data in a table format:
+ *
+ * addr: xx xx xx xx xx xx
+ * addr: xx xx xx xx xx xx
+ * ...
+ */
+static inline void raw_dump_table(const char *caller, char *msg,
+ unsigned char *buf, int len)
+{
+#ifdef DEBUG
+ if (msg)
+ pr_debug("%s():%s:\n", caller, msg);
+ print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, len, false);
+#endif /* DEBUG */
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct lowpan_info *info = NULL;
+ struct peer *peer;
+ int err = -ENOENT;
+
+ peer = lookup_peer(conn, &info);
+ if (!peer)
+ return -ENOENT;
+
+ if (info && info->net) {
+ err = recv_pkt(skb, info->net, conn);
+ BT_DBG("recv pkt %d", err);
+ }
+
+ return err;
+}
+
+static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
+ skb->priority);
+
+ return;
+}
+
+static int conn_send(struct l2cap_conn *conn,
+ void *msg, size_t len, u32 priority,
+ struct net_device *dev)
+{
+ struct sk_buff *skb = {0};
+
+ do_send(conn, skb);
+ return 0;
+}
+
+/* Packet to BT LE device */
+static int send_pkt(struct l2cap_conn *conn, const void *saddr,
+ const void *daddr, struct sk_buff *skb,
+ struct net_device *dev)
+{
+ raw_dump_table(__func__, "raw skb data dump before fragmentation",
+ skb->data, skb->len);
+
+ return conn_send(conn, skb->data, skb->len, 0, dev);
+}
+
+static int send_mcast_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sk_buff *local_skb;
+ struct lowpan_dev *entry, *tmp;
+ int err = 0;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ struct peer *pentry, *ptmp;
+ struct lowpan_info *info;
+
+ if (entry->dev != dev)
+ continue;
+
+ info = lowpan_info(entry->dev);
+
+ list_for_each_entry_safe(pentry, ptmp, &info->peers, list) {
+ local_skb = skb_clone(skb, GFP_ATOMIC);
+
+ err = send_pkt(pentry->conn, dev->dev_addr,
+ pentry->eui64_addr,
+ local_skb, dev);
+
+ kfree_skb(local_skb);
+ }
+ }
+
+ write_unlock(&devices_lock);
+
+ return err;
+}
+
+static netdev_tx_t xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int err = -ENOENT;
+ unsigned char *eui64_addr;
+ struct lowpan_info *info;
+ struct peer *peer;
+ bdaddr_t addr;
+ u8 addr_type;
+
+ if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
+ /*
+ * We need to send the packet to every device
+ * behind this interface.
+ */
+ err = send_mcast_pkt(skb, dev);
+ } else {
+ get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+ eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
+ info = lowpan_info(dev);
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_ba(info, addr_type, &addr);
+ write_unlock(&devices_lock);
+
+ BT_DBG("xmit from %s to %pMR (%pI6c), peer %p", dev->name,
+ &addr, &lowpan_cb(skb)->addr, peer);
+
+ if (peer && peer->conn)
+ err = send_pkt(peer->conn,
+ dev->dev_addr,
+ eui64_addr,
+ skb,
+ dev);
+ }
+ dev_kfree_skb(skb);
+
+ if (err)
+ BT_DBG("ERROR: xmit failed (%d)", err);
+
+ return (err < 0) ? NET_XMIT_DROP : err;
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_start_xmit = xmit,
+};
+
+static void netdev_setup(struct net_device *dev)
+{
+ dev->addr_len = EUI64_ADDR_LEN;
+ dev->type = ARPHRD_RAWIP;
+
+ dev->hard_header_len = 0;
+ dev->needed_tailroom = 0;
+ dev->mtu = IPV6_MIN_MTU;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_RUNNING | IFF_POINTOPOINT;
+ dev->watchdog_timeo = 0;
+
+ dev->netdev_ops = &netdev_ops;
+ dev->destructor = free_netdev;
+}
+
+static struct device_type bt_type = {
+ .name = "bluetooth",
+};
+
+static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
+{
+ /* addr is the BT address in little-endian format */
+ eui[0] = addr[5];
+ eui[1] = addr[4];
+ eui[2] = addr[3];
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[5] = addr[2];
+ eui[6] = addr[1];
+ eui[7] = addr[0];
+
+ eui[0] ^= 2;
+
+ /*
+ * Universal/local bit set, RFC 4291
+ */
+ if (addr_type == BDADDR_LE_PUBLIC)
+ eui[0] |= 1;
+ else
+ eui[0] &= ~1;
+}
+
+static void set_dev_addr(struct net_device *net, bdaddr_t *addr,
+ u8 addr_type)
+{
+ net->addr_assign_type = NET_ADDR_PERM;
+ set_addr(net->dev_addr, addr->b, addr_type);
+ net->dev_addr[0] ^= 2;
+}
+
+static void ifup(struct net_device *net)
+{
+ int err;
+
+ rtnl_lock();
+ err = dev_open(net);
+ if (err < 0)
+ BT_INFO("iface %s cannot be opened (%d)", net->name, err);
+ rtnl_unlock();
+}
+
+/*
+ * This gets called when BT LE 6LoWPAN device is connected. We then
+ * create network device that acts as a proxy between BT LE device
+ * and kernel network stack.
+ */
+int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+ struct lowpan_info *dev = NULL;
+ struct peer *peer = NULL;
+ struct net_device *net;
+ struct lowpan_dev *entry;
+ struct inet6_dev *idev;
+ int err;
+
+ peer = lookup_peer(conn, &dev);
+ if (peer)
+ return -EEXIST;
+
+ /*
+ * If net device exists already, just add route.
+ */
+ if (dev && !peer)
+ goto add_peer;
+
+ net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
+ netdev_setup);
+ if (!net)
+ return -ENOMEM;
+
+ dev = netdev_priv(net);
+ dev->net = net;
+ INIT_LIST_HEAD(&dev->peers);
+
+ set_dev_addr(net, &conn->hcon->src, conn->hcon->src_type);
+
+ net->netdev_ops = &netdev_ops;
+ SET_NETDEV_DEV(net, &conn->hcon->dev);
+ SET_NETDEV_DEVTYPE(net, &bt_type);
+
+ err = register_netdev(net);
+ if (err < 0) {
+ BT_INFO("register_netdev failed %d", err);
+ free_netdev(net);
+ goto out;
+ }
+
+ BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+ net->ifindex, &conn->hcon->dst, &conn->hcon->src);
+ set_bit(__LINK_STATE_PRESENT, &net->state);
+
+ idev = in6_dev_get(net);
+ if (idev) {
+ idev->cnf.autoconf = 1;
+ idev->cnf.forwarding = 1;
+ idev->cnf.accept_ra = 2;
+
+ in6_dev_put(idev);
+ }
+
+ entry = kzalloc(sizeof(struct lowpan_dev), GFP_KERNEL);
+ if (!entry) {
+ unregister_netdev(net);
+ return -ENOMEM;
+ }
+
+ entry->dev = net;
+
+ write_lock(&devices_lock);
+ INIT_LIST_HEAD(&entry->list);
+ list_add(&entry->list, &bt_6lowpan_devices);
+ write_unlock(&devices_lock);
+
+ ifup(net);
+
+add_peer:
+ peer = kzalloc(sizeof(struct peer), GFP_KERNEL);
+ if (!peer)
+ return -ENOMEM;
+
+ peer->conn = conn;
+
+ write_lock(&devices_lock);
+ INIT_LIST_HEAD(&peer->list);
+ peer_add(dev, peer);
+ write_unlock(&devices_lock);
+
+out:
+ return err;
+}
+
+static void delete_netdev(struct work_struct *work)
+{
+ struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+ delete_netdev);
+
+ unregister_netdev(entry->dev);
+
+ /* The entry pointer is deleted in device_event() */
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+ struct lowpan_dev *entry, *tmp;
+ struct lowpan_info *info = NULL;
+ struct peer *peer;
+ int err = -ENOENT;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ info = lowpan_info(entry->dev);
+ peer = peer_lookup_conn(info, conn);
+ if (peer) {
+ peer_del(info, peer);
+ err = 0;
+ break;
+ }
+ }
+
+ write_unlock(&devices_lock);
+
+ if (!err && info && info->peer_count == 0) {
+ /*
+ * This function is called with hci dev lock held which means
+ * that we must delete the netdevice in worker thread.
+ */
+ INIT_WORK(&entry->delete_netdev, delete_netdev);
+ schedule_work(&entry->delete_netdev);
+ }
+
+ return err;
+}
+
+static int device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct lowpan_dev *entry, *tmp;
+
+ if (dev->type != ARPHRD_RAWIP)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ write_lock(&devices_lock);
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+ list) {
+ if (entry->dev == dev) {
+ list_del(&entry->list);
+ kfree(entry);
+ break;
+ }
+ }
+ write_unlock(&devices_lock);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block bt_6lowpan_dev_notifier = {
+ .notifier_call = device_event,
+};
+
+int bt_6lowpan_init(void)
+{
+ return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
+
+void bt_6lowpan_cleanup(void)
+{
+ unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
new file mode 100644
index 0000000..680eac8
--- /dev/null
+++ b/net/bluetooth/6lowpan.h
@@ -0,0 +1,26 @@
+/*
+ Copyright (c) 2013 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ 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.
+*/
+
+#ifndef __6LOWPAN_H
+#define __6LOWPAN_H
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/l2cap.h>
+
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
+int bt_6lowpan_add_conn(struct l2cap_conn *conn);
+int bt_6lowpan_del_conn(struct l2cap_conn *conn);
+int bt_6lowpan_init(void);
+void bt_6lowpan_cleanup(void);
+
+#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 6a791e7..80cb215 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,6 @@ obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
- a2mp.o amp.o
+ a2mp.o amp.o 6lowpan.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4af3821..155485f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -40,6 +40,7 @@
#include "smp.h"
#include "a2mp.h"
#include "amp.h"
+#include "6lowpan.h"
bool disable_ertm;
@@ -6473,6 +6474,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conn_del(conn->hcon, EACCES);
break;
+ case L2CAP_FC_6LOWPAN:
+ bt_6lowpan_recv(conn, skb);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
@@ -6510,6 +6515,11 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
return exact ? lm1 : lm2;
}
+static bool is_bt_6lowpan(struct hci_conn *hcon)
+{
+ return false;
+}
+
void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn;
@@ -6518,8 +6528,12 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
if (!status) {
conn = l2cap_conn_add(hcon);
- if (conn)
+ if (conn) {
l2cap_conn_ready(conn);
+
+ if (is_bt_6lowpan(hcon))
+ bt_6lowpan_add_conn(conn);
+ }
} else {
l2cap_conn_del(hcon, bt_to_errno(status));
}
@@ -6540,6 +6554,9 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
+ if (is_bt_6lowpan(hcon))
+ bt_6lowpan_del_conn(hcon->l2cap_data);
+
l2cap_conn_del(hcon, bt_to_errno(reason));
}
@@ -6817,11 +6834,14 @@ int __init l2cap_init(void)
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
NULL, &l2cap_debugfs_fops);
+ bt_6lowpan_init();
+
return 0;
}
void l2cap_exit(void)
{
+ bt_6lowpan_cleanup();
debugfs_remove(l2cap_debugfs);
l2cap_cleanup_sockets();
}
--
1.8.3.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox