Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v3 0/5] Add new UUID utility functions and use them in GATT
From: Elvis Pfützenreuter @ 2011-03-14 18:53 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: epx

This series creates new UUID utility functions and, at this momemt, puts
them into use in GATT/Low Energy code. Major differences between 'old'
and 'new' UUID functions are:

* UUID is stored in host order, and converted to/from network or
  Bluetooth order upon use.
* in bt_uuid{16,32,128}_create, raw numeric value (uint16, uint32 or
  uint128) is always passed by value.
* sdp_* prefix is replaced by bt_* for generality.

Types and macros needed by both "old" and "new" UUID functions as well as
their clients are moved to lib/bluetooth.h. This includes ntoh64(),
ntoh128() and friends.

Claudio Takahasi (1):
  Add new UUID utility functions

Elvis Pfützenreuter (4):
  Add more functions for new UUID handling
  Use new UUID functions in GATT
  Use new UUID functions in example GATT server
  Add "unit test" for new UUID functions

 Makefile.am          |    6 +-
 Makefile.tools       |    6 +-
 attrib/att.c         |   47 +++-----
 attrib/att.h         |   69 ++++++++++-
 attrib/client.c      |   26 ++---
 attrib/example.c     |   92 ++++++++-------
 attrib/gatt.c        |   68 ++++++-----
 attrib/gatt.h        |    4 +-
 attrib/gattrib.c     |    3 +-
 attrib/gatttool.c    |   18 ++--
 attrib/interactive.c |   22 ++--
 attrib/utils.c       |    1 +
 health/mcap_sync.c   |   15 ---
 lib/bluetooth.h      |   54 +++++++++
 lib/sdp.c            |   43 -------
 lib/sdp.h            |    4 +-
 lib/uuid.c           |  274 +++++++++++++++++++++++++++++++++++++++++++
 lib/uuid.h           |   65 ++++++++++
 src/adapter.c        |    1 +
 src/attrib-server.c  |   93 ++++++++-------
 src/attrib-server.h  |    4 +-
 src/device.c         |    1 +
 src/main.c           |    1 +
 test/hciemu.c        |   16 ---
 test/uuidtest.c      |  319 ++++++++++++++++++++++++++++++++++++++++++++++++++
 25 files changed, 971 insertions(+), 281 deletions(-)
 create mode 100644 lib/uuid.c
 create mode 100644 lib/uuid.h
 create mode 100644 test/uuidtest.c


^ permalink raw reply

* Re: [PATCH 1/4] Fix the behaviour of the Attribute API Discover method
From: Vinicius Costa Gomes @ 2011-03-14 18:13 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1299865982-13601-1-git-send-email-vinicius.gomes@openbossa.org>

Hi Johan,

On 14:52 Fri 11 Mar, Vinicius Costa Gomes wrote:
> This method returns as soons as all the characteristics
> are discovered or a error happens. The old behaviour was
> to return as soon as the connection was made.
> 
> Now it is safe to call GetCharacteristics() as soon as this
> method returns sucessfully.
> 
> After this method returns it will try to read all the characteristics
> values.
> ---
>  attrib/client.c |   17 +++++++++++++----
>  1 files changed, 13 insertions(+), 4 deletions(-)
> 

[ ... ]

Please disconsider this patch series. Will send another one soon.


Cheers,
-- 
Vinicius

^ permalink raw reply

* Re: [2.6.38-rc8, patch] fix hci_dev_list locking
From: Gustavo F. Padovan @ 2011-03-14 18:04 UTC (permalink / raw)
  To: Daniel J Blueman; +Cc: Linux Kernel, linux-bluetooth
In-Reply-To: <AANLkTimckJ5tYv2Fvy+RgTPbP3SFex2+4MxE+Rn2314H@mail.gmail.com>

Hi Daniel,

* Daniel J Blueman <daniel.blueman@gmail.com> [2011-03-13 11:59:27 +0800]:

> Hi Gustavo,
> 
> On 12 March 2011 09:47, Gustavo F. Padovan <padovan@profusion.mobi> wrote:
> > Hi Daniel,
> >
> > * Daniel J Blueman <daniel.blueman@gmail.com> [2011-03-09 00:07:54 +0800]:
> >
> >> No response from original post, and it missed getting taken into -rc7
> >> and -rc8, so repost:
> >>
> >> Release acquired lock on error path, fixing potential hang up.
> >
> > I already have a patch like this one in my tree, thanks anyway. :)
> 
> I guess I'm not looking in the right public tree, but I still couldn't
> detect the fix in:
> http://git.kernel.org/?p=linux/kernel/git/padovan/bluetooth-2.6.git
> or:
> http://git.kernel.org/?p=linux/kernel/git/padovan/bluetooth-next-2.6.git

On bluetooth-next:

commit b2c60d42db0fea1e6c4345739601024863566a13
Author: Jesper Juhl <jj@chaosbits.net>
Date:   Fri Jan 14 00:18:49 2011 +0100

    Bluetooth: Fix failure to release lock in read_index_list()
    
    If alloc_skb() fails in read_index_list() we'll return -ENOMEM without
    releasing 'hci_dev_list_lock'.
    
    Signed-off-by: Jesper Juhl <jj@chaosbits.net>
    Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>

-- 
Gustavo F. Padovan
http://profusion.mobi

^ permalink raw reply

* Re: how to set adapter to master with bluez 4.69?
From: Brad Midgley @ 2011-03-14 15:29 UTC (permalink / raw)
  To: Brian J. Murrell; +Cc: linux-bluetooth, Brad Midgley
In-Reply-To: <4D7E211F.1030000@interlinx.bc.ca>

Brian,

iirc, the hcitool output tells you the role of the local adapter in
the connection. So when you see 'master' in that list, it's what you
want. You should also be requesting master when you try hcitool.

Brad

^ permalink raw reply

* Re: [PATCH] network: Fix for TP_BNEP_CTRL_BV_01_C qualification test
From: Johan Hedberg @ 2011-03-14 15:11 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth, par-gunnar.p.hjalmdahl, henrik.possung
In-Reply-To: <1300108706-22998-1-git-send-email-lukasz.rymanowski@tieto.com>

Hi Lukasz,

On Mon, Mar 14, 2011, Lukasz Rymanowski wrote:
> With this fix network plugin will correctly respond on unknown
> BNEP control command.
> ---
>  network/server.c |   15 +++++++++++++++
>  1 files changed, 15 insertions(+), 0 deletions(-)

Pushed upstream. Thanks.

Johan

^ permalink raw reply

* [PATCH] Fix "possible circular locking dependency" in rfcomm
From: Ferraton, Jean RegisX @ 2011-03-14 14:56 UTC (permalink / raw)
  To: Gustavo F. Padovan
  Cc: marcel@holtmann.org, davem@davemloft.net, eric.dumazet@gmail.com,
	xiaosuo@gmail.com, linux-bluetooth@vger.kernel.org,
	linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <6E42A1B4DD2F7B4D80A1F26BB498BF9F8C9F86D162@irsmsx501.ger.corp.intel.com>

No response from original post, submit again, more details below:

Changes in sock.c in function __rfcomm_sock_close, to release sock before c=
alling rfcomm_dlc_close to avoid possible deadlock when calling rfcomm_lock=
 (in rfcomm_dlc_close).

Signed-off-by: Jean Regis Ferraton <jrferraton@gmail.com>

---
 net/bluetooth/rfcomm/sock.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c inde=
x 66cc1f0..3b5ec09 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -234,7 +234,10 @@ static void __rfcomm_sock_close(struct sock *sk)
 	case BT_CONNECT2:
 	case BT_CONFIG:
 	case BT_CONNECTED:
+		/* release_sock to avoid deadlock when calling rfcomm_lock() */
+		release_sock(sk);
 		rfcomm_dlc_close(d, 0);
+		lock_sock(sk);
=20
 	default:
 		sock_set_flag(sk, SOCK_ZAPPED);
--
1.6.2.5



This patch solves a problem of "possible circular locking dependency" relat=
ed to rfcomm. Problem seen on kernel 2.6.37. Maybe it's not a bug, just a w=
arning, see the trace below.

This problem occurs in 2 cases:
1- When initiating a connection from the remote device
2- When browsing my BT device from my PC

My analysis is the following:
1- We call the function rfcomm_sock_shutdown() which locks a socket, an aft=
er calls __rfcomm_sock_close()
     	lock_sock(sk);
    	if (!sk->sk_shutdown) {
    		sk->sk_shutdown =3D SHUTDOWN_MASK;
    		__rfcomm_sock_close(sk);
2- In the function __rfcomm_sock_close(), we call rfcomm_dlc_close()
	case BT_CONNECTED:
    		rfcomm_dlc_close(d, 0);
3- In the function rfcomm_dlc_close(), we call rfcomm_lock() which locks a =
mutex on rfcomm, and this second lock causes the problem.
	rfcomm_lock();
	r =3D __rfcomm_dlc_close(d, err);

My message trace is the following (for remote connect):
[  113.995511] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
[  114.001897] [ INFO: possible circular locking dependency detected ]=20
[  114.008157] 2.6.37.3jrf_fix #1=20
[  114.011196] -------------------------------------------------------
[  114.017457] bluetoothd/511 is trying to acquire lock:
[  114.022499]  (rfcomm_mutex){+.+.+.}, at: [<c14f281a>] rfcomm_dlc_close+0=
x15/0x30=20
[  114.029887]=20
[  114.029892] but task is already holding lock:
[  114.035713]  (sk_lock-AF_BLUETOOTH){+.+.+.}, at: [<c14f4cfe>] rfcomm_soc=
k_shutdown+0x17/0x5d=20
[  114.044147]=20
[  114.044152] which lock already depends on the new lock.
[  114.044158]
[  114.052319]
[  114.052324] the existing dependency chain (in reverse order) is:
[  114.059796]
[  114.059801] -> #1 (sk_lock-AF_BLUETOOTH){+.+.+.}:
[  114.065967]        [<c105d660>] lock_acquire+0xf9/0x135
[  114.071184]        [<c1445cf3>] lock_sock_nested+0x55/0x65
[  114.076662]        [<c14ed17d>] l2cap_sock_sendmsg+0x3f/0x606
[  114.082400]        [<c1443775>] sock_sendmsg+0xc9/0xe0
[  114.087530]        [<c14437b4>] kernel_sendmsg+0x28/0x37
[  114.092833]        [<c14f2278>] rfcomm_send_frame+0x2f/0x37
[  114.098398]        [<c14f232c>] rfcomm_send_ua+0x57/0x59
[  114.103701]        [<c14f3884>] rfcomm_run+0x431/0xb06
[  114.108830]        [<c104bc8f>] kthread+0x5f/0x64
[  114.113525]        [<c1002b7a>] kernel_thread_helper+0x6/0x1a
[  114.119265]
[  114.119270] -> #0 (rfcomm_mutex){+.+.+.}:
[  114.124739]        [<c105cff1>] __lock_acquire+0xe03/0x1379
[  114.130304]        [<c105d660>] lock_acquire+0xf9/0x135
[  114.135522]        [<c1522bf3>] mutex_lock_nested+0x45/0x286
[  114.141171]        [<c14f281a>] rfcomm_dlc_close+0x15/0x30
[  114.146650]        [<c14f4cdb>] __rfcomm_sock_close+0x5e/0x6a
[  114.152388]        [<c14f4d12>] rfcomm_sock_shutdown+0x2b/0x5d
[  114.158212]        [<c14f4d5d>] rfcomm_sock_release+0x19/0x60
[  114.163951]        [<c14445da>] sock_release+0x14/0x5b
[  114.169080]        [<c144463d>] sock_close+0x1c/0x20
[  114.174037]        [<c10bdd19>] fput+0xfa/0x19e
[  114.178557]        [<c10bb3c5>] filp_close+0x51/0x5b
[  114.183512]        [<c10bb488>] sys_close+0xb9/0xee
[  114.188381]        [<c1002650>] sysenter_do_call+0x12/0x36
[  114.193858]
[  114.193863] other info that might help us debug this:
[  114.193870]
[  114.201860] 1 lock held by bluetoothd/511:
[  114.205945]  #0:  (sk_lock-AF_BLUETOOTH){+.+.+.}, at: [<c14f4cfe>] rfcom=
m_sock_shutdown+0x17/0x5d
[  114.214811]
[  114.214816] stack backtrace:
[  114.219167] Pid: 511, comm: bluetoothd Tainted: G        WC  2.6.37.3jrf=
_fix #1
[  114.226463] Call Trace:
[  114.228913]  [<c152108a>] ? printk+0xf/0x11=20
[  114.233086]  [<c105bc3b>] print_circular_bug+0x90/0x9c=20
[  114.238211]  [<c105cff1>] __lock_acquire+0xe03/0x1379=20
[  114.243260]  [<c14f281a>] ? rfcomm_dlc_close+0x15/0x30=20
[  114.248382]  [<c105d660>] lock_acquire+0xf9/0x135=20
[  114.253079]  [<c14f281a>] ? rfcomm_dlc_close+0x15/0x30=20
[  114.258208]  [<c1522bf3>] mutex_lock_nested+0x45/0x286=20
[  114.263338]  [<c14f281a>] ? rfcomm_dlc_close+0x15/0x30=20
[  114.268469]  [<c105b68d>] ? trace_hardirqs_on_caller+0x100/0x121
[  114.274467]  [<c1445cf8>] ? lock_sock_nested+0x5a/0x65=20
[  114.279597]  [<c105b6b9>] ? trace_hardirqs_on+0xb/0xd=20
[  114.284639]  [<c14f281a>] rfcomm_dlc_close+0x15/0x30=20
[  114.289594]  [<c14f4cdb>] __rfcomm_sock_close+0x5e/0x6a=20
[  114.294811]  [<c14f4d12>] rfcomm_sock_shutdown+0x2b/0x5d=20
[  114.300115]  [<c14f4d5d>] rfcomm_sock_release+0x19/0x60=20
[  114.305331]  [<c14445da>] sock_release+0x14/0x5b=20
[  114.309937]  [<c144463d>] sock_close+0x1c/0x20=20
[  114.314373]  [<c10bdd19>] fput+0xfa/0x19e=20
[  114.318371]  [<c10bb3c5>] filp_close+0x51/0x5b=20
[  114.322804]  [<c10bb488>] sys_close+0xb9/0xee=20
[  114.327151]  [<c1002650>] sysenter_do_call+0x12/0x36
---------------------------------------------------------------------
Intel Corporation SAS (French simplified joint stock company)
Registered headquarters: "Les Montalets"- 2, rue de Paris,=20
92196 Meudon Cedex, France
Registration Number:  302 456 199 R.C.S. NANTERRE
Capital: 4,572,000 Euros

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

^ permalink raw reply

* Re: [PATCH v2 4/4] Add detection of MAP function in OBEX requests
From: Slawomir Bochenski @ 2011-03-14 14:27 UTC (permalink / raw)
  To: Slawomir Bochenski, linux-bluetooth; +Cc: Johan Hedberg
In-Reply-To: <20110311212758.GA8147@jh-x301>

I will start with some introduction.

*** How MAP obexd plugin works? ***

Service driver and MIME driver are split between mas.c and messages-*.c.
mas.c makes the actual obexd plugin, i.e. there are OBEX_PLUGIN_DEFINE,
obex_service_driver and obex_mime_type_driver there.

In order to support different message storages the actual data accessing
is done in messages-*.c files (a symbolic link messages.c to one of them
is made during ./configure phase, as it is also done in PBAP plugin). I
will refer to this one as "backend".

There is only one mime driver here, as OBEX request type and OBEX type
header is used in MAP merely to select operation to perform. This
operation in MAP specification is called function. Processing of the
incoming headers and response is done in the same fashion for each one of
them, thus one driver handling all requests to MAP target is enough.

Backend in many ways resembles service plugin/mime type driver pair.

So there are messages_init() and messages_exit() to give backend the
chance to do its own initialization during MAP startup. Converting it to
plugin on its own wouldn't make much sense, as it is fully functional
only in context of MAP plugin (i.e. mas.c). Calling messages_init()
manually from MAP plugin init() function also allows MAP plugin to stop
from loading if the messages backend is non-functional.

There is also messages_connect() and messages_disconnect() which are
called from service driver ->connect() and ->disconnect() respectively.
Session awareness is needed for doing connection-specific processing, for
example storing (un)read status per client (which is required by MAP
specification). The message_connect() call returns pointer which is in
turn passed to all other in-session functions of backend.

The low-level processing which would be the same regardless of storage in
use are done in mas.c. When receiving get/put request, mas.c determines
operation to perform (MAP function) and parses application parameters
headers. Then as in case of other obexd services
obex_(get|put)_stream_start gets called which in turn triggers MIME
driver's ->open().

As I mentioned, the MIME driver functionality is split between mas.c and
backend. So actual MIME driver open() elects appropriate callback for MAP
function in question and calls messages_open() which does the rest of the
job of preparing I/O for the request. It however does not pass the same
data it gets, as some preliminary processing has been already done - so
message_open() gets requested opcode (MAP function), input parameters,
storage for output parameters, OBEX name header value and callback
address. This callback is used to return data asynchronously.

The data in callback is returned in a structure specific to selected MAP
function and optionally wakes the I/O. Callback code takes the structure,
formats it into a stream for sending and appends this to output buffer.
So for example, in GetMessagesListing case, callback gets struct with
metadata of messages in folder and makes an XML from it.

Simultaneously MIME driver read()-s are called (as driven by OpenOBEX
streaming mode) and they pass to the caller what's in the buffer, or
return -EAGAIN if buffer is empty and callback did not set the flag
informing that the operation is already finished.

At the end of the request or at any time if the request is aborted MIME
driver close() is called which in turn calls messages_close() to free
it's data or stop any pending actions (for example D-Bus calls if backend
uses D-Bus).


