/* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2010 ST-Ericsson SA * Copyright (C) 2011 Tieto Poland * * Author: Waldemar Rymarkiewicz * 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 #endif #include #include #include #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); }