mirror of
https://github.com/estkme-group/lpac
synced 2025-08-18 10:03:02 +02:00
211 lines
6.6 KiB
C
211 lines
6.6 KiB
C
#include "at.h"
|
|
#include "at_cmd.h"
|
|
|
|
#include <cjson/cJSON_ex.h>
|
|
#include <euicc/hexutil.h>
|
|
#include <euicc/interface.h>
|
|
#include <lpac/utils.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int at_emit_command(struct at_userdata *userdata, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
|
|
|
static int at_emit_command(struct at_userdata *userdata, const char *fmt, ...) {
|
|
va_list args, args_length;
|
|
va_start(args, fmt);
|
|
|
|
va_copy(args_length, args);
|
|
const int n = vsnprintf(NULL, 0, fmt, args_length);
|
|
va_end(args_length);
|
|
|
|
char *formatted = calloc(n + 2 /* CR+LF */ + 1, 1);
|
|
if (formatted == NULL) {
|
|
va_end(args);
|
|
return -1;
|
|
}
|
|
|
|
vsnprintf(formatted, n + 1, fmt, args);
|
|
va_end(args);
|
|
|
|
formatted[n + 0] = '\r'; // CR
|
|
formatted[n + 1] = '\n'; // LF
|
|
formatted[n + 2] = '\0'; // NUL
|
|
|
|
if (getenv_or_default(ENV_AT_DEBUG, (bool)false))
|
|
fprintf(stderr, "AT_DEBUG_TX: %s", formatted);
|
|
|
|
return at_write_command(userdata, formatted);
|
|
}
|
|
|
|
static int apdu_interface_connect(struct euicc_ctx *ctx) {
|
|
struct at_userdata *userdata = ctx->apdu.interface->userdata;
|
|
|
|
const char *device = getenv_or_default(ENV_AT_DEVICE, get_at_default_device(userdata));
|
|
if (at_device_open(userdata, device) != 0) {
|
|
fprintf(stderr, "Failed to open device: %s\n", device);
|
|
return -1;
|
|
}
|
|
|
|
// TS 127 007, 8.43 Generic UICC logical channel access +CGLA
|
|
// TS 127 007, 8.45 Open logical channel +CCHO
|
|
// TS 127 007, 8.46 Close logical channel +CCHC
|
|
static const char *commands[] = {"AT+CCHO", "AT+CCHC", "AT+CGLA", NULL};
|
|
|
|
at_emit_command(userdata, "AT");
|
|
if (at_expect(userdata, NULL, NULL) != 0) {
|
|
fprintf(stderr, "Device not responding to AT commands\n");
|
|
return false;
|
|
}
|
|
|
|
for (int index = 0; commands[index] != NULL; index++) {
|
|
at_emit_command(userdata, "%s=?", commands[index]);
|
|
if (at_expect(userdata, NULL, NULL) == 0)
|
|
continue;
|
|
fprintf(stderr, "Device missing %s support\n", commands[index]);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void apdu_interface_disconnect(struct euicc_ctx *ctx) {
|
|
struct at_userdata *userdata = ctx->apdu.interface->userdata;
|
|
|
|
at_device_close(userdata);
|
|
}
|
|
|
|
static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx,
|
|
const uint32_t tx_len) {
|
|
struct at_userdata *userdata = ctx->apdu.interface->userdata;
|
|
const int logic_channel = ctx->apdu._internal.logic_channel;
|
|
|
|
int fret = 0;
|
|
char *response = NULL;
|
|
|
|
int ret;
|
|
char *encoded = NULL;
|
|
|
|
*rx = NULL;
|
|
*rx_len = 0;
|
|
|
|
encoded = malloc(tx_len * 2 + 1);
|
|
euicc_hexutil_bin2hex(encoded, tx_len * 2 + 1, tx, tx_len);
|
|
|
|
// AT+CGLA=<channel>,<length>,<command>
|
|
at_emit_command(userdata, "AT+CGLA=%d,%u,\"%s\"", logic_channel, tx_len * 2, encoded);
|
|
// +CGLA: <length>,<response>
|
|
if (at_expect(userdata, &response, "+CGLA: ") != 0 || response == NULL)
|
|
goto err;
|
|
// Parse the response
|
|
{
|
|
strtok(response, ","); // Skip the length part
|
|
response = strtok(NULL, ","); // Get the response part
|
|
// unquote strings
|
|
const size_t n = strlen(response);
|
|
if (response[0] == '"' && response[n - 1] == '"') {
|
|
memmove(response, response + 1, n - 2);
|
|
response[n - 2] = '\0'; // Null-terminate the new string
|
|
}
|
|
}
|
|
|
|
*rx_len = strlen(response) / 2;
|
|
*rx = malloc(*rx_len);
|
|
if (*rx == NULL)
|
|
goto err;
|
|
ret = euicc_hexutil_hex2bin_r(*rx, *rx_len, response, strlen(response));
|
|
if (ret < 0)
|
|
goto err;
|
|
*rx_len = ret;
|
|
|
|
goto exit;
|
|
|
|
err:
|
|
fret = -1;
|
|
free(*rx);
|
|
*rx = NULL;
|
|
*rx_len = 0;
|
|
exit:
|
|
return fret;
|
|
}
|
|
|
|
static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, const uint8_t aid_len) {
|
|
struct at_userdata *userdata = ctx->apdu.interface->userdata;
|
|
|
|
for (int channel = 1; channel <= 20; channel++) {
|
|
at_emit_command(userdata, "AT+CCHC=%d", channel);
|
|
at_expect(userdata, NULL, NULL);
|
|
}
|
|
|
|
_cleanup_free_ char *aid_hex = malloc(aid_len * 2 + 1);
|
|
euicc_hexutil_bin2hex(aid_hex, aid_len * 2 + 1, aid, aid_len);
|
|
|
|
// AT+CCHO: Open logical channel
|
|
at_emit_command(userdata, "AT+CCHO=\"%s\"", aid_hex);
|
|
// +CCHO: <channel>
|
|
_cleanup_free_ char *response = NULL;
|
|
if (at_expect(userdata, &response, "+CCHO: ") != 0 || response == NULL)
|
|
return -1;
|
|
return (int)strtol(response, NULL, 10);
|
|
}
|
|
|
|
static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, const uint8_t channel) {
|
|
struct at_userdata *userdata = ctx->apdu.interface->userdata;
|
|
|
|
at_emit_command(userdata, "AT+CCHC=%d", channel);
|
|
at_expect(userdata, NULL, NULL);
|
|
}
|
|
|
|
static int libapduinterface_init(struct euicc_apdu_interface *ifstruct) {
|
|
set_deprecated_env_name(ENV_AT_DEBUG, "AT_DEBUG");
|
|
set_deprecated_env_name(ENV_AT_DEVICE, "AT_DEVICE");
|
|
|
|
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;
|
|
ifstruct->userdata = NULL;
|
|
|
|
return at_setup_userdata((struct at_userdata **)&ifstruct->userdata);
|
|
}
|
|
|
|
static int libapduinterface_main(const struct euicc_apdu_interface *ifstruct, const int argc, char **argv) {
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: %s <list>\n", argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(argv[1], "list") == 0) {
|
|
cJSON *devices = cJSON_CreateArray();
|
|
if (enumerate_serial_device != NULL) {
|
|
enumerate_serial_device(devices);
|
|
} else {
|
|
fprintf(stderr, "Serial device enumeration not implemented on this platform.\n");
|
|
fflush(stderr);
|
|
}
|
|
cJSON *payload = cJSON_CreateObject();
|
|
cJSON_AddStringOrNullToObject(payload, "env", ENV_AT_DEVICE);
|
|
cJSON_AddItemToObject(payload, "data", devices);
|
|
json_print("driver", payload);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct) {
|
|
struct at_userdata *userdata = ifstruct->userdata;
|
|
|
|
at_cleanup_userdata(&userdata);
|
|
}
|
|
|
|
const struct euicc_driver driver_apdu_at = {
|
|
.type = DRIVER_APDU,
|
|
.name = "at",
|
|
.init = (int (*)(void *))libapduinterface_init,
|
|
.main = (int (*)(void *, int, char **))libapduinterface_main,
|
|
.fini = (void (*)(void *))libapduinterface_fini,
|
|
};
|