Add support for SAP protocol features:
* connect and disconnect requests
* connect and disconnect responses
* disconnect indication
* timeouts for the valid connection
---
sap/sap.h | 2 +-
sap/server.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 424 insertions(+), 11 deletions(-)
diff --git a/sap/sap.h b/sap/sap.h
index bd0f06d..24240ca 100644
--- a/sap/sap.h
+++ b/sap/sap.h
@@ -143,7 +143,7 @@ enum sap_param_id {
#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
+#define SAP_PARAM_ID_TRANSPORT_PROTO_LEN 0x01
/* Transport Protocol - SAP v1.1 section 5.2.9 */
enum sap_transport_protocol {
diff --git a/sap/server.c b/sap/server.c
index 1c62a3e..8337fe1 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,72 @@ 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)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_TRANSFER_APDU_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
+ if ( msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+ return -EBADMSG;
+
+ if (msg->param->len == 0x00)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
+ return -EBADMSG;
+
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
+ return -EBADMSG;
+
+ 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 -EBADMSG;
+
+ break;
+ }
+
+ return 0;
+}
+
static sdp_record_t *create_sap_record(uint8_t channel)
{
sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
@@ -126,16 +206,170 @@ 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 *) ¶m->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)
+ goto error_req;
+
+ if (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);
+ /* Timer will disconnect if client won't do.*/
+ start_guard_timer(conn, SAP_TIMER_GRACEFUL_DISCONNECT);
+ }
+
+ return 0;
+
+ case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+ if (conn->state == SAP_STATE_DISCONNECTED)
+ goto error_req;
+
+ if (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;
+
+ 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 +409,163 @@ 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 *) ¶m->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 +626,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 +665,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)
@@ -347,6 +737,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);
}
@@ -361,6 +765,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);
@@ -468,6 +876,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);
}
@@ -494,7 +906,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
Hi Johan,
>We don't use the .py extension for any other python scripts in
>the tree so please don't use it for your scripts either.
>You'll probably want to name the scripts a little bit more
>descriptively than "sap" and "test-sap". Also remember to
>mention them to Makefile.tools so they are part of "make dist"
>(i.e. add them to EXTRA_DIST).
>
In fact, I missed EXTRA_DIST. Will fix this then.
I will remove .py extensions as well. I understand that the dbusdef.py is an exception?
I could propose test/sap-client and test/test-sap or test/test-sap-server . It seems more saner I think. Doesn't it?
Waldek
Johan,
>The first three patches have been pushed upstream. Thanks.
>However, I hope you do pay attention the refactoring
>suggestions that were made and keep a look-out for
>possibilities for simplifying the code.
>
Thanks.
Yes, I will see what I can refactor there.
Waldek
Hi Waldek,
On Wed, Mar 16, 2011, Waldemar Rymarkiewicz wrote:
> 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 | 944 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> test/test-sap.py | 139 ++++++++
> 2 files changed, 1083 insertions(+), 0 deletions(-)
> create mode 100644 test/sap.py
> create mode 100755 test/test-sap.py
We don't use the .py extension for any other python scripts in the tree
so please don't use it for your scripts either. You'll probably want to
name the scripts a little bit more descriptively than "sap" and
"test-sap". Also remember to mention them to Makefile.tools so they are
part of "make dist" (i.e. add them to EXTRA_DIST).
Johan
Hi Waldek,
On Wed, Mar 16, 2011, Waldemar Rymarkiewicz wrote:
> Add SAP dummy driver implementation and extend configure
> with --with-sap=<driver>.
> ---
> .gitignore | 1 +
> Makefile.am | 13 +++-
> acinclude.m4 | 6 ++
> sap/sap-dummy.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
> 4 files changed, 275 insertions(+), 14 deletions(-)
The first three patches have been pushed upstream. Thanks. However, I
hope you do pay attention the refactoring suggestions that were made and
keep a look-out for possibilities for simplifying the code.
Johan
On Thu, Mar 17, 2011 at 10:50 AM, <[email protected]> wrote:
> Hi Angus.H,
>
>>A lot of code could be refactored.
>>Quite a few repeatable (logically) blocks in every
>>"...._req()" and "..._rsp()" handlers.
>>
>
> Indeed, but those blocks are not equal in all cases. Thus, I prefere to split it up into seperate functions instead of having one big function with switch-case statement.
> It's not like we can replace those blocks with one generic function without switch-case. I consider this as slightly more readable. However, I'm not strongly opposed to change it one function.
>
> Waldek
>
Hi
I just wanted to say that refactoring of typical req() here could be
seen as 3 or 4 internal prcedure calls,
[ refactoring here == extracting common/repeatable code to new routine ]
typical req example:
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);
}
could be represented as:
{
validate_req(conn, VALIDATION_RULE)
assign_req(conn,REQ)
send_req(conn, handlers[REQ]) ( where handlers is an array of req/rsp
handlers to sap driver )
error_handling:
}
it's just an example, more readable/maintainable.
/AH
Hi Angus.H,
>A lot of code could be refactored.
>Quite a few repeatable (logically) blocks in every
>"...._req()" and "..._rsp()" handlers.
>
Indeed, but those blocks are not equal in all cases. Thus, I prefere to split it up into seperate functions instead of having one big function with switch-case statement.
It's not like we can replace those blocks with one generic function without switch-case. I consider this as slightly more readable. However, I'm not strongly opposed to change it one function.
Waldek
2011/3/16 Waldemar Rymarkiewicz <[email protected]>:
> 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 | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
> 1 files changed, 399 insertions(+), 16 deletions(-)
Hi
A lot of code could be refactored.
Quite a few repeatable (logically) blocks in every
"...._req()" and "..._rsp()" handlers.
/A.H
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 | 944 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/test-sap.py | 139 ++++++++
2 files changed, 1083 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..5a0c711
--- /dev/null
+++ b/test/sap.py
@@ -0,0 +1,944 @@
+""" Copyright (C) 2011 Tieto """
+
+""" Szymon Janc <[email protected]> """
+""" Waldemar Rymarkiewicz <[email protected]> """
+
+""" 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..393f785
--- /dev/null
+++ b/test/test-sap.py
@@ -0,0 +1,139 @@
+""" Copyright (C) 2011 Tieto """
+""" Waldemar Rymarkiewicz <[email protected]> """
+
+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
Add SAP dummy driver implementation and extend configure
with --with-sap=<driver>.
---
.gitignore | 1 +
Makefile.am | 13 +++-
acinclude.m4 | 6 ++
sap/sap-dummy.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 275 insertions(+), 14 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 7267200..2994bf9 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 b433ba3..39e1fc9 100644
--- a/sap/sap-dummy.c
+++ b/sap/sap-dummy.c
@@ -2,6 +2,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 ST-Ericsson SA
+ * Copyright (C) 2011 Tieto Poland
*
* Author: Waldemar Rymarkiewicz <[email protected]>
* for ST-Ericsson
@@ -21,50 +22,185 @@
* 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,
@@ -73,13 +209,122 @@ 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
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 | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 399 insertions(+), 16 deletions(-)
diff --git a/sap/server.c b/sap/server.c
index 8337fe1..35abffb 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)
@@ -375,38 +413,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)
@@ -572,49 +731,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)
--
1.7.1