driver(APDU): add QMI backend (#131)

* driver: pass EUICC APDU or HTTP struct to driver fini OP

Currently, the .fini driver OP can only be used with global variables, but
that will be an issue when we cant use global variables.

This will be used with direct QMI in order to free the memory used by the
driver private structure instead of global variables.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: rename QRTR QMI helpers to QMI helpers

Almost all of the QRTR QMI helpers will be reused for direct QMI so,
lets rename the sources to indicate that.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: extract common QMI code

Support for using QMI directly and not over QRTR would use a lot of the
same code as QMI over QRTR, so in order to prevent code duplication lets
extract that common code so it can be reused.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: qmi-helpers: allow compiling without libqrtr

Direct QMI backend wont use any of the libqmi QRTR functionality so it
wont depend on libqrtr and thus we need to make sure QMI helpers still
compile without libqrtr in the system.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: dont use global variables for QMI

Since we split out the common QMI code then we cannot be using global
variables since existing QRTR and the coming QMI drivers will clash
by trying to use the same global variable names with the common code.

So, lets move to passing a structure which is driver specific instead.

Since we are not using global variables anymore, we cannot be using atexit
anymore, so lets move that cleanup step to driver finish OP instead.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: add QMI backend

Previously QMI over QRTR support was added, however that is only present in
modern Qualcomm modems and only when running in PCIe mode and its quite
common to still only use even the latest modems via USB.

In that case, they are all still controllable via QMI but it is directly
exposed as a character device in Linux so we can reuse most of the code
from QMI over QRTR support but drop the support for libqrtr to talk to
the modems.

We require the QMI device path to be passed via QMI_DEVICE env variable
for the backend to operate.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* docs: ENVVARS: document QMI backend

Document the new direct QMI backend ENV variables as well as make it
clear that UIM_SLOT is not a QMI QRTR only variable.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: qmi-helpers: support opening QMI device in proxy mode

Currently, we are using QMI_DEVICE_OPEN_FLAGS_NONE to open the QMI device
and thus we are requesting exclusive access to the device while we need
to talk to it.

This will work fine as long as we are the only thing trying to use that
QMI device at the same time or the device was not already opened in proxy
mode.

This is an issue since ModemManager will open the device in proxy mode so
that qmicli or other applications can still talk to the same QMI device
but it will break lpac from trying to use QMI.

So, lets try and open the device in proxy mode, libqmi will the start the
qmi-proxy service automatically as its built and installed as part of it.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

---------

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
This commit is contained in:
Robert Marko 2024-07-24 09:59:37 +02:00 committed by GitHub
parent 0011ea6cc4
commit 3bde4a1d3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 409 additions and 199 deletions

View file

@ -6,6 +6,7 @@
- `at`: use AT commands interface used by LTE module
- `pcsc`: use PC/SC Smart Card API
- `stdio`: use standard input/output
- `qmi`: use QMI
- `qmi_qrtr`: use QMI over QRTR
- GBinder-based backends for `libhybris` (Halium) distributions:
- `gbinder_hidl`: use HIDL IRadio (SoC launched before Android 13)
@ -13,7 +14,8 @@
- `curl`: use libcurl
- `stdio`: use standard input/ouput
* `AT_DEVICE`: specify which serial port device will be used by AT APDU backend.
* `UIM_SLOT`: specify which UIM slot will be used by QMI QRTR APDU backend. (default: 1)
* `QMI_DEVICE`: specify which QMI device will be used by QMI APDU backend.
* `UIM_SLOT`: specify which UIM slot will be used by QMI APDU backends. (default: 1)
* `DRIVER_IFID`: specify which PC/SC interface will be used by PC/SC APDU backend.
## Debug

View file

@ -4,6 +4,7 @@ cmake_dependent_option(LPAC_DYNAMIC_DRIVERS "Build lpac/libeuicc driver backends
option(LPAC_WITH_APDU_PCSC "Build APDU PCSC Backend (requires PCSC libraries)" ON)
option(LPAC_WITH_APDU_AT "Build APDU AT Backend" ON)
option(LPAC_WITH_APDU_GBINDER "Build APDU Gbinder backend for libhybris devices (requires gbinder headers)" OFF)
option(LPAC_WITH_APDU_QMI "Build QMI backend for Qualcomm devices (requires libqmi)" OFF)
option(LPAC_WITH_APDU_QMI_QRTR "Build QMI-over-QRTR backend for Qualcomm devices (requires libqrtr and libqmi headers)" OFF)
option(LPAC_WITH_HTTP_CURL "Build HTTP Curl interface" ON)
@ -59,9 +60,26 @@ if(LPAC_WITH_APDU_GBINDER)
endif()
endif()
if(LPAC_WITH_APDU_QMI)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_QMI")
target_sources(euicc-drivers PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi.c
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_helpers.c
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_common.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(QMI_GLIB REQUIRED IMPORTED_TARGET qmi-glib)
target_link_libraries(euicc-drivers PkgConfig::QMI_GLIB)
if(LPAC_DYNAMIC_DRIVERS)
list(APPEND LIBEUICC_DRIVERS_REQUIRES "qmi-glib")
endif()
endif()
if(LPAC_WITH_APDU_QMI_QRTR)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLPAC_WITH_APDU_QMI_QRTR")
target_sources(euicc-drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_qrtr.c ${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_qrtr_helpers.c)
target_sources(euicc-drivers PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_qrtr.c
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_helpers.c
${CMAKE_CURRENT_SOURCE_DIR}/apdu/qmi_common.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(QRTR_GLIB REQUIRED IMPORTED_TARGET qrtr-glib)
pkg_check_modules(QMI_GLIB REQUIRED IMPORTED_TARGET qmi-glib)

View file

@ -218,7 +218,7 @@ static int libapduinterface_main(int argc, char **argv)
return 0;
}
static void libapduinterface_fini(void)
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
}
@ -227,5 +227,5 @@ const struct euicc_driver driver_apdu_at = {
.name = "at",
.init = (int (*)(void *))libapduinterface_init,
.main = libapduinterface_main,
.fini = libapduinterface_fini,
.fini = (void (*)(void *))libapduinterface_fini,
};

View file

@ -314,7 +314,7 @@ static int libapduinterface_main(int argc, char **argv)
return 0;
}
static void libapduinterface_fini(void)
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
}

View file

@ -453,7 +453,7 @@ static int libapduinterface_main(int argc, char **argv)
return 0;
}
static void libapduinterface_fini(void)
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
}
@ -462,5 +462,5 @@ const struct euicc_driver driver_apdu_pcsc = {
.name = "pcsc",
.init = (int (*)(void *))libapduinterface_init,
.main = libapduinterface_main,
.fini = libapduinterface_fini,
.fini = (void (*)(void *))libapduinterface_fini,
};

101
driver/apdu/qmi.c Normal file
View file

@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2024, Robert Marko <robert.marko@sartura.hr>
*/
#include "qmi.h"
#include <stdio.h>
#include "qmi_common.h"
static int apdu_interface_connect(struct euicc_ctx *ctx)
{
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
g_autoptr(GError) error = NULL;
QmiDevice *device = NULL;
QmiClient *client = NULL;
const char *device_path;
GFile *file;
if (!(device_path = getenv("QMI_DEVICE"))) {
fprintf(stderr, "No QMI device path specified!\n");
return -1;
}
file = g_file_new_for_path(device_path);
qmi_priv->context = g_main_context_new();
device = qmi_device_new_from_path(file, qmi_priv->context, &error);
if (!device) {
fprintf(stderr, "error: create QMI device from path failed: %s\n", error->message);
return -1;
}
qmi_device_open_sync(device, qmi_priv->context, &error);
if (error) {
fprintf(stderr, "error: open QMI device failed: %s\n", error->message);
return -1;
}
client = qmi_device_allocate_client_sync(device, qmi_priv->context, &error);
if (!client) {
fprintf(stderr, "error: allocate QMI client failed: %s\n", error->message);
return -1;
}
qmi_priv->uimClient = QMI_CLIENT_UIM(client);
return 0;
}
static int libapduinterface_init(struct euicc_apdu_interface *ifstruct)
{
struct qmi_data *qmi_priv;
qmi_priv = malloc(sizeof(struct qmi_data));
if(!qmi_priv) {
fprintf(stderr, "Failed allocating memory\n");
return -1;
}
memset(ifstruct, 0, sizeof(struct euicc_apdu_interface));
ifstruct->connect = apdu_interface_connect;
ifstruct->disconnect = qmi_apdu_interface_disconnect;
ifstruct->logic_channel_open = qmi_apdu_interface_logic_channel_open;
ifstruct->logic_channel_close = qmi_apdu_interface_logic_channel_close;
ifstruct->transmit = qmi_apdu_interface_transmit;
/*
* Allow the user to select the SIM card slot via environment variable.
* Use the primary SIM slot if not set.
*/
if (getenv("UIM_SLOT"))
qmi_priv->uimSlot = atoi(getenv("UIM_SLOT"));
else
qmi_priv->uimSlot = 1;
ifstruct->userdata = qmi_priv;
return 0;
}
static int libapduinterface_main(int argc, char **argv)
{
return 0;
}
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
struct qmi_data *qmi_priv = ifstruct->userdata;
qmi_cleanup(qmi_priv);
free(qmi_priv);
}
const struct euicc_driver driver_apdu_qmi = {
.type = DRIVER_APDU,
.name = "qmi",
.init = (int (*)(void *))libapduinterface_init,
.main = libapduinterface_main,
.fini = (void (*)(void *))libapduinterface_fini,
};

