blob: 854105e29250ee8d498dda84ae61ab6a537099d0 [file] [log] [blame]
/*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 ST-Ericsson SA
* Copyright (C) 2011 Tieto Poland
*
* 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 <glib.h>
#include <gdbus/gdbus.h>
#include <stdint.h>
#include "src/dbus-common.h"
#include "src/error.h"
#include "src/log.h"
#include "sap.h"
#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1"
#define SAP_DUMMY_PATH "/org/bluez/test"
enum {
SIM_DISCONNECTED = 0x00,
SIM_CONNECTED = 0x01,
SIM_POWERED_OFF = 0x02,
SIM_MISSING = 0x03
};
static unsigned int init_cnt = 0;
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)
{
DBG("status: %d", sim_card_conn_status);
if (sim_card_conn_status != SIM_DISCONNECTED) {
sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
return;
}
if (max_msg_size_supported > maxmsgsize) {
sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL);
return;
}
if (max_msg_size_supported < maxmsgsize) {
sap_connect_rsp(sap_device,
SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED);
return;
}
if (ongoing_call_status) {
sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL);
return;
}
sim_card_conn_status = SIM_CONNECTED;
sap_data = sap_device;
sap_connect_rsp(sap_device, SAP_STATUS_OK);
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)
{
char apdu[] = "APDU response!";
DBG("status: %d", sim_card_conn_status);
switch (sim_card_conn_status) {
case SIM_MISSING:
sap_transfer_apdu_rsp(sap_device,
SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
break;
case SIM_POWERED_OFF:
sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
NULL, 0);
break;
case SIM_DISCONNECTED:
sap_transfer_apdu_rsp(sap_device,
SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
break;
case SIM_CONNECTED:
sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
(uint8_t *)apdu, sizeof(apdu));
break;
}
}
void sap_transfer_atr_req(void *sap_device)
{
char atr[] = "ATR response!";
DBG("status: %d", sim_card_conn_status);
switch (sim_card_conn_status) {
case SIM_MISSING:
sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
NULL, 0);
break;
case SIM_POWERED_OFF:
sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
NULL, 0);
break;
case SIM_DISCONNECTED:
sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
NULL, 0);
break;
case SIM_CONNECTED:
sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
(uint8_t *)atr, sizeof(atr));
break;
}
}
void sap_power_sim_off_req(void *sap_device)
{
DBG("status: %d", sim_card_conn_status);
switch (sim_card_conn_status) {
case SIM_MISSING:
sap_power_sim_off_rsp(sap_device,
SAP_RESULT_ERROR_CARD_REMOVED);
break;
case SIM_POWERED_OFF:
sap_power_sim_off_rsp(sap_device,
SAP_RESULT_ERROR_POWERED_OFF);
break;
case SIM_DISCONNECTED:
sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
break;
case SIM_CONNECTED:
sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
sim_card_conn_status = SIM_POWERED_OFF;
break;
}
}
void sap_power_sim_on_req(void *sap_device)
{
DBG("status: %d", sim_card_conn_status);
switch (sim_card_conn_status) {
case SIM_MISSING:
sap_power_sim_on_rsp(sap_device,
SAP_RESULT_ERROR_CARD_REMOVED);
break;
case SIM_POWERED_OFF:
sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
sim_card_conn_status = SIM_CONNECTED;
break;
case SIM_DISCONNECTED:
sap_power_sim_on_rsp(sap_device,
SAP_RESULT_ERROR_NOT_ACCESSIBLE);
break;
case SIM_CONNECTED:
sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
break;
}
}
void sap_reset_sim_req(void *sap_device)
{
DBG("status: %d", sim_card_conn_status);
switch (sim_card_conn_status) {
case SIM_MISSING:
sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
break;
case SIM_POWERED_OFF:
sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
break;
case SIM_DISCONNECTED:
sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
break;
case SIM_CONNECTED:
sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
break;
}
}
void sap_transfer_card_reader_status_req(void *sap_device)
{
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,
struct sap_parameter *param)
{
sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
}
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 btd_error_invalid_args(msg);
if (ongoing_call_status && !ongoing) {
/* An ongoing call has finished. Continue connection.*/
sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
ongoing_call_status = FALSE;
} else if (!ongoing_call_status && ongoing) {
/* An ongoing call has started.*/
ongoing_call_status = TRUE;
}
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 btd_error_failed(msg,
"Can't change msg size when connected.");
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
DBUS_TYPE_INVALID))
return btd_error_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_immediate(DBusConnection *conn, DBusMessage *msg,
void *data)
{
if (sim_card_conn_status == SIM_DISCONNECTED)
return btd_error_failed(msg, "Already disconnected.");
sim_card_conn_status = SIM_DISCONNECTED;
sap_disconnect_ind(sap_data, SAP_DISCONNECTION_TYPE_IMMEDIATE);
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 btd_error_failed(msg,
"Can't change msg size when not connected.");
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
switch (status) {
case 0: /* card removed */
sim_card_conn_status = SIM_MISSING;
sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
break;
case 1: /* card inserted */
if (sim_card_conn_status == SIM_MISSING) {
sim_card_conn_status = SIM_CONNECTED;
sap_status_ind(sap_data,
SAP_STATUS_CHANGE_CARD_INSERTED);
}
break;
case 2: /* card not longer available*/
sim_card_conn_status = SIM_POWERED_OFF;
sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
break;
default:
return btd_error_failed(msg,
"Unknown card status. Use 0, 1 or 2.");
}
DBG("Card status changed to %d", status);
return dbus_message_new_method_return(msg);
}
static const GDBusMethodTable dummy_methods[] = {
{ GDBUS_EXPERIMENTAL_METHOD("OngoingCall",
GDBUS_ARGS({ "ongoing", "b" }), NULL,
ongoing_call) },
{ GDBUS_EXPERIMENTAL_METHOD("MaxMessageSize",
GDBUS_ARGS({ "size", "u" }), NULL,
max_msg_size) },
{ GDBUS_EXPERIMENTAL_METHOD("DisconnectImmediate", NULL, NULL,
disconnect_immediate) },
{ GDBUS_EXPERIMENTAL_METHOD("CardStatus",
GDBUS_ARGS({ "status", "" }), NULL,
card_status) },
{ }
};
int sap_init(void)
{
if (init_cnt++)
return 0;
if (g_dbus_register_interface(btd_get_dbus_connection(), SAP_DUMMY_PATH,
SAP_DUMMY_IFACE, dummy_methods, NULL, NULL,
NULL, NULL) == FALSE) {
init_cnt--;
return -1;
}
return 0;
}
void sap_exit(void)
{
if (--init_cnt)
return;
g_dbus_unregister_interface(btd_get_dbus_connection(),
SAP_DUMMY_PATH, SAP_DUMMY_IFACE);
}