summaryrefslogtreecommitdiff
path: root/sap/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'sap/server.c')
-rw-r--r--sap/server.c1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/sap/server.c b/sap/server.c
new file mode 100644
index 0000000..c9c9a12
--- /dev/null
+++ b/sap/server.c
@@ -0,0 +1,1438 @@
+/*
+ * 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: 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
+ */
+
+#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
+
+#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 {
+ 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 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)
+ 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;
+ 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;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&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 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)
+{
+ 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("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,
+ struct sap_parameter *param)
+{
+ 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("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("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("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("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("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)
+{
+ 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)
+{
+ 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)
+{
+ 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;
+}
+
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
+ uint16_t length)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ struct sap_message *msg = buf;
+ struct sap_connection *conn = data;
+
+ if (!conn)
+ return -EINVAL;
+
+ if (size < sizeof(struct sap_message))
+ goto error_rsp;
+
+ if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
+ sizeof(struct sap_parameter) + 4))
+ goto error_rsp;
+
+ if (check_msg(msg) < 0)
+ goto error_rsp;
+
+ 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.");
+ break;
+ }
+
+error_rsp:
+ DBG("Bad request message format.");
+ sap_error_rsp(conn);
+ return -EBADMSG;
+}
+
+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) {
+ 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);
+ }
+}
+
+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;
+
+ /* 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);
+}
+
+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");
+
+ 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);
+}
+
+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 ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
+ 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);
+ server = NULL;
+}
+
+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)
+{
+ 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);
+ sap_exit();
+
+ return -1;
+}
+
+int sap_server_unregister(const char *path)
+{
+ 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);
+
+ sap_exit();
+
+ return 0;
+}
+
+int sap_server_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+ return 0;
+}
+
+void sap_server_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}