forked from mirrors/lpac
163 lines
5.4 KiB
C
163 lines
5.4 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright (c) 2024, Luca Weiss <luca.weiss@fairphone.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "qmi_common.h"
|
|
|
|
int qmi_apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len)
|
|
{
|
|
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GArray) apdu_data = NULL;
|
|
|
|
/* Convert tx into request GArray */
|
|
apdu_data = g_array_new(FALSE, FALSE, sizeof(guint8));
|
|
for (uint32_t i = 0; i < tx_len; i++)
|
|
g_array_append_val(apdu_data, tx[i]);
|
|
|
|
QmiMessageUimSendApduInput *input;
|
|
input = qmi_message_uim_send_apdu_input_new();
|
|
qmi_message_uim_send_apdu_input_set_slot(input, qmi_priv->uimSlot, NULL);
|
|
qmi_message_uim_send_apdu_input_set_channel_id(input, qmi_priv->lastChannelId, NULL);
|
|
qmi_message_uim_send_apdu_input_set_apdu(input, apdu_data, NULL);
|
|
|
|
QmiMessageUimSendApduOutput *output;
|
|
output = qmi_client_uim_send_apdu_sync(qmi_priv->uimClient, input, qmi_priv->context, &error);
|
|
|
|
qmi_message_uim_send_apdu_input_unref(input);
|
|
|
|
if (!qmi_message_uim_send_apdu_output_get_result(output, &error))
|
|
{
|
|
fprintf(stderr, "error: send apdu operation failed: %s\n", error->message);
|
|
return -1;
|
|
}
|
|
|
|
GArray *apdu_res = NULL;
|
|
if (!qmi_message_uim_send_apdu_output_get_apdu_response(output, &apdu_res, &error))
|
|
{
|
|
fprintf(stderr, "error: get apdu response operation failed: %s\n", error->message);
|
|
return -1;
|
|
}
|
|
|
|
/* Convert response GArray into rx */
|
|
*rx_len = apdu_res->len;
|
|
*rx = malloc(*rx_len);
|
|
if (!*rx)
|
|
return -1;
|
|
for (guint i = 0; i < apdu_res->len; i++)
|
|
(*rx)[i] = apdu_res->data[i];
|
|
|
|
qmi_message_uim_send_apdu_output_unref(output);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qmi_apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len)
|
|
{
|
|
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
|
|
g_autoptr(GError) error = NULL;
|
|
guint8 channel_id;
|
|
|
|
GArray *aid_data = g_array_new(FALSE, FALSE, sizeof(guint8));
|
|
for (int i = 0; i < aid_len; i++)
|
|
g_array_append_val(aid_data, aid[i]);
|
|
|
|
QmiMessageUimOpenLogicalChannelInput *input;
|
|
input = qmi_message_uim_open_logical_channel_input_new();
|
|
qmi_message_uim_open_logical_channel_input_set_slot(input, qmi_priv->uimSlot, NULL);
|
|
qmi_message_uim_open_logical_channel_input_set_aid(input, aid_data, NULL);
|
|
|
|
QmiMessageUimOpenLogicalChannelOutput *output;
|
|
output = qmi_client_uim_open_logical_channel_sync(qmi_priv->uimClient, input, qmi_priv->context, &error);
|
|
|
|
qmi_message_uim_open_logical_channel_input_unref(input);
|
|
g_array_unref(aid_data);
|
|
|
|
if (!output)
|
|
{
|
|
fprintf(stderr, "error: send Open Logical Channel command failed: %s\n", error->message);
|
|
return -1;
|
|
}
|
|
|
|
if (!qmi_message_uim_open_logical_channel_output_get_result(output, &error))
|
|
{
|
|
fprintf(stderr, "error: open logical channel operation failed: %s\n", error->message);
|
|
return -1;
|
|
}
|
|
|
|
if (!qmi_message_uim_open_logical_channel_output_get_channel_id(output, &channel_id, &error))
|
|
{
|
|
fprintf(stderr, "error: get channel id operation failed: %s\n", error->message);
|
|
return -1;
|
|
}
|
|
qmi_priv->lastChannelId = channel_id;
|
|
|
|
g_debug("Opened logical channel with id %d", channel_id);
|
|
|
|
qmi_message_uim_open_logical_channel_output_unref(output);
|
|
|
|
return channel_id;
|
|
}
|
|
|
|
void qmi_apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel)
|
|
{
|
|
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
QmiMessageUimLogicalChannelInput *input;
|
|
input = qmi_message_uim_logical_channel_input_new();
|
|
qmi_message_uim_logical_channel_input_set_slot(input, qmi_priv->uimSlot, NULL);
|
|
qmi_message_uim_logical_channel_input_set_channel_id(input, channel, NULL);
|
|
|
|
QmiMessageUimLogicalChannelOutput *output;
|
|
output = qmi_client_uim_logical_channel_sync(qmi_priv->uimClient, input, qmi_priv->context, &error);
|
|
|
|
qmi_message_uim_logical_channel_input_unref(input);
|
|
|
|
if (error)
|
|
{
|
|
fprintf(stderr, "error: send Close Logical Channel command failed: %s\n", error->message);
|
|
return;
|
|
}
|
|
|
|
if (!qmi_message_uim_logical_channel_output_get_result(output, &error))
|
|
{
|
|
fprintf(stderr, "error: logical channel operation failed: %s\n", error->message);
|
|
return;
|
|
}
|
|
|
|
/* Mark channel as having been cleaned up */
|
|
if (channel == qmi_priv->lastChannelId)
|
|
qmi_priv->lastChannelId = -1;
|
|
|
|
g_debug("Closed logical channel with id %d", channel);
|
|
|
|
qmi_message_uim_logical_channel_output_unref(output);
|
|
}
|
|
|
|
void qmi_apdu_interface_disconnect(struct euicc_ctx *ctx)
|
|
{
|
|
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
|
|
g_autoptr(GError) error = NULL;
|
|
QmiClient *client = QMI_CLIENT(qmi_priv->uimClient);
|
|
QmiDevice *device = QMI_DEVICE(qmi_client_get_device(client));
|
|
|
|
qmi_device_release_client_sync(device, client, qmi_priv->context, &error);
|
|
qmi_priv->uimClient = NULL;
|
|
|
|
g_main_context_unref(qmi_priv->context);
|
|
qmi_priv->context = NULL;
|
|
}
|
|
|
|
void qmi_cleanup(struct qmi_data *qmi_priv)
|
|
{
|
|
if (qmi_priv->lastChannelId > 0)
|
|
{
|
|
fprintf(stderr, "Cleaning up leaked APDU channel %d\n", qmi_priv->lastChannelId);
|
|
qmi_apdu_interface_logic_channel_close(NULL, qmi_priv->lastChannelId);
|
|
qmi_priv->lastChannelId = -1;
|
|
}
|
|
}
|