/* * 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 <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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 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_immediate(DBusConnection *conn, DBusMessage *msg, void *data) { if (sim_card_conn_status == SIM_DISCONNECTED) return g_dbus_create_error(msg, "org.bluez.Error.Failed", "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 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); 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 g_dbus_create_error(msg, "org.bluez.Error.Failed", "Unknown card status. Use 0, 1 or 2."); } 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}, { "DisconnectImmediate", "", "", disconnect_immediate}, { "CardStatus", "u", "", card_status}, { } }; int sap_init(void) { 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) { dbus_connection_unref(connection); connection = NULL; }