==========================================

So to answer Johan's doubts.

Current design makes some things easier. For instance request abortion,
as there is messages_open() and messages_close() and close already knows
what to close because of fid (function id) presence. In case of what
Johan wants either I would have to make 14 functions in global name space
(7 MAP functions, equivalent of close and open for any single one of
them) or use 7
identical "open" functions and one single close, which will rely on
hidden function id stored internally in backend. Latter would make ugly
asymmetry in plugin code, as in MIME driver open() there would be called
one of those 7 functions and in close() only messages_close() (or
cancel?) for which relation to one of previous functions would not be
clear from mas.c perspective.

And of course each of these "open" functions and also each of
these "close" functions would have the same prototype and would do the
same thing from the caller view.

What I'm trying to say is that the abstraction is rewarding also from the
service plugin point, and putting backend logic behind something almost
like mime driver is well...  logical, as it almost follows mime driver
work flow, it is just a little bit too crippled to be real mime driver on
its own so mime->open() does what it can (it does the low level OBEX
things) and then calls for assistance from messages_open(). And
messages_open does the rest of opening but not using low level mime-like
open() semantics, but MAP semantics.

Err... I said "also" because implementing backend is easier too. It can
simply return error for any MAP function it does not want to support
right away from messages_open and it also makes it immune to further
addition of new MAP functions.

And even though MAP calls its functions, well, functions, they are not
exactly the same as C functions they are more like an opcodes selecting
what to do with data presented. And this data is always of the same
structure.

The way it is now, MIME driver and thus plugin code is kept short and sweet.

^ permalink raw reply

* Re: how to set adapter to master with bluez 4.69?
From: Brian J. Murrell @ 2011-03-14 14:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Brad Midgley
In-Reply-To: <4D7E0A15.5000708@interlinx.bc.ca>