8
driver/apdu/qmi.h Normal file
View file

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2024, Robert Marko <robert.marko@sartura.hr>
*/
#pragma once
#include <driver.private.h>
extern const struct euicc_driver driver_apdu_qmi;

163
driver/apdu/qmi_common.c Normal file
View file

@ -0,0 +1,163 @@
// 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 != -1)
{
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;
}
}

21
driver/apdu/qmi_common.h Normal file
View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2024, Luca Weiss <luca.weiss@fairphone.com>
*/
#include <euicc/interface.h>
#include <euicc/euicc.h>
#include "qmi_helpers.h"
struct qmi_data {
int lastChannelId;
int uimSlot;
GMainContext *context;
QmiClientUim *uimClient;
};
int qmi_apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len);
int qmi_apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len);
void qmi_apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel);
void qmi_apdu_interface_disconnect(struct euicc_ctx *ctx);
void qmi_cleanup(struct qmi_data *qmi_priv);

View file

@ -3,7 +3,7 @@
* Copyright (c) 2024, Luca Weiss <luca.weiss@fairphone.com>
*/
#include "qmi_qrtr_helpers.h"
#include "qmi_helpers.h"
static void
async_result_ready(GObject *source_object,
@ -16,6 +16,7 @@ async_result_ready(GObject *source_object,
*result_out = g_object_ref(res);
}
#ifdef LPAC_WITH_APDU_QMI_QRTR
QrtrBus *
qrtr_bus_new_sync(GMainContext *context,
GError **error)
@ -56,6 +57,33 @@ qmi_device_new_from_node_sync(QrtrNode *node,
return qmi_device_new_from_node_finish(result, error);
}
#endif
#ifdef LPAC_WITH_APDU_QMI
QmiDevice *
qmi_device_new_from_path(GFile *file,
GMainContext *context,
GError **error)
{
g_autoptr(GMainContextPusher) pusher = NULL;
g_autoptr(GAsyncResult) result = NULL;
g_autofree gchar *id = NULL;
pusher = g_main_context_pusher_new(context);
id = g_file_get_path (file);
if (id)
qmi_device_new(file,
NULL,
async_result_ready,
&result);
while (result == NULL)
g_main_context_iteration(context, TRUE);
return qmi_device_new_finish(result, error);
}
#endif
gboolean
qmi_device_open_sync(QmiDevice *device,
@ -68,7 +96,7 @@ qmi_device_open_sync(QmiDevice *device,
pusher = g_main_context_pusher_new(context);
qmi_device_open(device,
QMI_DEVICE_OPEN_FLAGS_NONE,
QMI_DEVICE_OPEN_FLAGS_PROXY,
15,
NULL,
async_result_ready,

View file

@ -3,9 +3,11 @@
* Copyright (c) 2024, Luca Weiss <luca.weiss@fairphone.com>
*/
#include <libqrtr-glib.h>
#include <libqmi-glib.h>
#ifdef LPAC_WITH_APDU_QMI_QRTR
#include <libqrtr-glib.h>
QrtrBus *qrtr_bus_new_sync(
GMainContext *context,
GError **error);
@ -15,6 +17,15 @@ qmi_device_new_from_node_sync(
QrtrNode *node,
GMainContext *context,
GError **error);
#endif
#ifdef LPAC_WITH_APDU_QMI
QmiDevice *
qmi_device_new_from_path(
GFile *file,
GMainContext *context,
GError **error);
#endif
gboolean
qmi_device_open_sync(

View file

@ -11,25 +11,22 @@
#include <string.h>
#include <stdbool.h>
#include <libqrtr-glib.h>
#include "qmi_qrtr_helpers.h"
#include "qmi_common.h"
static int lastChannelId = -1;
static int uimSlot = -1;
static GMainContext *context = NULL;
static QrtrBus *bus = NULL;
static QmiClientUim *uimClient = NULL;
static int apdu_interface_connect(struct euicc_ctx *ctx)
{
struct qmi_data *qmi_priv = ctx->apdu.interface->userdata;
g_autoptr(GError) error = NULL;
QrtrNode *node = NULL;
QmiDevice *device = NULL;
QmiClient *client = NULL;
bool found = false;
context = g_main_context_new();
qmi_priv->context = g_main_context_new();
bus = qrtr_bus_new_sync(context, &error);
bus = qrtr_bus_new_sync(qmi_priv->context, &error);
if (bus == NULL)
{
fprintf(stderr, "error: connect to QRTR bus failed: %s\n", error->message);
@ -54,202 +51,49 @@ static int apdu_interface_connect(struct euicc_ctx *ctx)
return -1;
}
device = qmi_device_new_from_node_sync(node, context, &error);
device = qmi_device_new_from_node_sync(node, qmi_priv->context, &error);
if (!device)
{
fprintf(stderr, "error: create QMI device from QRTR node failed: %s\n", error->message);
return -1;
}
qmi_device_open_sync(device, context, &error);
qmi_device_open_sync(device, qmi_priv->context, &error);
if (error)
{
fprintf(stderr, "error: open QMI device failed: %s\n", error->message);
return -1;
}
client = qmi_device_allocate_client_sync(device, context, &error);
client = qmi_device_allocate_client_sync(device, qmi_priv->context, &error);
if (!client)
{
fprintf(stderr, "error: allocate QMI client failed: %s\n", error->message);
return -1;
}
uimClient = QMI_CLIENT_UIM(client);
qmi_priv->uimClient = QMI_CLIENT_UIM(client);
return 0;
}
static void apdu_interface_disconnect(struct euicc_ctx *ctx)
{
g_autoptr(GError) error = NULL;
QmiClient *client = QMI_CLIENT(uimClient);
QmiDevice *device = QMI_DEVICE(qmi_client_get_device(client));
qmi_device_release_client_sync(device, client, context, &error);
uimClient = NULL;
g_main_context_unref(context);
context = NULL;
}
static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len)
{
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, uimSlot, NULL);
qmi_message_uim_send_apdu_input_set_channel_id(input, lastChannelId, NULL);
qmi_message_uim_send_apdu_input_set_apdu(input, apdu_data, NULL);
QmiMessageUimSendApduOutput *output;
output = qmi_client_uim_send_apdu_sync(uimClient, input, 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;
}
static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len)
{
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, 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(uimClient, input, 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;
}
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;
}
static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel)
{
g_autoptr(GError) error = NULL;
QmiMessageUimLogicalChannelInput *input;
input = qmi_message_uim_logical_channel_input_new();
qmi_message_uim_logical_channel_input_set_slot(input, uimSlot, NULL);
qmi_message_uim_logical_channel_input_set_channel_id(input, channel, NULL);
QmiMessageUimLogicalChannelOutput *output;
output = qmi_client_uim_logical_channel_sync(uimClient, input, 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 == lastChannelId)
lastChannelId = -1;
g_debug("Closed logical channel with id %d", channel);
qmi_message_uim_logical_channel_output_unref(output);
}
static void cleanup(void)
{
if (lastChannelId != -1)
{
fprintf(stderr, "Cleaning up leaked APDU channel %d\n", lastChannelId);
apdu_interface_logic_channel_close(NULL, lastChannelId);
lastChannelId = -1;
}
}
static void sighandler(int sig)
{
// This triggers atexit() hooks and therefore call cleanup()
exit(0);
}
static int libapduinterface_init(struct euicc_apdu_interface *ifstruct)
{
struct qmi_data *qmi_priv;
qmi_priv = malloc(sizeof(struct qmi_data));
if(!qmi_priv) {
fprintf(stderr, "Failed allocating memory\n");
return -1;
}
memset(ifstruct, 0, sizeof(struct euicc_apdu_interface));
ifstruct->connect = apdu_interface_connect;
ifstruct->disconnect = apdu_interface_disconnect;
ifstruct->logic_channel_open = apdu_interface_logic_channel_open;
ifstruct->logic_channel_close = apdu_interface_logic_channel_close;
ifstruct->transmit = apdu_interface_transmit;
// Install cleanup routine
atexit(cleanup);
signal(SIGINT, sighandler);
ifstruct->disconnect = qmi_apdu_interface_disconnect;
ifstruct->logic_channel_open = qmi_apdu_interface_logic_channel_open;
ifstruct->logic_channel_close = qmi_apdu_interface_logic_channel_close;
ifstruct->transmit = qmi_apdu_interface_transmit;
/*
* Allow the user to select the SIM card slot via environment variable.
@ -257,13 +101,15 @@ static int libapduinterface_init(struct euicc_apdu_interface *ifstruct)
*/
if (getenv("UIM_SLOT"))
{
uimSlot = atoi(getenv("UIM_SLOT"));
qmi_priv->uimSlot = atoi(getenv("UIM_SLOT"));
}
else
{
uimSlot = 1;
qmi_priv->uimSlot = 1;
}
ifstruct->userdata = qmi_priv;
return 0;
}
@ -272,8 +118,13 @@ static int libapduinterface_main(int argc, char **argv)
return 0;
}
static void libapduinterface_fini(void)
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
struct qmi_data *qmi_priv = ifstruct->userdata;
qmi_cleanup(qmi_priv);
free(qmi_priv);
}
const struct euicc_driver driver_apdu_qmi_qrtr = {
@ -281,5 +132,5 @@ const struct euicc_driver driver_apdu_qmi_qrtr = {
.name = "qmi_qrtr",
.init = (int (*)(void *))libapduinterface_init,
.main = libapduinterface_main,
.fini = libapduinterface_fini,
.fini = (void (*)(void *))libapduinterface_fini,
};

View file

@ -340,7 +340,7 @@ static int libapduinterface_main(int argc, char **argv)
return 0;
}
static void libapduinterface_fini(void)
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
{
}
@ -349,5 +349,5 @@ const struct euicc_driver driver_apdu_stdio = {
.name = "stdio",
.init = (int (*)(void *))libapduinterface_init,
.main = libapduinterface_main,
.fini = libapduinterface_fini,
.fini = (void (*)(void *))libapduinterface_fini,
};

