mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-28 22:54:14 +08:00
762 lines
17 KiB
C
762 lines
17 KiB
C
/*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* SAP Driver for ST-Ericsson U8500 platform
|
|
*
|
|
* Copyright (C) 2010-2011 ST-Ericsson SA
|
|
*
|
|
* 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; version 2 of the License.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <glib.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#include "log.h"
|
|
#include "sap.h"
|
|
|
|
#define STE_SIMD_SOCK "/dev/socket/catd_a"
|
|
#define STE_CLIENT_TAG 0x0000
|
|
|
|
#ifdef STE_SAP_DEBUG
|
|
#define DBG_VERBOSE(fmt, arg...) DBG(fmt, arg)
|
|
#else
|
|
#define DBG_VERBOSE(fmt...)
|
|
#endif
|
|
|
|
#define sap_error(fmt, arg...) do { \
|
|
error("STE U8500 SAP: " fmt, ## arg); \
|
|
} while (0)
|
|
|
|
#define sap_info(fmt, arg...) do { \
|
|
info("STE U8500 SAP: " fmt, ## arg); \
|
|
} while (0)
|
|
|
|
struct ste_message {
|
|
uint16_t len;
|
|
uint16_t id;
|
|
uint32_t client_tag;
|
|
uint8_t payload[0];
|
|
} __attribute__((packed));
|
|
|
|
#define STE_MSG_PAYLOAD_SIZE(msg) (msg->len - sizeof(*msg) + sizeof(msg->len))
|
|
|
|
enum ste_protocol {
|
|
STE_START_SAP_REQ = 0x2D01,
|
|
STE_START_SAP_RSP = 0x2E01,
|
|
STE_END_SAP_REQ = 0x2D02,
|
|
STE_END_SAP_RSP = 0x2E02,
|
|
STE_POWER_OFF_REQ = 0x2D03,
|
|
STE_POWER_OFF_RSP = 0x2E03,
|
|
STE_POWER_ON_REQ = 0x2D04,
|
|
STE_POWER_ON_RSP = 0x2E04,
|
|
STE_RESET_REQ = 0x2D05,
|
|
STE_RESET_RSP = 0x2E05,
|
|
STE_SEND_APDU_REQ = 0x2D06,
|
|
STE_SEND_APDU_RSP = 0x2E06,
|
|
STE_GET_ATR_REQ = 0x2D07,
|
|
STE_GET_ATR_RSP = 0x2E07,
|
|
STE_GET_STATUS_REQ = 0x2D08,
|
|
STE_GET_STATUS_RSP = 0x2E08,
|
|
STE_STATUS_IND = 0x2F02,
|
|
STE_SIM_READY_IND = 0x2F03,
|
|
};
|
|
|
|
enum ste_msg {
|
|
STE_SEND_APDU_MSG,
|
|
STE_GET_ATR_MSG,
|
|
STE_POWER_OFF_MSG,
|
|
STE_POWER_ON_MSG,
|
|
STE_RESET_MSG,
|
|
STE_GET_STATUS_MSG,
|
|
STE_MSG_MAX,
|
|
};
|
|
|
|
enum ste_status {
|
|
STE_STATUS_OK = 0x00000000,
|
|
STE_STATUS_FAILURE = 0x00000001,
|
|
STE_STATUS_BUSY_CALL = 0x00000002,
|
|
};
|
|
|
|
enum ste_card_status {
|
|
STE_CARD_STATUS_UNKNOWN = 0x00,
|
|
STE_CARD_STATUS_ACTIVE = 0x01,
|
|
STE_CARD_STATUS_NOT_ACTIVE = 0x02,
|
|
STE_CARD_STATUS_MISSING = 0x03,
|
|
STE_CARD_STATUS_INVALID = 0x04,
|
|
STE_CARD_STATUS_DISCONNECTED = 0x05,
|
|
};
|
|
|
|
/* Card reader status bits as described in GSM 11.14, Section 12.33
|
|
* Bits 0-2 are for card reader identity and always zeros. */
|
|
#define ICC_READER_REMOVABLE (1 << 3)
|
|
#define ICC_READER_PRESENT (1 << 4)
|
|
#define ICC_READER_ID1 (1 << 5)
|
|
#define ICC_READER_CARD_PRESENT (1 << 6)
|
|
#define ICC_READER_CARD_POWERED (1 << 7)
|
|
|
|
enum ste_state {
|
|
STE_DISABLED, /* Reader not present or removed */
|
|
STE_POWERED_OFF, /* Card in the reader but powered off */
|
|
STE_NO_CARD, /* No card in the reader */
|
|
STE_ENABLED, /* Card in the reader and powered on */
|
|
STE_SIM_BUSY, /* Modem is busy with ongoing call.*/
|
|
STE_STATE_MAX
|
|
};
|
|
|
|
struct ste_u8500 {
|
|
GIOChannel *io;
|
|
enum ste_state state;
|
|
void *sap_data;
|
|
};
|
|
|
|
typedef int(*recv_state_change_cb)(void *sap, uint8_t result);
|
|
typedef int(*recv_pdu_cb)(void *sap, uint8_t result, uint8_t *data,
|
|
uint16_t len);
|
|
|
|
static struct ste_u8500 u8500;
|
|
|
|
static const uint8_t sim2sap_result[STE_MSG_MAX][STE_STATE_MAX] = {
|
|
/* SAP results for SEND APDU message */
|
|
{
|
|
SAP_RESULT_ERROR_NOT_ACCESSIBLE, /* STE_DISABLED */
|
|
SAP_RESULT_ERROR_POWERED_OFF, /* STE_POWERED_OFF */
|
|
SAP_RESULT_ERROR_CARD_REMOVED, /* STE_NO_CARD */
|
|
SAP_RESULT_ERROR_NO_REASON /* STE_ENABLED */
|
|
},
|
|
|
|
/* SAP results for GET_ATR message */
|
|
{
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_POWERED_OFF,
|
|
SAP_RESULT_ERROR_CARD_REMOVED,
|
|
SAP_RESULT_ERROR_NO_REASON
|
|
},
|
|
|
|
/* SAP results POWER OFF message */
|
|
{
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_POWERED_OFF,
|
|
SAP_RESULT_ERROR_CARD_REMOVED,
|
|
SAP_RESULT_ERROR_NO_REASON
|
|
},
|
|
|
|
/* SAP results POWER ON message */
|
|
{
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_NOT_ACCESSIBLE,
|
|
SAP_RESULT_ERROR_CARD_REMOVED,
|
|
SAP_RESULT_ERROR_POWERED_ON
|
|
},
|
|
|
|
/* SAP results SIM RESET message */
|
|
{
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_POWERED_OFF,
|
|
SAP_RESULT_ERROR_CARD_REMOVED,
|
|
SAP_RESULT_ERROR_NOT_ACCESSIBLE
|
|
},
|
|
|
|
/* SAP results GET STATUS message */
|
|
{
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_NO_REASON,
|
|
SAP_RESULT_ERROR_NO_REASON
|
|
}
|
|
};
|
|
|
|
static uint8_t get_sap_result(enum ste_msg msg, uint32_t status)
|
|
{
|
|
if (!u8500.io)
|
|
return SAP_RESULT_ERROR_NO_REASON;
|
|
|
|
switch (status) {
|
|
case STE_STATUS_OK:
|
|
return SAP_RESULT_OK;
|
|
|
|
case STE_STATUS_FAILURE:
|
|
return sim2sap_result[msg][u8500.state];
|
|
|
|
default:
|
|
DBG("Can't convert a result (status %u)", status);
|
|
return SAP_RESULT_ERROR_NO_REASON;
|
|
}
|
|
}
|
|
|
|
static int get_sap_reader_status(uint32_t card_status, uint8_t *icc_status)
|
|
{
|
|
/* Card reader is present, not removable and not ID-1 size. */
|
|
*icc_status = ICC_READER_PRESENT;
|
|
|
|
switch (card_status) {
|
|
case STE_CARD_STATUS_ACTIVE:
|
|
*icc_status |= ICC_READER_CARD_POWERED;
|
|
|
|
case STE_CARD_STATUS_NOT_ACTIVE:
|
|
case STE_CARD_STATUS_INVALID:
|
|
*icc_status |= ICC_READER_CARD_PRESENT;
|
|
|
|
case STE_CARD_STATUS_MISSING:
|
|
case STE_CARD_STATUS_DISCONNECTED:
|
|
return 0;
|
|
|
|
default:
|
|
DBG("Can't convert reader status %u", card_status);
|
|
|
|
case STE_CARD_STATUS_UNKNOWN:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static uint8_t get_sap_status_change(uint32_t card_status)
|
|
{
|
|
if (!u8500.io)
|
|
return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
|
|
|
|
switch (card_status) {
|
|
case STE_CARD_STATUS_UNKNOWN:
|
|
return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
|
|
|
|
case STE_CARD_STATUS_ACTIVE:
|
|
u8500.state = STE_ENABLED;
|
|
return SAP_STATUS_CHANGE_CARD_RESET;
|
|
|
|
case STE_CARD_STATUS_NOT_ACTIVE:
|
|
u8500.state = STE_DISABLED;
|
|
return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
|
|
|
|
case STE_CARD_STATUS_MISSING:
|
|
u8500.state = STE_DISABLED;
|
|
return SAP_STATUS_CHANGE_CARD_REMOVED;
|
|
|
|
case STE_CARD_STATUS_INVALID:
|
|
u8500.state = STE_DISABLED;
|
|
return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
|
|
|
|
default:
|
|
DBG("Can't convert status change %u", card_status);
|
|
return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
static int send_message(GIOChannel *io, void *buf, size_t size)
|
|
{
|
|
gsize written;
|
|
|
|
DBG_VERBOSE("io %p, size %zu", io, size);
|
|
|
|
if (g_io_channel_write_chars(io, buf, size, &written, NULL) !=
|
|
G_IO_STATUS_NORMAL)
|
|
return -EIO;
|
|
|
|
return written;
|
|
}
|
|
|
|
static int send_request(GIOChannel *io, uint16_t id,
|
|
struct sap_parameter *param)
|
|
{
|
|
int ret;
|
|
struct ste_message *msg;
|
|
size_t size = sizeof(*msg);
|
|
|
|
DBG_VERBOSE("io %p", io);
|
|
|
|
if (param)
|
|
size += param->len;
|
|
|
|
msg = g_try_malloc0(size);
|
|
if (!msg) {
|
|
sap_error("sending request failed: %s", strerror(ENOMEM));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->len = size - sizeof(msg->len);
|
|
msg->id = id;
|
|
msg->client_tag = STE_CLIENT_TAG;
|
|
|
|
if (param)
|
|
memcpy(msg->payload, param->val, param->len);
|
|
|
|
ret = send_message(io, msg, size);
|
|
if (ret < 0) {
|
|
sap_error("sending request failed: %s", strerror(-ret));
|
|
} else if (ret != (int) size) {
|
|
sap_error("sending request failed: %d out of %zu bytes sent",
|
|
ret, size);
|
|
ret = -EIO;
|
|
}
|
|
|
|
g_free(msg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void recv_status(uint32_t status)
|
|
{
|
|
sap_status_ind(u8500.sap_data, get_sap_status_change(status));
|
|
}
|
|
|
|
static void recv_card_status(uint32_t status, uint8_t *param)
|
|
{
|
|
uint32_t *card_status;
|
|
uint8_t result;
|
|
uint8_t iccrs;
|
|
|
|
if (status != STE_STATUS_OK)
|
|
return;
|
|
|
|
card_status = (uint32_t *)param;
|
|
|
|
if (get_sap_reader_status(*card_status, &iccrs) < 0)
|
|
result = SAP_RESULT_ERROR_NO_REASON;
|
|
else
|
|
result = get_sap_result(STE_GET_STATUS_MSG, status);
|
|
|
|
sap_transfer_card_reader_status_rsp(u8500.sap_data, result, iccrs);
|
|
}
|
|
|
|
static void recv_state_change(uint32_t ste_msg, uint32_t status,
|
|
uint32_t new_state, recv_state_change_cb callback)
|
|
{
|
|
if (status != STE_STATUS_OK)
|
|
return;
|
|
|
|
u8500.state = new_state;
|
|
|
|
if (callback)
|
|
callback(u8500.sap_data, get_sap_result(ste_msg, status));
|
|
}
|
|
|
|
static void recv_pdu(uint32_t ste_msg, struct ste_message *msg, uint32_t status,
|
|
uint8_t *param, recv_pdu_cb callback)
|
|
{
|
|
uint8_t *data = NULL;
|
|
uint8_t result;
|
|
int size = 0;
|
|
|
|
if (status == STE_STATUS_OK) {
|
|
data = param;
|
|
size = STE_MSG_PAYLOAD_SIZE(msg) - sizeof(status);
|
|
}
|
|
|
|
result = get_sap_result(ste_msg, status);
|
|
|
|
if (callback)
|
|
callback(u8500.sap_data, result, data, size);
|
|
}
|
|
|
|
static void simd_close(void)
|
|
{
|
|
DBG("io %p", u8500.io);
|
|
|
|
if (u8500.io) {
|
|
g_io_channel_shutdown(u8500.io, TRUE, NULL);
|
|
g_io_channel_unref(u8500.io);
|
|
}
|
|
|
|
u8500.state = STE_DISABLED;
|
|
u8500.io = NULL;
|
|
u8500.sap_data = NULL;
|
|
}
|
|
|
|
static void recv_sim_ready(void)
|
|
{
|
|
sap_info("sim is ready. Try to connect again");
|
|
|
|
if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
|
|
sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED,
|
|
SAP_BUF_SIZE);
|
|
simd_close();
|
|
}
|
|
}
|
|
|
|
static void recv_connect_rsp(uint32_t status)
|
|
{
|
|
switch (status) {
|
|
case STE_STATUS_OK:
|
|
if (u8500.state != STE_SIM_BUSY)
|
|
sap_connect_rsp(u8500.sap_data,
|
|
SAP_STATUS_OK, 0);
|
|
break;
|
|
case STE_STATUS_BUSY_CALL:
|
|
if (u8500.state != STE_SIM_BUSY) {
|
|
sap_connect_rsp(u8500.sap_data,
|
|
SAP_STATUS_OK_ONGOING_CALL,
|
|
SAP_BUF_SIZE);
|
|
|
|
u8500.state = STE_SIM_BUSY;
|
|
}
|
|
break;
|
|
default:
|
|
sap_connect_rsp(u8500.sap_data,
|
|
SAP_STATUS_CONNECTION_FAILED, 0);
|
|
simd_close();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void recv_response(struct ste_message *msg)
|
|
{
|
|
uint32_t status;
|
|
uint8_t *param;
|
|
|
|
DBG_VERBOSE("msg_id 0x%x", msg->id);
|
|
|
|
if (msg->id == STE_END_SAP_RSP) {
|
|
sap_disconnect_rsp(u8500.sap_data);
|
|
simd_close();
|
|
return;
|
|
}
|
|
|
|
param = msg->payload;
|
|
status = *(uint32_t *)param;
|
|
param += sizeof(status);
|
|
|
|
DBG_VERBOSE("status 0x%x", status);
|
|
|
|
switch (msg->id) {
|
|
case STE_START_SAP_RSP:
|
|
recv_connect_rsp(status);
|
|
break;
|
|
case STE_SEND_APDU_RSP:
|
|
recv_pdu(STE_SEND_APDU_MSG, msg, status, param,
|
|
sap_transfer_apdu_rsp);
|
|
break;
|
|
|
|
case STE_GET_ATR_RSP:
|
|
recv_pdu(STE_GET_ATR_MSG, msg, status, param,
|
|
sap_transfer_atr_rsp);
|
|
break;
|
|
|
|
case STE_POWER_OFF_RSP:
|
|
recv_state_change(STE_POWER_OFF_MSG, status, STE_POWERED_OFF,
|
|
sap_power_sim_off_rsp);
|
|
break;
|
|
|
|
case STE_POWER_ON_RSP:
|
|
recv_state_change(STE_POWER_ON_MSG, status, STE_ENABLED,
|
|
sap_power_sim_on_rsp);
|
|
break;
|
|
|
|
case STE_RESET_RSP:
|
|
recv_state_change(STE_RESET_MSG, status, STE_ENABLED,
|
|
sap_reset_sim_rsp);
|
|
break;
|
|
|
|
case STE_GET_STATUS_RSP:
|
|
recv_card_status(status, param);
|
|
break;
|
|
|
|
case STE_STATUS_IND:
|
|
recv_status(status);
|
|
break;
|
|
|
|
case STE_SIM_READY_IND:
|
|
recv_sim_ready();
|
|
break;
|
|
|
|
default:
|
|
sap_error("unsupported message received (id 0x%x)", msg->id);
|
|
}
|
|
}
|
|
|
|
static int recv_message(void *buf, size_t size)
|
|
{
|
|
uint8_t *iter = buf;
|
|
struct ste_message *msg = buf;
|
|
|
|
do {
|
|
DBG_VERBOSE("size %zu msg->len %u.", size, msg->len);
|
|
|
|
if (size < sizeof(*msg)) {
|
|
sap_error("invalid message received (%zu bytes)", size);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
/* Message must be complete. */
|
|
if (size < (msg->len + sizeof(msg->len))) {
|
|
sap_error("incomplete message received (%zu bytes)",
|
|
size);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
recv_response(msg);
|
|
|
|
/* Reduce total buffer size by just handled frame size. */
|
|
size -= msg->len + sizeof(msg->len);
|
|
|
|
/* Move msg pointer to the next message if any. */
|
|
iter += msg->len + sizeof(msg->len);
|
|
msg = (struct ste_message *)iter;
|
|
} while (size > 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean simd_data_cb(GIOChannel *io, GIOCondition cond, gpointer data)
|
|
{
|
|
char buf[SAP_BUF_SIZE];
|
|
gsize bytes_read;
|
|
GIOStatus gstatus;
|
|
|
|
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
|
|
DBG("Error condition on sim socket (0x%x)", cond);
|
|
return FALSE;
|
|
}
|
|
|
|
gstatus = g_io_channel_read_chars(io, buf, sizeof(buf), &bytes_read,
|
|
NULL);
|
|
if (gstatus != G_IO_STATUS_NORMAL) {
|
|
sap_error("error while reading from channel (%d)", gstatus);
|
|
return TRUE;
|
|
}
|
|
|
|
if (recv_message(buf, bytes_read) < 0)
|
|
sap_error("error while parsing STE Sim message");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void simd_watch(int sock, void *sap_data)
|
|
{
|
|
GIOChannel *io;
|
|
|
|
DBG("sock %d, sap_data %p ", sock, sap_data);
|
|
|
|
io = g_io_channel_unix_new(sock);
|
|
|
|
g_io_channel_set_close_on_unref(io, TRUE);
|
|
g_io_channel_set_encoding(io, NULL, NULL);
|
|
g_io_channel_set_buffered(io, FALSE);
|
|
|
|
u8500.io = io;
|
|
u8500.sap_data = sap_data;
|
|
u8500.state = STE_DISABLED;
|
|
|
|
g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
|
|
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
|
|
simd_data_cb, NULL, NULL);
|
|
}
|
|
|
|
static int simd_connect(void *sap_data)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int sock;
|
|
int err;
|
|
|
|
/* Already connected to simd */
|
|
if (u8500.io)
|
|
return -EALREADY;
|
|
|
|
sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (sock < 0) {
|
|
err = -errno;
|
|
sap_error("creating socket failed: %s", strerror(-err));
|
|
return err;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
memcpy(addr.sun_path, STE_SIMD_SOCK, sizeof(STE_SIMD_SOCK) - 1);
|
|
|
|
if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
err = -errno;
|
|
sap_error("connect to the socket failed: %s", strerror(-err));
|
|
goto failed;
|
|
}
|
|
|
|
if (fcntl(sock, F_SETFL, O_NONBLOCK) > 0) {
|
|
err = -errno;
|
|
sap_error("setting up socket failed: %s", strerror(-err));
|
|
goto failed;
|
|
}
|
|
|
|
simd_watch(sock, sap_data);
|
|
|
|
return 0;
|
|
|
|
failed:
|
|
close(sock);
|
|
return err;
|
|
}
|
|
|
|
void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
|
|
{
|
|
DBG("sap_device %p maxmsgsize %u", sap_device, maxmsgsize);
|
|
|
|
sap_info("connect request");
|
|
|
|
if (simd_connect(sap_device) < 0) {
|
|
sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED, 0);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
|
|
sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
|
|
SAP_BUF_SIZE);
|
|
simd_close();
|
|
}
|
|
}
|
|
|
|
void sap_disconnect_req(void *sap_device, uint8_t linkloss)
|
|
{
|
|
DBG("sap_device %p linkloss %u", sap_device, linkloss);
|
|
|
|
sap_info("disconnect request %s", linkloss ? "by link loss" : "");
|
|
|
|
if (u8500.state == STE_DISABLED) {
|
|
sap_disconnect_rsp(sap_device);
|
|
simd_close();
|
|
return;
|
|
}
|
|
|
|
if (linkloss) {
|
|
simd_close();
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_END_SAP_REQ, NULL) < 0) {
|
|
sap_disconnect_rsp(sap_device);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG_VERBOSE("sap_device %p param %p", sap_device, param);
|
|
|
|
if (u8500.state != STE_ENABLED) {
|
|
result = get_sap_result(STE_SEND_APDU_MSG, STE_STATUS_FAILURE);
|
|
sap_transfer_apdu_rsp(sap_device, result, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_SEND_APDU_REQ, param) < 0)
|
|
sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA,
|
|
NULL, 0);
|
|
}
|
|
|
|
void sap_transfer_atr_req(void *sap_device)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
if (u8500.state != STE_ENABLED) {
|
|
result = get_sap_result(STE_GET_ATR_MSG, STE_STATUS_FAILURE);
|
|
sap_transfer_atr_rsp(sap_device, result, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_GET_ATR_REQ, NULL) < 0)
|
|
sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA, NULL,
|
|
0);
|
|
}
|
|
|
|
void sap_power_sim_off_req(void *sap_device)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
if (u8500.state != STE_ENABLED) {
|
|
result = get_sap_result(STE_POWER_OFF_MSG, STE_STATUS_FAILURE);
|
|
sap_power_sim_off_rsp(sap_device, result);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_POWER_OFF_REQ, NULL) < 0)
|
|
sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
|
|
}
|
|
|
|
void sap_power_sim_on_req(void *sap_device)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
if (u8500.state != STE_POWERED_OFF) {
|
|
result = get_sap_result(STE_POWER_ON_MSG, STE_STATUS_FAILURE);
|
|
sap_power_sim_on_rsp(sap_device, result);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_POWER_ON_REQ, NULL) < 0)
|
|
sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
|
|
}
|
|
|
|
void sap_reset_sim_req(void *sap_device)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
if (u8500.state != STE_ENABLED) {
|
|
result = get_sap_result(STE_RESET_MSG, STE_STATUS_FAILURE);
|
|
sap_reset_sim_rsp(sap_device, result);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_RESET_REQ, NULL) < 0)
|
|
sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
|
|
}
|
|
|
|
void sap_transfer_card_reader_status_req(void *sap_device)
|
|
{
|
|
uint8_t result;
|
|
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
if (u8500.state == STE_DISABLED) {
|
|
result = get_sap_result(STE_GET_STATUS_MSG, STE_STATUS_FAILURE);
|
|
sap_transfer_card_reader_status_rsp(sap_device, result, 0);
|
|
return;
|
|
}
|
|
|
|
if (send_request(u8500.io, STE_GET_STATUS_REQ, NULL) < 0)
|
|
sap_transfer_card_reader_status_rsp(sap_device,
|
|
SAP_RESULT_ERROR_NO_DATA, 0);
|
|
}
|
|
|
|
void sap_set_transport_protocol_req(void *sap_device,
|
|
struct sap_parameter *param)
|
|
{
|
|
DBG("sap_device %p", sap_device);
|
|
|
|
sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
|
|
}
|
|
|
|
int sap_init(void)
|
|
{
|
|
u8500.state = STE_DISABLED;
|
|
info("STE U8500 SAP driver initialized");
|
|
return 0;
|
|
}
|
|
|
|
void sap_exit(void)
|
|
{
|
|
}
|