|  | /* | 
|  | *  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 <stdint.h> | 
|  |  | 
|  | #include "gdbus/gdbus.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); | 
|  | } |