View file

@ -8,6 +8,10 @@
#include "driver/apdu/gbinder_hidl.h"
#endif
#ifdef LPAC_WITH_APDU_QMI
#include "driver/apdu/qmi.h"
#endif
#ifdef LPAC_WITH_APDU_QMI_QRTR
#include "driver/apdu/qmi_qrtr.h"
#endif
@ -28,6 +32,9 @@ static const struct euicc_driver *drivers[] = {
#ifdef LPAC_WITH_APDU_GBINDER
&driver_apdu_gbinder_hidl,
#endif
#ifdef LPAC_WITH_APDU_QMI
&driver_apdu_qmi,
#endif
#ifdef LPAC_WITH_APDU_QMI_QRTR
&driver_apdu_qmi_qrtr,
#endif
@ -112,10 +119,10 @@ void euicc_driver_fini()
{
if (_driver_apdu != NULL)
{
_driver_apdu->fini();
_driver_apdu->fini(&euicc_driver_interface_apdu);
}
if (_driver_http != NULL)
{
_driver_http->fini();
_driver_http->fini(&euicc_driver_interface_http);
}
}

View file

@ -12,5 +12,5 @@ struct euicc_driver
const char *name;
int (*init)(void *interface);
int (*main)(int argc, char **argv);
void (*fini)(void);
void (*fini)(void *interface);
};

View file

@ -192,7 +192,7 @@ static int libhttpinterface_main(int argc, char **argv)
return 0;
}
static void libhttpinterface_fini(void)
static void libhttpinterface_fini(struct euicc_http_interface *ifstruct)
{
}
@ -201,5 +201,5 @@ const struct euicc_driver driver_http_curl = {
.name = "curl",
.init = (int (*)(void *))libhttpinterface_init,
.main = libhttpinterface_main,
.fini = libhttpinterface_fini,
.fini = (void (*)(void *))libhttpinterface_fini,
};

View file

@ -272,7 +272,7 @@ static int libhttpinterface_main(int argc, char **argv)
return 0;
}
static void libhttpinterface_fini(void)
static void libhttpinterface_fini(struct euicc_http_interface *ifstruct)
{
}
@ -281,5 +281,5 @@ const struct euicc_driver driver_http_stdio = {
.name = "stdio",
.init = (int (*)(void *))libhttpinterface_init,
.main = libhttpinterface_main,
.fini = libhttpinterface_fini,
.fini = (void (*)(void *))libhttpinterface_fini,
};