[-- Attachment #1: Type: text/plain, Size: 1589 bytes --]

On 11-03-14 08:29 AM, Brian J. Murrell wrote:
> 
> Having been playing an audio stream to the headset for nearly an hour,
> it seems to be co-existing with the mouse today.  Currently HCI
> connections are:
> 
> $ hcitool con
> Connections:
> 	< SCO 00:1A:45:1B:19:89 handle 1 state 1 lm SLAVE
> 	> ACL 00:1A:45:1B:19:89 handle 11 state 1 lm MASTER AUTH ENCRYPT
> 	> ACL 00:1F:20:0F:30:6A handle 12 state 1 lm MASTER
> 
> Which I find interesting.
> 
> I'm going to try the other headset now and see if it's just as stable
> this morning.

So, I tried the other headset, which was reported as:

$ hcitool con
Connections:
	< eSCO 00:18:6B:E5:4F:7E handle 6 state 1 lm SLAVE
	> ACL 00:18:6B:E5:4F:7E handle 12 state 1 lm MASTER AUTH ENCRYPT
	> ACL 00:1F:20:0F:30:6A handle 11 state 1 lm MASTER

and it was not as stable.  I had periodic disconnect/reconnects of all
B/T devices.

I've disconnected the (unstable) LG headset and connected the Jabra
headset again and it seems stable again.

It's just too bad I dislike the ergonomics of the Jabra headset.  It's
nowhere near as comfortable as the LG headset.  The LG headset "seals"
into the ear where Jabra basically just "hangs" there using gravity to
hold itself to the ear and does not create anywhere near as good a
fit/seal to the ear.  :-(

I'd entertain going and buying yet another headset, hoping to get the
stability of this Jabra with the fit of the LG, but given my 50/50
success rate here, it seems like it will be a real crapshoot getting a
headset that works stably.  :-(

b.




[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* [PATCH] network: Fix for TP_BNEP_CTRL_BV_01_C qualification test
From: Lukasz Rymanowski @ 2011-03-14 13:18 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: par-gunnar.p.hjalmdahl, henrik.possung, Lukasz Rymanowski

With this fix network plugin will correctly respond on unknown
BNEP control command.
---
 network/server.c |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/network/server.c b/network/server.c
index 7c63c3e..d1da8a9 100644
--- a/network/server.c
+++ b/network/server.c
@@ -394,6 +394,21 @@ static gboolean bnep_setup(GIOChannel *chan,
 		return FALSE;
 	}
 
+	/* Highest known Control command ID
+	 * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+	if (req->type == BNEP_CONTROL &&
+				req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+		uint8_t pkt[3];
+
+		pkt[0] = BNEP_CONTROL;
+		pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+		pkt[2] = req->ctrl;
+
+		send(sk, pkt, sizeof(pkt), 0);
+
+		return FALSE;
+	}
+
 	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
 		return FALSE;
 
-- 
1.7.0.4

On behalf of ST-Ericsson.
/Lukasz

^ permalink raw reply related

* Re: how to set adapter to master with bluez 4.69?
From: Brian J. Murrell @ 2011-03-14 12:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Brad Midgley
In-Reply-To: <4D7DF55D.2010402@interlinx.bc.ca>

[-- Attachment #1: Type: text/plain, Size: 1968 bytes --]

Hi again Brad.

On 11-03-14 07:00 AM, Brian J. Murrell wrote:
> 
> Any advise how to proceed, even just to set the adapter to master manually?

A little more playing here and I seem to have been able to set the
adapter to master:

$ hciconfig hci0 lm
hci0:	Type: BR/EDR  Bus: USB
	BD Address: 00:02:72:1E:E0:12  ACL MTU: 1021:7  SCO MTU: 64:1
	Link mode: SLAVE ACCEPT
$ hciconfig hci0 lm MASTER
Can't set default link mode on hci0: Permission denied (13)
$ sudo hciconfig hci0 lm MASTER
$ hciconfig hci0 lm
hci0:	Type: BR/EDR  Bus: USB
	BD Address: 00:02:72:1E:E0:12  ACL MTU: 1021:7  SCO MTU: 64:1
	Link mode: MASTER

Or was I?

$ sudo hcitool con
Connections:
	> ACL 00:1F:20:0F:30:6A handle 11 state 1 lm MASTER

So the mouse is still reporting it's master.

But so is the adapter:

$ hciconfig hci0 lm
hci0:	Type: BR/EDR  Bus: USB
	BD Address: 00:02:72:1E:E0:12  ACL MTU: 1021:7  SCO MTU: 64:1
	Link mode: MASTER

Maybe I am misunderstanding what I am reading.

For good measure I deleted (i.e. in the GUI) the mouse and re-added it
but it still seems to be master:

$ sudo hcitool con
Connections:
	< ACL 00:1F:20:0F:30:6A handle 11 state 1 lm MASTER

Hrm.  Now I've added my Jabra BT125 headset and the connections being
reported now are:

$ sudo hcitool con
Connections:
	< ACL 00:1A:45:1B:19:89 handle 12 state 1 lm MASTER AUTH ENCRYPT
	< ACL 00:1F:20:0F:30:6A handle 11 state 1 lm MASTER


Everybody's master now?

Having been playing an audio stream to the headset for nearly an hour,
it seems to be co-existing with the mouse today.  Currently HCI
connections are:

$ hcitool con
Connections:
	< SCO 00:1A:45:1B:19:89 handle 1 state 1 lm SLAVE
	> ACL 00:1A:45:1B:19:89 handle 11 state 1 lm MASTER AUTH ENCRYPT
	> ACL 00:1F:20:0F:30:6A handle 12 state 1 lm MASTER

Which I find interesting.

I'm going to try the other headset now and see if it's just as stable
this morning.

b.




[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* [PATCH v3 5/5] Sim Access Profile test scripts
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz
In-Reply-To: <1300101380-10358-1-git-send-email-waldemar.rymarkiewicz@tieto.com>

Add simple SAP client python implementation and a test script.

To run test-sap.py you need Python 2.6 or newer (tested with 2.6 only)
and PyBluez package installed.
---
 test/sap.py      |  945 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 test/test-sap.py |  140 ++++++++
 2 files changed, 1085 insertions(+), 0 deletions(-)
 create mode 100644 test/sap.py
 create mode 100755 test/test-sap.py

diff --git a/test/sap.py b/test/sap.py
new file mode 100644
index 0000000..3164861
--- /dev/null
+++ b/test/sap.py
@@ -0,0 +1,945 @@
+""" Copyright (C) 2011 Tieto """
+
+""" Szymon Janc <szymon.janc@tieto.com> """
+""" Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> """
+
+""" This program is free software; you can redistribute it and/or modify """
+""" it under the terms of the GNU General Public License as published by """
+""" the Free Software Foundation; either version 2 of the License, or """
+""" (at your option) any later version. """
+
+""" This program is distributed in the hope that it will be useful, """
+""" but WITHOUT ANY WARRANTY; without even the implied warranty of """
+""" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the """
+""" GNU General Public License for more details. """
+
+""" You should have received a copy of the GNU General Public License """
+""" along with this program; if not, write to the Free Software """
+""" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """
+
+from array import array
+from bluetooth import *
+import time
+import re
+
+class SAPParam:
+    """ SAP Parameter Class """
+
+    MaxMsgSize = 0x00
+    ConnectionStatus = 0x01
+    ResultCode = 0x02
+    DisconnectionType = 0x03
+    CommandAPDU = 0x04
+    ResponseAPDU = 0x05
+    ATR = 0x06
+    CardReaderStatus = 0x07
+    StatusChange = 0x08
+    TransportProtocol = 0x09
+    CommandAPDU7816 = 0x10
+
+    def __init__(self, name, id, value = None):
+        self.name = name
+        self.id = id
+        self.value = value
+
+    def _padding(self,  buf):
+        pad = array('B')
+        while ( (len(buf) + len(pad)) % 4 ) != 0:
+            pad.append(0)
+        return pad
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 or buf[1] != 0:
+                return (-1,  -1)
+        if buf[0] != self.id:
+            return (-1,  -1)
+        plen = buf[2] * 256 + buf[3] + 4
+        if plen > len(buf):
+            return (-1,  -1)
+        pad = plen
+        while (pad % 4) != 0:
+            if buf[pad] != 0:
+                return (-1,  -1)
+            pad+=1
+        return (plen,  pad)
+
+    def getID(self):
+        return self.id
+
+    def getValue(self):
+        return self.value
+
+    def getContent(self):
+        return "%s(id=0x%.2X), value=%s \n" %  (self.name,  self.id, self.value)
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[1] = 0	# reserved
+        a[2] = 0	# length
+        a[3] = 1	# length
+        a.append(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.id = buf[0]
+        self.value = buf[4]
+        return p[1]
+
+
+class SAPParam_MaxMsgSize(SAPParam):
+    """MaxMsgSize Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"MaxMsgSize",  SAPParam.MaxMsgSize, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value > 0xFFFF:
+             self.value = 0xFFFF
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[3] = 2
+        a.append(self.value / 256)
+        a.append(self.value % 256)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1 :
+            return -1
+        self.value = buf[4] * 256 + buf[5]
+        return p[1]
+
+class SAPParam_CommandAPDU(SAPParam):
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B', value))
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        plen = len(self.value)
+        a[2] = plen / 256
+        a[3] = plen % 256
+        a.extend(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.value = buf[4:p[0]]
+        return p[1]
+
+class SAPParam_ResponseAPDU(SAPParam_CommandAPDU):
+    """ResponseAPDU Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B', value))
+
+class SAPParam_ATR(SAPParam_CommandAPDU):
+    """ATR Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B'))
+        else:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B', value))
+
+class SAPParam_CommandAPDU7816(SAPParam_CommandAPDU):
+    """Command APDU7816 Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B', value))
+
+
+class SAPParam_ConnectionStatus(SAPParam):
+    """Connection status Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ConnectionStatus",  SAPParam.ConnectionStatus, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04):
+            print "Warning. ConnectionStatus value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_ResultCode(SAPParam):
+    """ Result Code Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ResultCode",  SAPParam.ResultCode, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05,  0x06,  0x07):
+            print "Warning. ResultCode value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_DisconnectionType(SAPParam):
+    """Disconnection Type Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"DisconnectionType",  SAPParam.DisconnectionType, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. DisconnectionType value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_CardReaderStatus(SAPParam_CommandAPDU):
+    """Card reader Status Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B'))
+        else:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B', value))
+
+class SAPParam_StatusChange(SAPParam):
+    """Status Change Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"StatusChange",  SAPParam.StatusChange, value)
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05):
+            print "Warning. StatusChange value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_TransportProtocol(SAPParam):
+    """Transport Protocol Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"TransportProtocol",  SAPParam.TransportProtocol, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. TransportProtoco value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPMessage:
+
+    CONNECT_REQ = 0x00
+    CONNECT_RESP = 0x01
+    DISCONNECT_REQ = 0x02
+    DISCONNECT_RESP =0x03
+    DISCONNECT_IND = 0x04
+    TRANSFER_APDU_REQ = 0x05
+    TRANSFER_APDU_RESP = 0x06
+    TRANSFER_ATR_REQ = 0x07
+    TRANSFER_ATR_RESP = 0x08
+    POWER_SIM_OFF_REQ = 0x09
+    POWER_SIM_OFF_RESP = 0x0A
+    POWER_SIM_ON_REQ = 0x0B
+    POWER_SIM_ON_RESP = 0x0C
+    RESET_SIM_REQ = 0x0D
+    RESET_SIM_RESP = 0x0E
+    TRANSFER_CARD_READER_STATUS_REQ = 0x0F
+    TRANSFER_CARD_READER_STATUS_RESP = 0x10
+    STATUS_IND = 0x11
+    ERROR_RESP = 0x12
+    SET_TRANSPORT_PROTOCOL_REQ = 0x13
+    SET_TRANSPORT_PROTOCOL_RESP = 0x14
+
+    def __init__(self,  name,  id):
+        self.name = name
+        self.id = id
+        self.params = []
+        self.buf = array('B')
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 :
+            return False
+
+        if buf[0] != self.id:
+            return False
+
+        return True
+
+    def getID(self):
+        return self.id
+
+    def getContent(self):
+        s = "%s(id=0x%.2X) " % (self.name,  self.id)
+        if len( self.buf): s = s + "[%s]" % re.sub("(.{2})", "0x\\1 " , self.buf.tostring().encode("hex").upper(), re.DOTALL)
+        s = s + "\n\t"
+        for p in self.params:
+            s = s + "\t" + p.getContent()
+        return s
+
+    def getParams(self):
+        return self.params
+
+    def addParam(self,  param):
+        self.params.append(param)
+
+    def serialize(self):
+        ret = array('B', '\00\00\00\00')
+        ret[0] = self.id
+        ret[1] = len(self.params)
+        ret[2] = 0	# reserved
+        ret[3] = 0	# reserved
+        for p in self.params:
+            ret.extend(p.serialize())
+
+        self.buf = ret
+        return ret
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        return len(buf) == 4 and buf[1] == 0 and self._basicCheck(buf)
+
+
+class SAPMessage_CONNECT_REQ(SAPMessage):
+    def __init__(self,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_REQ",  SAPMessage.CONNECT_REQ)
+        if MaxMsgSize is not None:
+            self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.MaxMsgSize:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_MaxMsgSize()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_CONNECT_RESP(SAPMessage):
+    def __init__(self,  ConnectionStatus = None,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_RESP",  SAPMessage.CONNECT_RESP)
+        if ConnectionStatus is not None:
+            self.addParam(SAPParam_ConnectionStatus(ConnectionStatus))
+            if MaxMsgSize is not None:
+                self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ConnectionStatus:
+                if self.params[0].getValue() ==  0x02:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ConnectionStatus()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_MaxMsgSize()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_DISCONNECT_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_REQ",  SAPMessage.DISCONNECT_REQ)
+
+class SAPMessage_DISCONNECT_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_RESP",  SAPMessage.DISCONNECT_RESP)
+
+class SAPMessage_DISCONNECT_IND(SAPMessage):
+    def __init__(self,  Type = None):
+        SAPMessage.__init__(self,"DISCONNECT_IND",  SAPMessage.DISCONNECT_IND)
+        if Type is not None:
+            self.addParam(SAPParam_DisconnectionType(Type))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.DisconnectionType:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_DisconnectionType()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+
+class SAPMessage_TRANSFER_APDU_REQ(SAPMessage):
+    def __init__(self,  APDU = None,  T = False):
+        SAPMessage.__init__(self,"TRANSFER_APDU_REQ",  SAPMessage.TRANSFER_APDU_REQ)
+        if APDU is not None:
+            if T :
+                self.addParam(SAPParam_CommandAPDU(APDU))
+            else:
+                self.addParam(SAPParam_CommandAPDU7816(APDU))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.CommandAPDU or self.params[0].getID() == SAPParam.CommandAPDU7816:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_CommandAPDU()
+            p2 = SAPParam_CommandAPDU7816()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+            elif p2.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p2)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_APDU_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Response = None):
+        SAPMessage.__init__(self,"TRANSFER_APDU_RESP",  SAPMessage.TRANSFER_APDU_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Response is not None:
+                self.addParam(SAPParam_ResponseAPDU(Response))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_ResponseAPDU()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_ATR_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_ATR_REQ",  SAPMessage.TRANSFER_ATR_REQ)
+
+class SAPMessage_TRANSFER_ATR_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  ATR = None):
+        SAPMessage.__init__(self,"TRANSFER_ATR_RESP",  SAPMessage.TRANSFER_ATR_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if ATR is not None:
+                self.addParam(SAPParam_ATR(ATR))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+
+            if  r != -1:
+
+                self.addParam(p)
+                if buf[1] == 2:
+
+                    p = SAPParam_ATR()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_OFF_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_REQ",  SAPMessage.POWER_SIM_OFF_REQ)
+
+class SAPMessage_POWER_SIM_OFF_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_RESP",  SAPMessage.POWER_SIM_OFF_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.ResultCode:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_ON_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_ON_REQ",  SAPMessage.POWER_SIM_ON_REQ)
+
+class SAPMessage_POWER_SIM_ON_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_ON_RESP",  SAPMessage.POWER_SIM_ON_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_RESET_SIM_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"RESET_SIM_REQ",  SAPMessage.RESET_SIM_REQ)
+
+class SAPMessage_RESET_SIM_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"RESET_SIM_RESP",  SAPMessage.RESET_SIM_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_STATUS_IND(SAPMessage):
+    def __init__(self,  StatusChange = None):
+        SAPMessage.__init__(self,"STATUS_IND",  SAPMessage.STATUS_IND)
+        if StatusChange is not None:
+            self.addParam(SAPParam_StatusChange(StatusChange))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.StatusChange:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_StatusChange()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_REQ",  SAPMessage.TRANSFER_CARD_READER_STATUS_REQ)
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Status = None):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_RESP",  SAPMessage.TRANSFER_CARD_READER_STATUS_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Status is not None:
+                self.addParam(SAPParam_CardReaderStatus(Status))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_CardReaderStatus()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_ERROR_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"ERROR_RESP",  SAPMessage.ERROR_RESP)
+
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(SAPMessage):
+    def __init__(self,  protocol = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_REQ",  SAPMessage.SET_TRANSPORT_PROTOCOL_REQ)
+        if protocol is not None:
+            self.addParam(SAPParam_TransportProtocol(protocol))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.TransportProtocol:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_TransportProtocol()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_RESP",  SAPMessage.SET_TRANSPORT_PROTOCOL_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+
+class SAPClient:
+
+    CONNECTED = 1
+    DISCONNECTED = 0
+
+    uuid = "0000112D-0000-1000-8000-00805F9B34FB"
+    bufsize = 1024
+    timeout = 20
+    state = DISCONNECTED
+
+    def __init__(self,  host = None,  port = None):
+        self.sock = None
+
+        if host is None or is_valid_address(host):
+            self.host = host
+        else:
+            raise BluetoothError ("%s is not a valid BT address." % host)
+            self.host = None
+            return
+
+        if port is None:
+            self.__discover()
+        else:
+            self.port = port
+
+        self.__connectRFCOMM()
+
+    def __del__(self):
+        self.__disconnectRFCOMM()
+
+    def __disconnectRFCOMM(self):
+        if self.sock is not None:
+            self.sock.close()
+            self.state = self.DISCONNECTED
+
+    def __discover(self):
+        service_matches = find_service(self.uuid, self.host)
+
+        if len(service_matches) == 0:
+            raise BluetoothError ("No SAP service found")
+            return
+
+        first_match = service_matches[0]
+        self.port = first_match["port"]
+        self.host = first_match["host"]
+
+        print "SAP Service found on %s(%s)" % first_match["name"] % self.host
+
+    def __connectRFCOMM(self):
+        self.sock=BluetoothSocket( RFCOMM )
+        self.sock.connect((self.host, self.port))
+        self.sock.settimeout(self.timeout)
+        self.state = self.CONNECTED
+
+    def __sendMsg(self, msg):
+        if isinstance(msg,  SAPMessage):
+            s = msg.serialize()
+            print "\tTX: " + msg.getContent()
+            return self.sock.send(s.tostring())
+
+    def __rcvMsg(self,  msg):
+        if isinstance(msg,  SAPMessage):
+            print "\tRX Wait: %s(id = 0x%.2x)" % (msg.name, msg.id)
+            data = self.sock.recv(self.bufsize)
+            if data:
+                if msg.deserialize(array('B',data)):
+                    print "\tRX: len(%d) %s" % (len(data), msg.getContent())
+                    return msg
+                else:
+                    print "msg: %s" % array('B',data)
+                    raise BluetoothError ("Message deserialization failed.")
+            else:
+                raise BluetoothError ("Timeout. No data received.")
+
+    def connect(self):
+        self.__connectRFCOMM()
+
+    def disconnect(self):
+        self.__disconnectRFCOMM()
+
+    def isConnected(self):
+    	return self.state
+
+    def proc_connect(self):
+        try:
+            self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+            params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+            if params[0].getValue() in (0x00,  0x04):
+                pass
+            elif params[0].getValue() == 0x02:
+                self.bufsize = params[1].getValue()
+
+                self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+                params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+                if params[0].getValue() not in (0x00,  0x04):
+                    return False
+            else:
+                return False
+
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+            if params[0].getValue() == 0x00:
+                return False
+            elif params[0].getValue() == 0x01:
+                """OK, Card reset"""
+                return self.proc_transferATR()
+            elif params[0].getValue() == 0x02:
+                """T0 not supported"""
+                if self.proc_transferATR():
+                    return self.proc_setTransportProtocol(1)
+                else:
+                    return False
+            else:
+                return False
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByClient(self, timeout=0):
+        try:
+            self.__sendMsg(SAPMessage_DISCONNECT_REQ())
+            self.__rcvMsg(SAPMessage_DISCONNECT_RESP())
+            time.sleep(timeout) # let srv to close rfcomm
+            self.__disconnectRFCOMM()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByServer(self, timeout=0):
+        try:
+            params = self.__rcvMsg(SAPMessage_DISCONNECT_IND()).getParams()
+
+            """gracefull"""
+            if params[0].getValue() == 0x00:
+                if not self.proc_transferAPDU():
+                    return False
+
+            return self.proc_disconnectByClient(timeout)
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferAPDU(self,  apdu = "Sample APDU command"):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_APDU_REQ(apdu))
+            params = self.__rcvMsg(SAPMessage_TRANSFER_APDU_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferATR(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_ATR_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_ATR_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOff(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_OFF_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_OFF_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOn(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_ON_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_ON_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_resetSim(self):
+        try:
+            self.__sendMsg(SAPMessage_RESET_SIM_REQ())
+            params = self.__rcvMsg(SAPMessage_RESET_SIM_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_reportStatus(self):
+        try:
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferCardReaderStatus(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_errorResponse(self):
+        try:
+            """ send malformed message, no mandatory maxmsgsize parameter"""
+            self.__sendMsg(SAPMessage_CONNECT_REQ())
+
+            params = self.__rcvMsg(SAPMessage_ERROR_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_setTransportProtocol(self,  protocol = 0):
+        try:
+            self.__sendMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(protocol))
+            params = self.__rcvMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_RESP()).getParams()
+
+            if params[0].getValue() == 0x00:
+                params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+                if params[0].getValue() in (0x01,  0x02):
+                    return self.proc_transferATR()
+                else:
+                    return True
+                    """return False ???"""
+            elif params[0].getValue == 0x07:
+                """not supported"""
+                return True
+                """return False ???"""
+            else:
+                return False
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+if __name__ == "__main__":
+    pass
+
diff --git a/test/test-sap.py b/test/test-sap.py
new file mode 100755
index 0000000..5b2f329
--- /dev/null
+++ b/test/test-sap.py
@@ -0,0 +1,140 @@
+""" Copyright (C) 2011 Tieto """
+""" Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> """
+
+from sap import *
+import time
+
+def connect_disconnect_by_client(sap):
+
+    print "[Test] Connect - Disconnect by client \n"
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByClient():
+                print "OK"
+                return 0
+
+        print "NOT OK"
+        return 1
+
+    except BluetoothError , e:
+        print "Error " + str(e)
+
+
+def connect_disconnect_by_server_gracefully(sap, timeout=0):
+
+    print "[Test] Connect - Disconnect by server with timer \n"
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByServer(timeout):
+                print "OK"
+                return 0
+
+        print "NOT OK"
+        return 1
+
+    except BluetoothError , e:
+        print "Error " + str(e)
+
+
+def connect_txAPDU_disconnect_by_client(sap):
+
+    print "[Test] Connect - TX APDU - Disconnect by client \n"
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_transferAPDU():
+                print "NOT OK 1"
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print "NOT OK 2"
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print "NOT OK 3"
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print "NOT OK 4"
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print "OK"
+                return 0
+
+        print "NOT OK"
+        return 1
+
+    except BluetoothError , e:
+        print "Error " + str(e)
+
+def connect_rfcomm_only_and_wait_for_close_by_server(sap):
+
+    print "[Test] Connect rfcomm only  - Disconnect by server timeout \n"
+
+    if not sap.isConnected():
+       sap.connect()
+
+    time.sleep(40)
+    print "OK"
+
+def power_sim_off_on(sap):
+
+    print "[Test] Powe sim off \n"
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_resetSim():
+                print "NOT OK"
+                return 1
+
+            if not sap.proc_powerSimOff():
+                print "NOT OK"
+                return 1
+
+            if not sap.proc_powerSimOn():
+                print "NOT OK"
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print "OK"
+                return 0
+
+        print "NOT OK"
+        return 1
+
+    except BluetoothError , e:
+        print "Error " + str(e)
+
+
+if __name__ == "__main__":
+
+    host = "00:15:83:38:BF:18"
+    port = 8
+
+    try:
+        s = SAPClient(host, port)
+    except BluetoothError , e:
+        print "Error " +str(e)
+
+#    connect_disconnect_by_client(s)
+#    connect_disconnect_by_server_gracefully(s)
+#    connect_disconnect_by_server_gracefully(s, 40)  #  wait 40 sec for srv to close rfcomm sock
+#    connect_rfcomm_only_and_wait_for_close_by_server(s)
+    connect_txAPDU_disconnect_by_client(s)
+    power_sim_off_on(s)
+
-- 
1.7.1

^ permalink raw reply related

* [PATCH v3 4/5] Sim Access Profile dummy driver
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz
In-Reply-To: <1300101380-10358-1-git-send-email-waldemar.rymarkiewicz@tieto.com>

Add SAP dummy driver implementation and extend configure
with --with-sap=<driver>.
---
 .gitignore      |    1 +
 Makefile.am     |   13 +++-
 acinclude.m4    |    6 ++
 sap/sap-dummy.c |  270 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 275 insertions(+), 15 deletions(-)

diff --git a/.gitignore b/.gitignore
index 07e239f..3e36a59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ lib/bluetooth
 src/builtin.h
 src/bluetoothd
 audio/telephony.c
+sap/sap.c
 scripts/bluetooth.rules
 scripts/97-bluetooth.rules
 scripts/97-bluetooth-hid2hci.rules
diff --git a/Makefile.am b/Makefile.am
index af374a9..93a0794 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,7 +148,13 @@ builtin_modules += sap
 builtin_sources += sap/main.c \
 			sap/manager.h sap/manager.c \
 			sap/server.h sap/server.c \
-			sap/sap.h sap/sap-dummy.c
+			sap/sap.h
+
+builtin_nodist += sap/sap.c
+
+noinst_LIBRARIES = sap/libsap.a
+
+sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c
 endif
 
 if INPUTPLUGIN
@@ -278,7 +284,7 @@ EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
 			input/input.conf serial/serial.conf \
 			audio/audio.conf audio/telephony-dummy.c \
 			audio/telephony-maemo5.c audio/telephony-ofono.c \
-			audio/telephony-maemo6.c
+			audio/telephony-maemo6.c sap/sap-dummy.c
 
 
 if ALSA
@@ -403,6 +409,9 @@ src/builtin.h: src/genbuiltin $(builtin_sources)
 audio/telephony.c: audio/@TELEPHONY_DRIVER@
 	$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
 
+sap/sap.c: sap/@SAP_DRIVER@
+	$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
 scripts/%.rules:
 	$(AM_V_GEN)cp $(subst 97-,,$@) $@
 
diff --git a/acinclude.m4 b/acinclude.m4
index d07418f..faa7f7c 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -205,6 +205,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	configfiles_enable=yes
 	telephony_driver=dummy
 	maemo6_enable=no
+	sap_driver=dummy
 
 	AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
 		optimization_enable=${enableval}
@@ -226,6 +227,11 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		sap_enable=${enableval}
 	])
 
+	AC_ARG_WITH(sap, AC_HELP_STRING([--with-sap=DRIVER], [select SAP driver]), [
+		sap_driver=${withval}
+	])
+	AC_SUBST([SAP_DRIVER], [sap-${sap_driver}.c])
+
 	AC_ARG_ENABLE(serial, AC_HELP_STRING([--disable-serial], [disable serial plugin]), [
 		serial_enable=${enableval}
 	])
diff --git a/sap/sap-dummy.c b/sap/sap-dummy.c
index 3c0305f..2391bac 100644
--- a/sap/sap-dummy.c
+++ b/sap/sap-dummy.c
@@ -2,13 +2,15 @@
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto
  *
  *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
  *          for ST-Ericsson
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; version 2 of the License.
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,50 +22,182 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gdbus.h>
+
 #include "log.h"
 #include "sap.h"
 
+#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest"
+#define SAP_DUMMY_PATH "/org/bluez/test"
+
+enum {
+	SIM_DISCONNECTED= 0x00,
+	SIM_CONNECTED	= 0x01,
+	SIM_POWERED_OFF	= 0x02,
+	SIM_MISSING	= 0x03
+};
+
+static DBusConnection *connection = NULL;
+
+static int sim_card_conn_status = SIM_DISCONNECTED;
+static void *sap_data = NULL;  /* SAP server private data.*/
+static gboolean ongoing_call_status = FALSE;
+static int max_msg_size_supported = 512;
+
 void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
 {
-	sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
-	sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_DISCONNECTED) {
+		sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+								maxmsgsize);
+		return;
+	} else if (max_msg_size_supported > maxmsgsize) {
+		sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL,
+						max_msg_size_supported);
+		return;
+	} else if (max_msg_size_supported < maxmsgsize) {
+		sap_connect_rsp(sap_device,
+				SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+				max_msg_size_supported);
+		return;
+	} else if (ongoing_call_status) {
+		sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL,
+								maxmsgsize);
+		return;
+	} else {
+		sim_card_conn_status = SIM_CONNECTED;
+		sap_data = sap_device;
+
+		sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
+		sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+	}
 }
 
 void sap_disconnect_req(void *sap_device, uint8_t linkloss)
 {
+	sim_card_conn_status = SIM_DISCONNECTED;
+	sap_data = NULL;
+	ongoing_call_status = FALSE;
+
+	DBG("status: %d", sim_card_conn_status);
+
+	if (linkloss)
+		return;
+
 	sap_disconnect_rsp(sap_device);
 }
 
 void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
 {
-	sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+	char apdu[] = "APDU response!";
+
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status == SIM_MISSING)
+		sap_transfer_apdu_rsp(sap_device,
+				SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
+	else if (sim_card_conn_status == SIM_POWERED_OFF)
+		sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+								NULL, 0);
+	else if (sim_card_conn_status != SIM_CONNECTED)
+		sap_transfer_apdu_rsp(sap_device,
+			SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
+	else
+		sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK, (uint8_t*)&apdu, sizeof(apdu));
 }
 
 void sap_transfer_atr_req(void *sap_device)
 {
-	sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+	char atr[] = "ATR response!";
+
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status == SIM_MISSING)
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
+								NULL, 0);
+	else if (sim_card_conn_status == SIM_POWERED_OFF)
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+								NULL, 0);
+	else if (sim_card_conn_status != SIM_CONNECTED)
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
+								NULL, 0);
+	else
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK, (uint8_t*)&atr, sizeof(atr));
 }
 
 void sap_power_sim_off_req(void *sap_device)
 {
-	sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status == SIM_MISSING) {
+		sap_power_sim_off_rsp(sap_device,
+					SAP_RESULT_ERROR_CARD_REMOVED);
+	} else if (sim_card_conn_status == SIM_POWERED_OFF) {
+		sap_power_sim_off_rsp(sap_device,
+					SAP_RESULT_ERROR_POWERED_OFF);
+	} else if (sim_card_conn_status != SIM_CONNECTED) {
+		sap_power_sim_off_rsp(sap_device,
+					SAP_RESULT_ERROR_NO_REASON);
+	} else {
+		sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+		sim_card_conn_status = SIM_POWERED_OFF;
+	}
 }
 
 void sap_power_sim_on_req(void *sap_device)
 {
-	sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status == SIM_MISSING) {
+		sap_power_sim_on_rsp(sap_device,
+					SAP_RESULT_ERROR_CARD_REMOVED);
+	} else if (sim_card_conn_status == SIM_POWERED_OFF) {
+		sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+		sim_card_conn_status = SIM_CONNECTED;
+		return;
+	} else if (sim_card_conn_status != SIM_CONNECTED) {
+		sap_power_sim_on_rsp(sap_device,
+					SAP_RESULT_ERROR_NOT_ACCESSIBLE);
+	} else {
+		sap_power_sim_on_rsp(sap_device,
+					SAP_RESULT_ERROR_NO_REASON);
+	}
 }
 
 void sap_reset_sim_req(void *sap_device)
 {
-	sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
-	sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status == SIM_MISSING) {
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
+	} else if (sim_card_conn_status == SIM_POWERED_OFF) {
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
+	} else if (sim_card_conn_status != SIM_CONNECTED) {
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+	} else {
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+	}
 }
 
 void sap_transfer_card_reader_status_req(void *sap_device)
 {
-	sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK,
-						ICC_READER_CARD_POWERED_ON);
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_CONNECTED) {
+		sap_transfer_card_reader_status_rsp(sap_device,
+					SAP_RESULT_ERROR_NO_REASON, 0xF1);
+		return;
+	}
+	sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1);
 }
 
 void sap_set_transport_protocol_req(void *sap_device,
@@ -72,13 +206,123 @@ void sap_set_transport_protocol_req(void *sap_device,
 	sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
 }
 
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+					"Invalid arguments in method call");
+}
+
+static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	dbus_bool_t ongoing;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing,
+						DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	if (ongoing_call_status && !ongoing) {
+		/* An ongoing call has finished. Continue connection.*/
+		sap_connect_rsp(sap_data, SAP_STATUS_OK,
+						max_msg_size_supported);
+		sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
+		ongoing_call_status = ongoing;
+	} else if (!ongoing_call_status && ongoing) {
+		/* An ongoing call has started.*/
+		ongoing_call_status = ongoing;
+	}
+
+	DBG("OngoingCall status set to %d", ongoing_call_status);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	dbus_uint32_t size;
+
+	if (sim_card_conn_status == SIM_CONNECTED)
+		return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+				"Can't change msg size when connected.");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
+						DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	max_msg_size_supported = size;
+
+	DBG("MaxMessageSize set to %d", max_msg_size_supported);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	sim_card_conn_status = SIM_DISCONNECTED;
+	sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	dbus_uint32_t status;
+
+	DBG("status %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_CONNECTED)
+		return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+				"Can't change msg size when not connected.");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
+						DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	if (status) {
+		if (sim_card_conn_status == SIM_MISSING) {
+			sim_card_conn_status = SIM_CONNECTED;
+			sap_status_ind(sap_data,
+					SAP_STATUS_CHANGE_CARD_INSERTED);
+		}
+	} else {
+		sim_card_conn_status = SIM_MISSING;
+		sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
+	}
+
+	DBG("Card status changed to %d", status);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable dummy_methods[] = {
+	{ "OngoingCall",	"b",	"",	ongoing_call},
+	{ "MaxMessageSize",	"u",	"",	max_msg_size},
+	{ "Disconnect",		"",	"",	disconnect},
+	{ "CardStatus",		"u",	"",	card_status},
+	{ }
+};
+
 int sap_init(void)
 {
-	DBG("SAP driver init.");
+	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+	if (g_dbus_register_interface(connection, SAP_DUMMY_PATH,
+					SAP_DUMMY_IFACE,
+					dummy_methods, NULL,
+					NULL, NULL, NULL) == FALSE) {
+		error("sap-dummy interface %s init failed on path %s",
+			SAP_DUMMY_IFACE, SAP_DUMMY_PATH);
+		return -1;
+	}
+
 	return 0;
 }
 
 void sap_exit(void)
 {
-	DBG("SAP driver exit.");
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
-- 
1.7.1

^ permalink raw reply related

* [PATCH v3 3/5] Add support for SAP protocol
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz
In-Reply-To: <1300101380-10358-1-git-send-email-waldemar.rymarkiewicz@tieto.com>

Add new protocol features:
	* transfer APDu request/response
	* get ATR request/response
	* power sim of/on request/response
	* card reader status
	* set transport protocol
---
 sap/server.c |  417 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 400 insertions(+), 17 deletions(-)

diff --git a/sap/server.c b/sap/server.c
index 838938c..a954b6b 100644
--- a/sap/server.c
+++ b/sap/server.c
@@ -89,6 +89,44 @@ static void start_guard_timer(struct sap_connection *conn, guint interval);
 static void stop_guard_timer(struct sap_connection *conn);
 static gboolean guard_timeout(gpointer data);
 
+static size_t add_result_parameter(uint8_t result,
+					struct sap_parameter *param)
+{
+	param->id = SAP_PARAM_ID_RESULT_CODE;
+	param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
+	*param->val = result;
+
+	return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
+}
+
+static int is_power_sim_off_req_allowed(uint8_t processing_req)
+{
+	switch (processing_req) {
+	case SAP_NO_REQ:
+	case SAP_TRANSFER_APDU_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_POWER_SIM_ON_REQ:
+	case SAP_RESET_SIM_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int is_reset_sim_req_allowed(uint8_t processing_req)
+{
+	switch (processing_req) {
+	case SAP_NO_REQ:
+	case SAP_TRANSFER_APDU_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
 static int check_msg(struct sap_message *msg)
 {
 	if (!msg)
@@ -360,38 +398,159 @@ error_req:
 static void transfer_apdu_req(struct sap_connection *conn,
 					struct sap_parameter *param)
 {
-	DBG("SAP_APDU_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (!param)
+		goto error_rsp;
+
+	param->len = ntohs(param->len);
+
+	if (conn->state != SAP_STATE_CONNECTED &&
+			conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_APDU_REQ;
+	sap_transfer_apdu_req(conn, param);
+
+	return;
+
+error_rsp:
+	error("Processing error (param %p state %d pr 0x%02x)", param,
+					conn->state, conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void transfer_atr_req(struct sap_connection *conn)
 {
-	DBG("SAP_ATR_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_ATR_REQ;
+	sap_transfer_atr_req(conn);
+
+	return;
+
+error_rsp:
+	error("Processing error (state %d pr 0x%02x)", conn->state,
+						conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void power_sim_off_req(struct sap_connection *conn)
 {
-	DBG("SAP_SIM_OFF_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (!is_power_sim_off_req_allowed(conn->processing_req))
+		goto error_rsp;
+
+	conn->processing_req = SAP_POWER_SIM_OFF_REQ;
+	sap_power_sim_off_req(conn);
+
+	return;
+
+error_rsp:
+	error("Processing error (state %d pr 0x%02x)", conn->state,
+						conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void power_sim_on_req(struct sap_connection *conn)
 {
-	DBG("SAP_SIM_ON_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_POWER_SIM_ON_REQ;
+	sap_power_sim_on_req(conn);
+
+	return;
+
+error_rsp:
+	error("Processing error (state %d pr 0x%02x)", conn->state,
+						conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void reset_sim_req(struct sap_connection *conn)
 {
-	DBG("SAP_RESET_SIM_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (!is_reset_sim_req_allowed(conn->processing_req))
+		goto error_rsp;
+
+	conn->processing_req = SAP_RESET_SIM_REQ;
+	sap_reset_sim_req(conn);
+
+	return;
+
+error_rsp:
+	error("Processing error (state %d pr 0x%02x param)", conn->state,
+						conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void transfer_card_reader_status_req(struct sap_connection *conn)
 {
-	DBG("SAP_TRANSFER_CARD_READER_STATUS_REQUEST");
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
+	sap_transfer_card_reader_status_req(conn);
+
+	return;
+
+error_rsp:
+	error("Processing error (state %d pr 0x%02x)", conn->state,
+						conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void set_transport_protocol_req(struct sap_connection *conn,
 					struct sap_parameter *param)
 {
-	DBG("SAP_SET_TRANSPORT_PROTOCOL_REQUEST");
+	if (!param)
+		goto error_rsp;
+
+	DBG("conn %p state %d param %p", conn, conn->state, param);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
+	sap_set_transport_protocol_req(conn, param);
+
+	return;
+
+error_rsp:
+	error("Processing error (param %p state %d pr 0x%02x)", param,
+					conn->state, conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static void start_guard_timer(struct sap_connection *conn, guint interval)
@@ -556,49 +715,273 @@ int sap_disconnect_rsp(void *sap_device)
 int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
 					uint16_t length)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
+		return 0;
+
+	if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_APDU_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add APDU response. */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_RESPONSE_APDU;
+		param->len = htons(length);
+
+		size += PARAMETER_SIZE(length);
+
+		if (size > SAP_BUF_SIZE)
+			return -EOVERFLOW;
+
+		memcpy(param->val, apdu, length);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
 					uint16_t length)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
+			conn->processing_req, length);
+
+	if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
+		return 0;
+
+	if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_ATR_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add ATR response */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_ATR;
+		param->len = htons(length);
+		size += PARAMETER_SIZE(length);
+
+		if (size > SAP_BUF_SIZE)
+			return -EOVERFLOW;
+
+		memcpy(param->val, atr, length);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_POWER_SIM_OFF_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_POWER_SIM_ON_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_reset_sim_rsp(void *sap_device, uint8_t result)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+					conn->processing_req, result);
+
+	if (conn->processing_req != SAP_RESET_SIM_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_RESET_SIM_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
 						uint8_t status)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+					conn->processing_req, result);
+
+	if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add card reader status. */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_CARD_READER_STATUS;
+		param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+		*param->val = status;
+		size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+					conn->processing_req, result);
+
+	if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_error_rsp(void *sap_device)
 {
-	return 0;
+	struct sap_message msg;
+	struct sap_connection *conn = sap_device;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.id = SAP_ERROR_RESP;
+
+	return send_message(conn, &msg, sizeof(msg));
 }
 
 int sap_status_ind(void *sap_device, uint8_t status_change)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
+				status_change);
+
+	if (conn->state != SAP_STATE_CONNECTED &&
+			conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_STATUS_IND;
+	msg->nparam = 0x01;
+
+	/* Add status change. */
+	param->id  = SAP_PARAM_ID_STATUS_CHANGE;
+	param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+	*param->val = status_change;
+	size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+
+	return send_message(sap_device, buf, size);
 }
 
 static int handle_cmd(void *data, void *buf, size_t size)
@@ -633,7 +1016,7 @@ static int handle_cmd(void *data, void *buf, size_t size)
 		transfer_atr_req(conn);
 		return 0;
 	case SAP_POWER_SIM_OFF_REQ:
-	power_sim_off_req(conn);
+		power_sim_off_req(conn);
 		return 0;
 	case SAP_POWER_SIM_ON_REQ:
 		power_sim_on_req(conn);
-- 
1.7.1

^ permalink raw reply related

* [PATCH v3 2/5] Sim Access Profile connect/disconnect procedures
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz
In-Reply-To: <1300101380-10358-1-git-send-email-waldemar.rymarkiewicz@tieto.com>

Add support for SAP protocol features:
	* connect and disconnect requests
	* connect and disconnect responses
	* disconnect indication
	* timeouts for the valid connection
---
 sap/server.c |  417 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 407 insertions(+), 10 deletions(-)

diff --git a/sap/server.c b/sap/server.c
index 923ae20..838938c 100644
--- a/sap/server.c
+++ b/sap/server.c
@@ -50,14 +50,28 @@
 #define SAP_SERVER_CHANNEL	8
 #define SAP_BUF_SIZE		512
 
+#define PADDING4(x) (4 - (x & 0x03))
+#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
+
+#define SAP_NO_REQ 0xFF
+
+#define SAP_TIMER_GRACEFUL_DISCONNECT 30
+#define SAP_TIMER_NO_ACTIVITY 30
+
 enum {
 	SAP_STATE_DISCONNECTED,
+	SAP_STATE_CONNECT_IN_PROGRESS,
 	SAP_STATE_CONNECTED,
+	SAP_STATE_GRACEFUL_DISCONNECT,
+	SAP_STATE_IMMEDIATE_DISCONNECT,
+	SAP_STATE_CLIENT_DISCONNECT
 };
 
 struct sap_connection {
 	GIOChannel *io;
 	uint32_t state;
+	uint8_t processing_req;
+	guint timer_id;
 };
 
 struct sap_server {
@@ -71,6 +85,51 @@ struct sap_server {
 static DBusConnection *connection;
 static struct sap_server *server;
 
+static void start_guard_timer(struct sap_connection *conn, guint interval);
+static void stop_guard_timer(struct sap_connection *conn);
+static gboolean guard_timeout(gpointer data);
+
+static int check_msg(struct sap_message *msg)
+{
+	if (!msg)
+		return -EINVAL;
+
+	switch (msg->id) {
+	case SAP_CONNECT_REQ:
+		if (msg->nparam == 0x01 &&
+				msg->param->id == SAP_PARAM_ID_MAX_MSG_SIZE &&
+				ntohs(msg->param->len) == SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+			return 0;
+		break;
+	case SAP_TRANSFER_APDU_REQ:
+		if (msg->nparam == 0x01 &&
+				(msg->param->id == SAP_PARAM_ID_COMMAND_APDU ||
+				msg->param->id == SAP_PARAM_ID_COMMAND_APDU7816) &&
+				msg->param->len != 0x00)
+			return 0;
+		break;
+	case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+		if (msg->nparam == 0x01 &&
+				msg->param->id == SAP_PARAM_ID_TRANSPORT_PROTOCOL &&
+				ntohs(msg->param->len) == SAP_PARAM_ID_TRANSPORT_PROTOCOL_LEN &&
+				(*msg->param->val  == SAP_TRANSPORT_PROTOCOL_T0 ||
+				*msg->param->val == SAP_TRANSPORT_PROTOCOL_T1))
+			return 0;
+		break;
+	case SAP_DISCONNECT_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_POWER_SIM_OFF_REQ:
+	case SAP_POWER_SIM_ON_REQ:
+	case SAP_RESET_SIM_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		if (msg->nparam == 0x00)
+			return 0;
+		break;
+	}
+
+	return -EBADMSG;
+}
+
 static sdp_record_t *create_sap_record(uint8_t channel)
 {
 	sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
@@ -126,16 +185,176 @@ static sdp_record_t *create_sap_record(uint8_t channel)
 	return record;
 }
 
+static int send_message(struct sap_connection *conn, void *buf, size_t size)
+{
+	size_t written = 0;
+	GError *gerr = NULL;
+	GIOStatus gstatus;
+
+	if (!conn || !buf)
+		return -EINVAL;
+
+	DBG("size %zu", size);
+
+	gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
+						&gerr);
+	if (gstatus != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			g_error_free(gerr);
+
+		error("write error (0x%02x).", gstatus);
+		return -EINVAL;
+	}
+
+	if (written != size)
+		error("write error.(written %zu size %zu)", written, size);
+
+	return 0;
+}
+
+static int disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+	if (conn->state != SAP_STATE_GRACEFUL_DISCONNECT &&
+			conn->state != SAP_STATE_IMMEDIATE_DISCONNECT) {
+		error("Processing error (state %d pr 0x%02x)", conn->state,
+							conn->processing_req);
+		return -EPERM;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_DISCONNECT_IND;
+	msg->nparam = 0x01;
+
+	/* Add disconnection type param. */
+	param->id  = SAP_PARAM_ID_DISCONNECT_IND;
+	param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+	*param->val = disc_type;
+	size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+
+	return send_message(sap_device, buf, size);
+}
+
 static void connect_req(struct sap_connection *conn,
-					struct sap_parameter *param)
+				struct sap_parameter *param)
 {
-	DBG("SAP_CONNECT_REQUEST");
+	uint16_t maxmsgsize, *val;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (!param)
+		goto error_rsp;
+
+	if (conn->state != SAP_STATE_DISCONNECTED)
+		goto error_rsp;
+
+	stop_guard_timer(conn);
+
+	val = (uint16_t *) &param->val;
+	maxmsgsize = ntohs(*val);
+
+	DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
+
+	conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
+
+	if (maxmsgsize <= SAP_BUF_SIZE) {
+		conn->processing_req = SAP_CONNECT_REQ;
+		sap_connect_req(conn, maxmsgsize);
+	} else {
+		sap_connect_rsp(conn, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+								SAP_BUF_SIZE);
+	}
+
+	return;
+
+error_rsp:
+	error("Processing error (param %p state %d pr 0x%02x)", param,
+					conn->state, conn->processing_req);
+	sap_error_rsp(conn);
 }
 
 static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
 {
-	DBG("SAP_DISCONNECT_REQUEST");
-	return 0;
+	DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+	switch (disc_type) {
+	case SAP_DISCONNECTION_TYPE_GRACEFUL:
+		if (conn->state == SAP_STATE_DISCONNECTED ||
+				conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
+			goto error_req;
+
+		if (conn->state == SAP_STATE_CONNECTED) {
+			conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
+			conn->processing_req = SAP_NO_REQ;
+			disconnect_ind(conn, disc_type);
+
+			/* Start guard timer - timer will disconnect
+			 * connection if client doesn't do it. */
+			start_guard_timer(conn,
+					SAP_TIMER_GRACEFUL_DISCONNECT);
+
+			return 0;
+		}
+
+		/* Disconnection is ongoing - do nothing. */
+		return 0;
+
+	case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+		if (conn->state == SAP_STATE_DISCONNECTED ||
+				conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
+			goto error_req;
+
+		if (conn->state == SAP_STATE_CONNECTED ||
+				conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
+			conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
+			conn->processing_req = SAP_NO_REQ;
+
+			stop_guard_timer(conn);
+
+			disconnect_ind(conn, disc_type);
+			sap_disconnect_req(conn, 0);
+
+			return 0;
+		}
+
+		/* Disconnection is ongoing - do nothing. */
+		return 0;
+
+	case SAP_DISCONNECTION_TYPE_CLIENT:
+		if (conn->state != SAP_STATE_CONNECTED &&
+				conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+			goto error_rsp;
+
+		conn->state = SAP_STATE_CLIENT_DISCONNECT;
+		conn->processing_req = SAP_NO_REQ;
+
+		stop_guard_timer(conn);
+
+		sap_disconnect_req(conn, 0);
+
+		return 0;
+
+	default:
+		error("Unknown disconnection type (0x%02x).", disc_type);
+		return -EINVAL;
+	}
+
+error_rsp:
+	sap_error_rsp(conn);
+error_req:
+	error("Processing error (state %d pr 0x%02x)", conn->state,
+						conn->processing_req);
+	return -EPERM;
 }
 
 static void transfer_apdu_req(struct sap_connection *conn,
@@ -175,13 +394,162 @@ static void set_transport_protocol_req(struct sap_connection *conn,
 	DBG("SAP_SET_TRANSPORT_PROTOCOL_REQUEST");
 }
 
+static void start_guard_timer(struct sap_connection *conn, guint interval)
+{
+	if (!conn)
+		return;
+
+	if (!conn->timer_id)
+		conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
+							conn);
+	else
+		error("Timer is already active.");
+}
+
+static void stop_guard_timer(struct sap_connection *conn)
+{
+	if (conn  && conn->timer_id) {
+		g_source_remove(conn->timer_id);
+		conn->timer_id = 0;
+	}
+}
+
+static gboolean guard_timeout(gpointer data)
+{
+	struct sap_connection *conn = data;
+
+	if (!conn)
+		return FALSE;
+
+	DBG("conn %p state %d pr 0x%02x", conn, conn->state,
+					conn->processing_req);
+
+	conn->timer_id = 0;
+
+	switch (conn->state) {
+	case SAP_STATE_DISCONNECTED:
+		/* Client opened RFCOMM channel but didn't send CONNECT_REQ,
+		 * in fixed time or client disconnected SAP connection but
+		 * didn't closed RFCOMM channel in fixed time.*/
+		if (conn->io) {
+			g_io_channel_shutdown(conn->io, TRUE, NULL);
+			g_io_channel_unref(conn->io);
+		}
+
+		break;
+
+	case SAP_STATE_GRACEFUL_DISCONNECT:
+		/* Client didn't disconnect SAP connection in fixed time,
+		 * so close SAP connection immediately. */
+		disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+		break;
+
+	default:
+		error("Unexpected state (%d).", conn->state);
+		break;
+	}
+
+	return FALSE;
+}
+
 int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
 {
-	return 0;
+	struct sap_connection *conn = sap_device;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x status 0x%02x", conn->state,
+					conn->processing_req, status);
+
+	if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+		return -EPERM;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_CONNECT_RESP;
+	msg->nparam = 0x01;
+
+	/* Add connection status */
+	param->id = SAP_PARAM_ID_CONN_STATUS;
+	param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
+	*param->val = status;
+	size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
+
+	/* Add MaxMsgSize */
+	if (maxmsgsize && (status == SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED ||
+			status == SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL)) {
+		uint16_t *len;
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
+		param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+		len = (uint16_t *) &param->val;
+		*len = htons(maxmsgsize);
+		size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+	}
+
+	if (status == SAP_STATUS_OK) {
+		gboolean connected = TRUE;
+		emit_property_changed(connection, server->path,
+						SAP_SERVER_INTERFACE,
+			"Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+		conn->state = SAP_STATE_CONNECTED;
+	} else {
+		conn->state = SAP_STATE_DISCONNECTED;
+
+		/* Timer will shutdown channel if client doesn't send
+		 * CONNECT_REQ or doesn't shutdown channel itself.*/
+		start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(sap_device, buf, size);
 }
 
 int sap_disconnect_rsp(void *sap_device)
 {
+	struct sap_connection *conn = sap_device;
+	struct sap_message msg;
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	switch (conn->state) {
+	case SAP_STATE_CLIENT_DISCONNECT:
+		memset(&msg, 0, sizeof(msg));
+		msg.id = SAP_DISCONNECT_RESP;
+
+		conn->state = SAP_STATE_DISCONNECTED;
+		conn->processing_req = SAP_NO_REQ;
+
+		/* Timer will close channel if client doesn't do it.*/
+		start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+		return send_message(sap_device, &msg, sizeof(msg));
+
+	case SAP_STATE_IMMEDIATE_DISCONNECT:
+		conn->state = SAP_STATE_DISCONNECTED;
+		conn->processing_req = SAP_NO_REQ;
+
+		if (conn->io) {
+			g_io_channel_shutdown(conn->io, TRUE, NULL);
+			g_io_channel_unref(conn->io);
+		}
+
+		return 0;
+
+	default:
+		break;
+	}
+
 	return 0;
 }
 
@@ -242,11 +610,14 @@ static int handle_cmd(void *data, void *buf, size_t size)
 		return -EINVAL;
 
 	if (size < sizeof(struct sap_message))
-		return -EINVAL;
+		goto error_rsp;
 
 	if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
 			sizeof(struct sap_parameter) + 4))
-		return -EBADMSG;
+		goto error_rsp;
+
+	if (check_msg(msg) < 0)
+		goto error_rsp;
 
 	switch (msg->id) {
 	case SAP_CONNECT_REQ:
@@ -278,10 +649,13 @@ static int handle_cmd(void *data, void *buf, size_t size)
 		return 0;
 	default:
 		DBG("SAP unknown message.");
-		return -ENOMSG;
+		break;
 	}
 
-	return -1;
+error_rsp:
+	DBG("Bad request message format.");
+	sap_error_rsp(conn);
+	return -EBADMSG;
 }
 
 static void sap_conn_remove(struct sap_connection *conn)
@@ -348,6 +722,20 @@ static void sap_io_destroy(void *data)
 	DBG("conn %p", conn);
 
 	if (conn && conn->io) {
+		gboolean connected = FALSE;
+
+		stop_guard_timer(conn);
+
+		if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+			emit_property_changed(connection, server->path,
+					SAP_SERVER_INTERFACE, "Connected",
+					DBUS_TYPE_BOOLEAN, &connected);
+
+		if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+				conn->state == SAP_STATE_CONNECTED ||
+				conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
+			sap_disconnect_req(NULL, 1);
+
 		conn->io = NULL;
 		sap_conn_remove(conn);
 	}
@@ -362,6 +750,10 @@ static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
 	if (!conn)
 		return;
 
+	/* Timer will shutdown the channel in case of lack of client
+	   activity */
+	start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
 	g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
 			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 			sap_io_cb, conn, sap_io_destroy);
@@ -471,6 +863,10 @@ static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
 	if (!server->conn)
 		return message_failed(msg, "Client already disconnected");
 
+	if (disconnect_req(server->conn, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
+		return g_dbus_create_error(msg, ERROR_INTERFACE	".Failed",
+				"There is no active connection");
+
 	return dbus_message_new_method_return(msg);
 }
 
@@ -497,7 +893,8 @@ static DBusMessage *get_properties(DBusConnection *c,
 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
 
-	connected = (conn->state == SAP_STATE_CONNECTED);
+	connected = (conn->state == SAP_STATE_CONNECTED ||
+			conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
 	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
 
 	dbus_message_iter_close_container(&iter, &dict);
-- 
1.7.1

^ permalink raw reply related

* [PATCH v3 1/5] Sim Access Profile Server
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz
In-Reply-To: <1300101380-10358-1-git-send-email-waldemar.rymarkiewicz@tieto.com>

Add a Sim Access Server to the SAP plugin and a framework for the dummy
sap driver as well.

	* add the server register and unregister rutines
	* add server listening socket setup
	* add SAP DBus API
	* add prototypes for SAP protocol implementation
	* add skeleton of dummy SIM driver
---
 Makefile.am     |    3 +-
 sap/sap-dummy.c |   84 ++++++++
 sap/sap.h       |  187 +++++++++++++++++
 sap/server.c    |  615 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 880 insertions(+), 9 deletions(-)
 create mode 100644 sap/sap-dummy.c
 create mode 100644 sap/sap.h

diff --git a/Makefile.am b/Makefile.am
index 49c45c1..af374a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -147,7 +147,8 @@ if SAPPLUGIN
 builtin_modules += sap
 builtin_sources += sap/main.c \
 			sap/manager.h sap/manager.c \
-			sap/server.h sap/server.c
+			sap/server.h sap/server.c \
+			sap/sap.h sap/sap-dummy.c
 endif
 
 if INPUTPLUGIN
diff --git a/sap/sap-dummy.c b/sap/sap-dummy.c
new file mode 100644
index 0000000..3c0305f
--- /dev/null
+++ b/sap/sap-dummy.c
@@ -0,0 +1,84 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "log.h"
+#include "sap.h"
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+	sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
+	sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+	sap_disconnect_rsp(sap_device);
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+	sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+	sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+	sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+	sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+	sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+	sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+	sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK,
+						ICC_READER_CARD_POWERED_ON);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+					struct sap_parameter *param)
+{
+	sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+int sap_init(void)
+{
+	DBG("SAP driver init.");
+	return 0;
+}
+
+void sap_exit(void)
+{
+	DBG("SAP driver exit.");
+}
diff --git a/sap/sap.h b/sap/sap.h
new file mode 100644
index 0000000..f94ac26
--- /dev/null
+++ b/sap/sap.h
@@ -0,0 +1,187 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdint.h>
+#include <glib.h>
+
+#define SAP_VERSION 0x0101
+
+/* Connection Status - SAP v1.1 section 5.2.2 */
+enum sap_status {
+	SAP_STATUS_OK				= 0x00,
+	SAP_STATUS_CONNECTION_FAILED		= 0x01,
+	SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED	= 0x02,
+	SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL	= 0x03,
+	SAP_STATUS_OK_ONGOING_CALL		= 0x04
+};
+
+/* Disconnection Type - SAP v1.1 section 5.2.3 */
+enum sap_disconnection_type {
+	SAP_DISCONNECTION_TYPE_GRACEFUL		= 0x00,
+	SAP_DISCONNECTION_TYPE_IMMEDIATE	= 0x01,
+	SAP_DISCONNECTION_TYPE_CLIENT		= 0xFF
+};
+
+/* Result codes - SAP v1.1 section 5.2.4 */
+enum sap_result {
+	SAP_RESULT_OK			= 0x00,
+	SAP_RESULT_ERROR_NO_REASON	= 0x01,
+	SAP_RESULT_ERROR_NOT_ACCESSIBLE	= 0x02,
+	SAP_RESULT_ERROR_POWERED_OFF	= 0x03,
+	SAP_RESULT_ERROR_CARD_REMOVED	= 0x04,
+	SAP_RESULT_ERROR_POWERED_ON	= 0x05,
+	SAP_RESULT_ERROR_NO_DATA	= 0x06,
+	SAP_RESULT_NOT_SUPPORTED	= 0x07
+};
+
+/* Status Change - SAP v1.1 section 5.2.8 */
+enum sap_status_change {
+	SAP_STATUS_CHANGE_UNKNOWN_ERROR		= 0x00,
+	SAP_STATUS_CHANGE_CARD_RESET		= 0x01,
+	SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE	= 0x02,
+	SAP_STATUS_CHANGE_CARD_REMOVED		= 0x03,
+	SAP_STATUS_CHANGE_CARD_INSERTED		= 0x04,
+	SAP_STATUS_CHANGE_CARD_RECOVERED	= 0x05
+};
+
+/* Message format - SAP v1.1 section 5.1 */
+struct sap_parameter {
+	uint8_t id;
+	uint8_t reserved;
+	uint16_t len;
+	uint8_t val[0];
+	/*
+	 * Padding bytes 0-3 bytes
+	 */
+} __attribute__((packed));
+
+struct sap_message {
+	uint8_t id;
+	uint8_t nparam;
+	uint16_t reserved;
+	struct sap_parameter param[0];
+} __attribute__((packed));
+
+enum {
+	ICC_READER_UNSPECIFIED_ERROR, /* No further information available */
+	ICC_READER_NOT_PRESENT,       /* Card Reader removed or not present */
+	ICC_READER_BUSY,              /* Card Reader in use */
+	ICC_READER_CARD_POWERED_ON,   /* Card in reader and is powered on */
+	ICC_READER_DEACTIVATED,       /* Card Reader deactivated */
+	ICC_READER_CARD_POWERED_OFF,  /* Card in reader, but powered off */
+	ICC_READER_NO_CARD,           /* No card in reader */
+	ICC_READER_LAST
+};
+
+#define SAP_BUF_SIZE		512
+#define SAP_MSG_HEADER_SIZE	4
+
+enum sap_protocol {
+	SAP_CONNECT_REQ		= 0x00,
+	SAP_CONNECT_RESP	= 0x01,
+	SAP_DISCONNECT_REQ	= 0x02,
+	SAP_DISCONNECT_RESP	= 0x03,
+	SAP_DISCONNECT_IND	= 0x04,
+	SAP_TRANSFER_APDU_REQ	= 0x05,
+	SAP_TRANSFER_APDU_RESP	= 0x06,
+	SAP_TRANSFER_ATR_REQ	= 0x07,
+	SAP_TRANSFER_ATR_RESP	= 0x08,
+	SAP_POWER_SIM_OFF_REQ	= 0x09,
+	SAP_POWER_SIM_OFF_RESP	= 0x0A,
+	SAP_POWER_SIM_ON_REQ	= 0x0B,
+	SAP_POWER_SIM_ON_RESP	= 0x0C,
+	SAP_RESET_SIM_REQ	= 0x0D,
+	SAP_RESET_SIM_RESP	= 0x0E,
+	SAP_TRANSFER_CARD_READER_STATUS_REQ	= 0x0F,
+	SAP_TRANSFER_CARD_READER_STATUS_RESP	= 0x10,
+	SAP_STATUS_IND	= 0x11,
+	SAP_ERROR_RESP	= 0x12,
+	SAP_SET_TRANSPORT_PROTOCOL_REQ	= 0x13,
+	SAP_SET_TRANSPORT_PROTOCOL_RESP	= 0x14
+};
+
+/* Parameters Ids - SAP 1.1 section 5.2 */
+enum sap_param_id {
+	SAP_PARAM_ID_MAX_MSG_SIZE	= 0x00,
+	SAP_PARAM_ID_CONN_STATUS	= 0x01,
+	SAP_PARAM_ID_RESULT_CODE	= 0x02,
+	SAP_PARAM_ID_DISCONNECT_IND	= 0x03,
+	SAP_PARAM_ID_COMMAND_APDU	= 0x04,
+	SAP_PARAM_ID_COMMAND_APDU7816	= 0x10,
+	SAP_PARAM_ID_RESPONSE_APDU	= 0x05,
+	SAP_PARAM_ID_ATR		= 0x06,
+	SAP_PARAM_ID_CARD_READER_STATUS	= 0x07,
+	SAP_PARAM_ID_STATUS_CHANGE	= 0x08,
+	SAP_PARAM_ID_TRANSPORT_PROTOCOL	= 0x09
+};
+
+#define SAP_PARAM_ID_MAX_MSG_SIZE_LEN		0x02
+#define SAP_PARAM_ID_CONN_STATUS_LEN		0x01
+#define SAP_PARAM_ID_RESULT_CODE_LEN		0x01
+#define SAP_PARAM_ID_DISCONNECT_IND_LEN		0x01
+#define SAP_PARAM_ID_CARD_READER_STATUS_LEN	0x01
+#define SAP_PARAM_ID_STATUS_CHANGE_LEN		0x01
+#define SAP_PARAM_ID_TRANSPORT_PROTOCOL_LEN	0x01
+
+/* Transport Protocol - SAP v1.1 section 5.2.9 */
+enum sap_transport_protocol {
+	SAP_TRANSPORT_PROTOCOL_T0 = 0x00,
+	SAP_TRANSPORT_PROTOCOL_T1 = 0x01
+};
+
+/*SAP driver init and exit routines. Implemented by sap-*.c */
+int sap_init(void);
+void sap_exit(void);
+
+/* SAP requests implemented by sap-*.c */
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize);
+void sap_disconnect_req(void *sap_device, uint8_t linkloss);
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param);
+void sap_transfer_atr_req(void *sap_device);
+void sap_power_sim_off_req(void *sap_device);
+void sap_power_sim_on_req(void *sap_device);
+void sap_reset_sim_req(void *sap_device);
+void sap_transfer_card_reader_status_req(void *sap_device);
+void sap_set_transport_protocol_req(void *sap_device,
+					struct sap_parameter *param);
+
+/*SAP responses to SAP requests. Implemented by server.c */
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize);
+int sap_disconnect_rsp(void *sap_device);
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result,
+				uint8_t *sap_apdu_resp, uint16_t length);
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result,
+				uint8_t *sap_atr, uint16_t length);
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result);
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result);
+int sap_reset_sim_rsp(void *sap_device, uint8_t result);
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+						uint8_t status);
+int sap_error_rsp(void *sap_device);
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result);
+
+/* Event indication. Implemented by server.c*/
+int sap_status_ind(void *sap_device, uint8_t status_change);
+
diff --git a/sap/server.c b/sap/server.c
index 2728778..923ae20 100644
--- a/sap/server.c
+++ b/sap/server.c
@@ -1,13 +1,18 @@
 /*
  *  BlueZ - Bluetooth protocol stack for Linux
  *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
  *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto Poland
  *
- *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> for ST-Ericsson.
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; version 2 of the License.
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,30 +24,624 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include "bluetooth.h"
-#include "log.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
 
+#include "adapter.h"
+#include "btio.h"
+#include "sdpd.h"
+#include "log.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "sap.h"
 #include "server.h"
 
+#define SAP_SERVER_INTERFACE	"org.bluez.SimAccess"
+#define SAP_UUID		"0000112D-0000-1000-8000-00805F9B34FB"
+#define SAP_SERVER_CHANNEL	8
+#define SAP_BUF_SIZE		512
+
+enum {
+	SAP_STATE_DISCONNECTED,
+	SAP_STATE_CONNECTED,
+};
+
+struct sap_connection {
+	GIOChannel *io;
+	uint32_t state;
+};
+
+struct sap_server {
+	bdaddr_t src;
+	char *path;
+	uint32_t record_id;
+	GIOChannel *listen_io;
+	struct sap_connection *conn;
+};
+
+static DBusConnection *connection;
+static struct sap_server *server;
+
+static sdp_record_t *create_sap_record(uint8_t channel)
+{
+	sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+	uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm;
+	sdp_profile_desc_t profile;
+	sdp_record_t *record;
+	sdp_data_t *ch;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &sap_uuid);
+	sdp_uuid16_create(&gt_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &gt_uuid);
+
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+	profile.version = SAP_VERSION;
+	profiles = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, profiles);
+	sdp_list_free(profiles, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm);
+	ch = sdp_data_alloc(SDP_UINT8, &channel);
+	proto[1] = sdp_list_append(proto[1], ch);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "SIM Access Server",
+			NULL, NULL);
+
+	sdp_data_free(ch);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static void connect_req(struct sap_connection *conn,
+					struct sap_parameter *param)
+{
+	DBG("SAP_CONNECT_REQUEST");
+}
+
+static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
+{
+	DBG("SAP_DISCONNECT_REQUEST");
+	return 0;
+}
+
+static void transfer_apdu_req(struct sap_connection *conn,
+					struct sap_parameter *param)
+{
+	DBG("SAP_APDU_REQUEST");
+}
+
+static void transfer_atr_req(struct sap_connection *conn)
+{
+	DBG("SAP_ATR_REQUEST");
+}
+
+static void power_sim_off_req(struct sap_connection *conn)
+{
+	DBG("SAP_SIM_OFF_REQUEST");
+}
+
+static void power_sim_on_req(struct sap_connection *conn)
+{
+	DBG("SAP_SIM_ON_REQUEST");
+}
+
+static void reset_sim_req(struct sap_connection *conn)
+{
+	DBG("SAP_RESET_SIM_REQUEST");
+}
+
+static void transfer_card_reader_status_req(struct sap_connection *conn)
+{
+	DBG("SAP_TRANSFER_CARD_READER_STATUS_REQUEST");
+}
+
+static void set_transport_protocol_req(struct sap_connection *conn,
+					struct sap_parameter *param)
+{
+	DBG("SAP_SET_TRANSPORT_PROTOCOL_REQUEST");
+}
+
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
+{
+	return 0;
+}
+
+int sap_disconnect_rsp(void *sap_device)
+{
+	return 0;
+}
+
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
+					uint16_t length)
+{
+	return 0;
+}
+
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
+					uint16_t length)
+{
+	return 0;
+}
+
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
+{
+	return 0;
+}
+
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
+{
+	return 0;
+}
+
+int sap_reset_sim_rsp(void *sap_device, uint8_t result)
+{
+	return 0;
+}
+
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+						uint8_t status)
+{
+	return 0;
+}
+
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
+{
+	return 0;
+}
+
+int sap_error_rsp(void *sap_device)
+{
+	return 0;
+}
+
+int sap_status_ind(void *sap_device, uint8_t status_change)
+{
+	return 0;
+}
+
+static int handle_cmd(void *data, void *buf, size_t size)
+{
+	struct sap_message *msg = buf;
+	struct sap_connection *conn = data;
+
+	if (!conn)
+		return -EINVAL;
+
+	if (size < sizeof(struct sap_message))
+		return -EINVAL;
+
+	if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
+			sizeof(struct sap_parameter) + 4))
+		return -EBADMSG;
+
+	switch (msg->id) {
+	case SAP_CONNECT_REQ:
+		connect_req(conn, msg->param);
+		return 0;
+	case SAP_DISCONNECT_REQ:
+		disconnect_req(conn, SAP_DISCONNECTION_TYPE_CLIENT);
+		return 0;
+	case SAP_TRANSFER_APDU_REQ:
+		transfer_apdu_req(conn, msg->param);
+		return 0;
+	case SAP_TRANSFER_ATR_REQ:
+		transfer_atr_req(conn);
+		return 0;
+	case SAP_POWER_SIM_OFF_REQ:
+	power_sim_off_req(conn);
+		return 0;
+	case SAP_POWER_SIM_ON_REQ:
+		power_sim_on_req(conn);
+		return 0;
+	case SAP_RESET_SIM_REQ:
+		reset_sim_req(conn);
+		return 0;
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		transfer_card_reader_status_req(conn);
+		return 0;
+	case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+		set_transport_protocol_req(conn, msg->param);
+		return 0;
+	default:
+		DBG("SAP unknown message.");
+		return -ENOMSG;
+	}
+
+	return -1;
+}
+
+static void sap_conn_remove(struct sap_connection *conn)
+{
+	DBG("conn %p", conn);
+
+	if (!conn)
+		return;
+
+	if (conn->io) {
+		g_io_channel_shutdown(conn->io, TRUE, NULL);
+		g_io_channel_unref(conn->io);
+	}
+
+	conn->io = NULL;
+	g_free(conn);
+	server->conn = NULL;
+}
+
+static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	char buf[SAP_BUF_SIZE];
+	size_t bytes_read = 0;
+	GError *gerr = NULL;
+	GIOStatus gstatus;
+
+	DBG("io %p", io);
+
+	if (cond & G_IO_NVAL) {
+		DBG("ERR (G_IO_NVAL) on rfcomm socket.");
+		return FALSE;
+	}
+
+	if (cond & G_IO_ERR) {
+		DBG("ERR (G_IO_ERR) on rfcomm socket.");
+		return FALSE;
+	}
+
+	if (cond & G_IO_HUP) {
+		DBG("HUP on rfcomm socket.");
+		return FALSE;
+	}
+
+	gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
+				&bytes_read, &gerr);
+
+	if (gstatus != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			g_error_free(gerr);
+
+		return TRUE;
+	}
+
+	if (handle_cmd(data, buf, bytes_read) < 0)
+		error("Invalid SAP message.");
+
+	return TRUE;
+}
+
+static void sap_io_destroy(void *data)
+{
+	struct sap_connection *conn = data;
+
+	DBG("conn %p", conn);
+
+	if (conn && conn->io) {
+		conn->io = NULL;
+		sap_conn_remove(conn);
+	}
+}
+
+static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
+{
+	struct sap_connection *conn = data;
+
+	DBG("io %p gerr %p data %p ", io, gerr, data);
+
+	if (!conn)
+		return;
+
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+			sap_io_cb, conn, sap_io_destroy);
+}
+
+static void connect_auth_cb(DBusError *derr, void *data)
+{
+	struct sap_connection *conn = data;
+	GError *gerr = NULL;
+
+	DBG("derr %p data %p ", derr, data);
+
+	if (!conn)
+		return;
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access denied: %s", derr->message);
+		sap_conn_remove(conn);
+		return;
+	}
+
+	if (!bt_io_accept(conn->io, sap_connect_cb, conn, NULL, &gerr)) {
+		error("bt_io_accept: %s", gerr->message);
+		g_error_free(gerr);
+		sap_conn_remove(conn);
+		return;
+	}
+
+	DBG("Client has been authorized.");
+}
+
+static void connect_confirm_cb(GIOChannel *io, gpointer data)
+{
+	struct sap_connection *conn = server->conn;
+	GError *gerr = NULL;
+	bdaddr_t src, dst;
+	int err;
+
+	DBG("io %p data %p ", io, data);
+
+	if (!io)
+		return;
+
+	if (conn) {
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	conn = g_try_new0(struct sap_connection, 1);
+	if (!conn) {
+		error("Can't allocate memory for incomming SAP connection.");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	g_io_channel_set_encoding(io, NULL, NULL);
+	g_io_channel_set_buffered(io, FALSE);
+
+	server->conn = conn;
+	conn->io = g_io_channel_ref(io);
+	conn->state = SAP_STATE_DISCONNECTED;
+
+	bt_io_get(io, BT_IO_RFCOMM, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		sap_conn_remove(conn);
+		return;
+	}
+
+	err = btd_request_authorization(&src, &dst, SAP_UUID,
+					connect_auth_cb, conn);
+
+	if (err < 0) {
+		DBG("Authorization denied: %d %s", err,  strerror(err));
+		sap_conn_remove(conn);
+		return;
+	}
+
+	DBG("SAP incoming connection (sock %d) authorization.",
+				g_io_channel_unix_get_fd(io));
+}
+
+static inline DBusMessage *message_failed(DBusMessage *msg,
+					const char *description)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+				"%s", description);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct sap_server *server = data;
+
+	DBG("server %p", server);
+
+	if (!server)
+		return message_failed(msg, "Server internal error.");
+
+	DBG("conn %p", server->conn);
+
+	if (!server->conn)
+		return message_failed(msg, "Client already disconnected");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *c,
+				DBusMessage *msg, void *data)
+{
+	struct sap_connection *conn = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	dbus_bool_t connected;
+
+	if (!conn)
+		return message_failed(msg, "Server internal error.");
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	connected = (conn->state == SAP_STATE_CONNECTED);
+	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static GDBusMethodTable server_methods[] = {
+	{"GetProperties", "", "a{sv}", get_properties},
+	{"Disconnect", "", "", disconnect},
+	{ }
+};
+
+static GDBusSignalTable server_signals[] = {
+	{ "PropertyChanged", "sv"},
+	{ }
+};
+
+static void server_free(struct sap_server *server)
+{
+	if (!server)
+		return;
+
+	sap_conn_remove(server->conn);
+	g_free(server->path);
+	g_free(server);
+}
+
+static void destroy_sap_interface(void *data)
+{
+	struct sap_server *server = data;
+
+	DBG("Unregistered interface %s on path %s",
+			SAP_SERVER_INTERFACE, server->path);
+
+	server_free(server);
+}
+
 int sap_server_register(const char *path, bdaddr_t *src)
 {
-	DBG("Register SAP server.");
+	sdp_record_t *record = NULL;
+	GError *gerr = NULL;
+	GIOChannel *io;
+
+	if (sap_init() < 0) {
+		error("Sap driver initialization failed.");
+		return -1;
+	}
+
+	server = g_try_new0(struct sap_server, 1);
+	if (!server) {
+		sap_exit();
+		return -ENOMEM;
+	}
+
+	bacpy(&server->src, src);
+	server->path = g_strdup(path);
+
+	record = create_sap_record(SAP_SERVER_CHANNEL);
+	if (!record) {
+		error("Creating SAP SDP record failed.");
+		goto sdp_err;
+	}
+
+	if (add_record_to_server(&server->src, record) < 0) {
+		error("Adding SAP SDP record to the SDP server failed.");
+		sdp_record_free(record);
+		goto sdp_err;
+	}
+
+	server->record_id = record->handle;
+
+	io = bt_io_listen(BT_IO_RFCOMM, NULL, connect_confirm_cb, server,
+			NULL, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &server->src,
+			BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
+			BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+			BT_IO_OPT_MASTER, TRUE,
+			BT_IO_OPT_INVALID);
+
+	if (!io) {
+		error("Can't listen at channel %d.", SAP_SERVER_CHANNEL);
+		g_error_free(gerr);
+		goto server_err;
+	}
+
+	DBG("Listen socket 0x%02x", g_io_channel_unix_get_fd(io));
+
+	server->listen_io = io;
+	server->conn = NULL;
+
+	if (!g_dbus_register_interface(connection, path, SAP_SERVER_INTERFACE,
+				server_methods, server_signals, NULL,
+				server, destroy_sap_interface)) {
+		error("D-Bus failed to register %s interface",
+						SAP_SERVER_INTERFACE);
+		goto server_err;
+	}
+
 	return 0;
+
+server_err:
+	remove_record_from_server(server->record_id);
+sdp_err:
+	server_free(server);
+	server = NULL;
+	sap_exit();
+
+	return -1;
 }
 
 int sap_server_unregister(const char *path)
 {
-	DBG("Unregister SAP server.");
+	if (!server)
+		return -EINVAL;
+
+	remove_record_from_server(server->record_id);
+
+	if (server->conn)
+		sap_conn_remove(server->conn);
+
+	if (server->listen_io) {
+		g_io_channel_shutdown(server->listen_io, TRUE, NULL);
+		g_io_channel_unref(server->listen_io);
+		server->listen_io = NULL;
+	}
+
+	g_dbus_unregister_interface(connection, path, SAP_SERVER_INTERFACE);
+
+	server_free(server);
+	server = NULL;
+	sap_exit();
+
 	return 0;
 }
 
 int sap_server_init(DBusConnection *conn)
 {
-	DBG("Init SAP server.");
+	connection = dbus_connection_ref(conn);
 	return 0;
 }
 
 void sap_server_exit(void)
 {
-	DBG("Exit SAP server.");
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
-- 
1.7.1

^ permalink raw reply related

* [PATCH v3 0/5] SIM Access Profile v3
From: Waldemar Rymarkiewicz @ 2011-03-14 11:16 UTC (permalink / raw)
  To: linux-bluetooth, Johan Hedberg
  Cc: padovan, Marcel Holtmann, Waldemar Rymarkiewicz

Hi Johan,

Thanks for your last comments. I've fixed them and as you suggested I split the SAP server patch into 3 smaller patches.
I hope this will help with a review.

Thanks,
/Waldek


Waldemar Rymarkiewicz (5):
  Sim Access Profile Server
  Sim Access Profile connect/disconnect procedures
  Add support for SAP protocol
  Sim Access Profile dummy driver
  Sim Access Profile test scripts

 .gitignore       |    1 +
 Makefile.am      |   14 +-
 acinclude.m4     |    6 +
 sap/sap-dummy.c  |  328 +++++++++++++
 sap/sap.h        |  187 ++++++++
 sap/server.c     | 1395 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 test/sap.py      |  945 ++++++++++++++++++++++++++++++++++++
 test/test-sap.py |  140 ++++++
 8 files changed, 3006 insertions(+), 10 deletions(-)
 create mode 100644 sap/sap-dummy.c
 create mode 100644 sap/sap.h
 create mode 100644 test/sap.py
 create mode 100755 test/test-sap.py

^ permalink raw reply

* Re: how to set adapter to master with bluez 4.69?
From: Brian J. Murrell @ 2011-03-14 11:00 UTC (permalink / raw)
  To: Brad Midgley; +Cc: public-linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <AANLkTi=G2_hvZrZ=pG6naJ2fw7n4_T8=3+heV29vRPRD@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1255 bytes --]

On 11-03-13 11:46 PM, Brad Midgley wrote:
> Brian,

Hi Brad,

> 
>>> $ sudo hcitool sr 00:1F:20:0F:30:6A slave
>>> Switch role request failed: Input/output error
> 
> Sorry I didn't look at the gui again.

No worries.  TBH, a gui knob would be nice, but I'd settle for just
being able to set it manually for the time being.

> Were you able to use hcitool to switch the role to master manually?

No, I never have been able to.  I was playing with it again this morning
and I can't even seem to get a list of remote devices any more.  I know
there is at least one -- a mouse, which I am using right at this moment.

$ sudo hcitool scan
Scanning ...
$ sudo hcitool inq
Inquiring ...
brian@pc:/etc/shorewall/gw-new$ sudo hcitool dev
Devices:
	hci0	00:02:72:1E:E0:12
$ sudo hcitool con
Connections:
	> ACL 00:1F:20:0F:30:6A handle 11 state 1 lm MASTER

I'd guess that means the mouse is at address 00:1F:20:0F:30:6A and is
master currently, yes?

$ sudo hcitool sr 00:1F:20:0F:30:6A SLAVE
Switch role request failed: Input/output error
$ hcitool sr 00:1F:20:0F:30:6A SLAVE
Switch role request failed: Operation not permitted

Any advise how to proceed, even just to set the adapter to master manually?

Cheers,
b.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* Re: how to set adapter to master with bluez 4.69?
From: Brad Midgley @ 2011-03-14  3:46 UTC (permalink / raw)
  To: Brian J. Murrell; +Cc: public-linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <AANLkTimzHs1=z11GJ-ye10o_6PfoQ_R1RkDeS1jCVue2@mail.gmail.com>



Brian,

>> $ sudo hcitool sr 00:1F:20:0F:30:6A slave
>> Switch role request failed: Input/output error

Sorry I didn't look at the gui again.

Were you able to use hcitool to switch the role to master manually?

-- 
Brad Midgley


^ permalink raw reply

* Re: [2.6.38-rc8, patch] fix hci_dev_list locking
From: Daniel J Blueman @ 2011-03-13  3:59 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: Linux Kernel, linux-bluetooth
In-Reply-To: <20110312014749.GB2164@joana>

Hi Gustavo,

On 12 March 2011 09:47, Gustavo F. Padovan <padovan@profusion.mobi> wrote:
> Hi Daniel,
>
> * Daniel J Blueman <daniel.blueman@gmail.com> [2011-03-09 00:07:54 +0800]:
>
>> No response from original post, and it missed getting taken into -rc7
>> and -rc8, so repost:
>>
>> Release acquired lock on error path, fixing potential hang up.
>
> I already have a patch like this one in my tree, thanks anyway. :)

I guess I'm not looking in the right public tree, but I still couldn't
detect the fix in:
http://git.kernel.org/?p=linux/kernel/git/padovan/bluetooth-2.6.git
or:
http://git.kernel.org/?p=linux/kernel/git/padovan/bluetooth-next-2.6.git

Thanks,
  Daniel
-- 
Daniel J Blueman

^ permalink raw reply

* Re: re "bluetooth disabled" and "[BUG] usb problems in .38-rc3+"
From: Justin P. Mattock @ 2011-03-12 17:44 UTC (permalink / raw)
  To: Ed Tomlinson, Mikko Vinni, linux-bluetooth
In-Reply-To: <20110312013349.GA2164@joana>

On 03/11/2011 05:33 PM, Gustavo F. Padovan wrote:
> * Johan Hedberg<johan.hedberg@gmail.com>  [2011-03-10 09:32:06 +0200]:
>
>> Hi,
>>
>> On Wed, Mar 09, 2011, Ed Tomlinson wrote:
>>>> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
>>>> index 9c4541b..f690eb5 100644
>>>> --- a/net/bluetooth/hci_core.c
>>>> +++ b/net/bluetooth/hci_core.c
>>>> @@ -95,18 +95,17 @@ void hci_req_complete(struct hci_dev *hdev, __u16
>>>> cmd, int result)
>>>>    {
>>>>    	BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result);
>>>>
>>>> +	if (hdev->req_status == HCI_REQ_PEND) {
>>>> +		hdev->req_result = result;
>>>> +		hdev->req_status = HCI_REQ_DONE;
>>>> +		wake_up_interruptible(&hdev->req_wait_q);
>>>> +	}
>>>>    	/* If the request has set req_last_cmd (typical for multi-HCI
>>>>    	 * command requests) check if the completed command matches
>>>>    	 * this, and if not just return. Single HCI command requests
>>>>    	 * typically leave req_last_cmd as 0 */
>>>>    	if (hdev->req_last_cmd&&  cmd != hdev->req_last_cmd)
>>>>    		return;
>>>> -
>>>> -	if (hdev->req_status == HCI_REQ_PEND) {
>>>> -		hdev->req_result = result;
>>>> -		hdev->req_status = HCI_REQ_DONE;
>>>> -		wake_up_interruptible(&hdev->req_wait_q);
>>>> -	}
>>>>    }
>>>>
>>>>    static void hci_req_cancel(struct hci_dev *hdev, int err)
>>>>
>>>
>>> Works here too - bluetooth comes up enabled.  On to Linus?
>>
>> All the patch does is it re-breaks the __hci_request function by
>> removing proper synchronization from it (again) and thereby hides the
>> real bug which is the kernel sending commands to the controller before
>> the HCI_Reset command has completed.
>
> I got an 1.1 Bluetooth dongle that can cause this issue, now I can test this
> by myself. I'll work to fix this next week.
>

cool... I am not going insane!!! let me know if you want me to test any 
patches out.

Justin P. Mattock

^ permalink raw reply

* Re: [2.6.38-rc8, patch] fix hci_dev_list locking
From: Gustavo F. Padovan @ 2011-03-12  1:47 UTC (permalink / raw)
  To: Daniel J Blueman; +Cc: Linux Kernel, linux-bluetooth
In-Reply-To: <AANLkTinN=eYJB3wF9jw7vbbTtUgOzr8oap4+i21Uei5X@mail.gmail.com>

Hi Daniel,

* Daniel J Blueman <daniel.blueman@gmail.com> [2011-03-09 00:07:54 +0800]:

> No response from original post, and it missed getting taken into -rc7
> and -rc8, so repost:
> 
> Release acquired lock on error path, fixing potential hang up.

I already have a patch like this one in my tree, thanks anyway. :)

-- 
Gustavo F. Padovan
http://profusion.mobi

^ permalink raw reply

* Re: re "bluetooth disabled" and "[BUG] usb problems in .38-rc3+"
From: Gustavo F. Padovan @ 2011-03-12  1:33 UTC (permalink / raw)
  To: Ed Tomlinson, Justin P. Mattock, Mikko Vinni, linux-bluetooth
In-Reply-To: <20110310073206.GA1033@jh-x301>

* Johan Hedberg <johan.hedberg@gmail.com> [2011-03-10 09:32:06 +0200]:

> Hi,
> 
> On Wed, Mar 09, 2011, Ed Tomlinson wrote:
> > > diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> > > index 9c4541b..f690eb5 100644
> > > --- a/net/bluetooth/hci_core.c
> > > +++ b/net/bluetooth/hci_core.c
> > > @@ -95,18 +95,17 @@ void hci_req_complete(struct hci_dev *hdev, __u16 
> > > cmd, int result)
> > >   {
> > >   	BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result);
> > > 
> > > +	if (hdev->req_status == HCI_REQ_PEND) {
> > > +		hdev->req_result = result;
> > > +		hdev->req_status = HCI_REQ_DONE;
> > > +		wake_up_interruptible(&hdev->req_wait_q);
> > > +	}
> > >   	/* If the request has set req_last_cmd (typical for multi-HCI
> > >   	 * command requests) check if the completed command matches
> > >   	 * this, and if not just return. Single HCI command requests
> > >   	 * typically leave req_last_cmd as 0 */
> > >   	if (hdev->req_last_cmd && cmd != hdev->req_last_cmd)
> > >   		return;
> > > -
> > > -	if (hdev->req_status == HCI_REQ_PEND) {
> > > -		hdev->req_result = result;
> > > -		hdev->req_status = HCI_REQ_DONE;
> > > -		wake_up_interruptible(&hdev->req_wait_q);
> > > -	}
> > >   }
> > > 
> > >   static void hci_req_cancel(struct hci_dev *hdev, int err)
> > > 
> > 
> > Works here too - bluetooth comes up enabled.  On to Linus?
> 
> All the patch does is it re-breaks the __hci_request function by
> removing proper synchronization from it (again) and thereby hides the
> real bug which is the kernel sending commands to the controller before
> the HCI_Reset command has completed.

I got an 1.1 Bluetooth dongle that can cause this issue, now I can test this
by myself. I'll work to fix this next week.

-- 
Gustavo F. Padovan
http://profusion.mobi

^ permalink raw reply

* [RFC] Auto Connections
From: Claudio Takahasi @ 2011-03-11 21:30 UTC (permalink / raw)
  To: BlueZ development; +Cc: Ville Tervo

Hi guys,

It is time to get opinions from some gurus!

We need to implement automatic connections to implement the Profiles.
At the moment BlueZ supports only dual mode adapters, as consequence
BlueZ needs to start the LE connection. IMHO, it is better to leave
the responsibility to the controller, implementing "selective"
connections will only introduce more code without concrete benefits.
Configuring the controller to autonomously establish connections seems
to be the right approach to proceed.

This topic is NOT about StartDiscovery() + CreateDevice.
StartDiscovery uses active scanning and CreateDevice uses direct
connection establishment. We need a mechanism to automatically connect
to "trusted" devices or devices flagged as "AutoConnect".


My initial idea is: change the LE server socket to report
outgoing(host initiated) connections through the server socket.
Awkward?
To achieve that we need to manage the LE Create Connection(using
whitelist) in the kernel, extend the management interface to control
devices in the whitelist, change the LE Connection Complete Event
handling to get the Role properly.
Pros:
- Controller manage connections
- Flexible to support  connections to "trusted" resolvable address and
passive scanning
- Only one "flow" for the connections: LE server socket
- Maybe we could hide resolvable address from the userspace, mapping
it directly to public or static random
Cons:
- Risky
- Less control of the connection establishment process


Another approach can be to manage the LE connections(using whitelist)
from the userspace. BDADDR_ANY in the destination field(sockaddr_l2)
can be used to define LE connections using white list.
Pros:
- It will not be necessary to extend the management interface now,
address can be added directly to the controller's white list
- Doesn't require changes in the LE server socket
Cons:
- BDADDR_ANY in the destination field has no meaning for Basic Rate
- Needs to expose the resolvable address to the userspace
- Different "flows" for incoming/outgoing connections


Open issue(s):
* How to handle timeouts when sending LE Create Connection
* Passive scanning management


Cheers,
Claudio

^ permalink raw reply

* Re: [PATCH v2 4/4] Add detection of MAP function in OBEX requests
From: Johan Hedberg @ 2011-03-11 21:27 UTC (permalink / raw)
  To: Slawomir Bochenski, linux-bluetooth
In-Reply-To: <20110311144855.GA23826@jh-x301>

Hi,

On Fri, Mar 11, 2011, Johan Hedberg wrote:
> This would be similar to how we handle the telephony driver in BlueZ and
> how the PBAP phonebook back-end is handled in obexd. I'm not saying this
> as justification for the approach but just as a place to look for
> examples. The main justification is to remove one unnecessary
> abstraction layer (the numeric function id) and to remove the need to
> fit each MAP request into the same C function signature (messages_open).
> 
> Trying to fit everything into the same mold forces you to create very
> generic structs like mas_request which then end up needing a
> private_data pointer (for request specific data)

Sorry, I mixed this with some other structs in your code that have this
sort of private data and layering. However, the main point about telling
the back-end which function to call indirectly through an integer
instead of just calling the function directly remains (even if the
function signatures would be the same or very similar).

Johan

^ permalink raw reply

* Re: [PATCH 1/5 v2] Add new UUID utility functions
From: Brian Gix @ 2011-03-11 18:37 UTC (permalink / raw)
  To: Claudio Takahasi; +Cc: Johan Hedberg, linux-bluetooth, Elvis Pfützenreuter
In-Reply-To: <AANLkTims=7dS56VhVQXgSgwkLQsjcu=rYL3cvrsZCfFq@mail.gmail.com>

On 3/11/2011 10:02 AM, Claudio Takahasi wrote:
> Hi Johan/Brian,
>
> On Fri, Mar 11, 2011 at 2:30 PM, Elvis Pfützenreuter<epx@signove.com>  wrote:
>> From: Claudio Takahasi<claudio.takahasi@openbossa.org>
>>
>> New UUID functions will store the UUIDs values on host order. Added
>> functions to create, compare and convert UUIDs.

[...]

>> +
>> +       memcpy(&uuid128->value.u128.data[BASE_UUID16_OFFSET],
>> +&uuid16->value.u16, sizeof(uuid16->value.u16));
>
> Are you fine with memcpy or it is better to use assignments(as
> proposed by Brian)?

Well, the memcpy does have the added advantage of being more immune to 
memory alignment issues.  These structures are all multiple of 4 bytes, 
so it probably doesn't matter, but the memcpy will place the data in the 
correct place for all supported architectures, without any danger of 
misalignment exceptions.  I'd now keep it as a memcpy.


Regards,

-- 
Brian Gix
bgix@codeaurora.org
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox