Merge pull request 'Change IE_METERING timing information from deciseconds to seconds and milliseconds; Fix: AOC-D unit information length; encode AOC-E according to real traces; Add option to disable seconds in time/date IE' (#4) from nt2mku/osmo-cc-misdn-endpoint:nt2mku/work into jolly/work

Reviewed-on: #4
This commit is contained in:
Andreas Eversberg 2024-01-29 16:07:01 +00:00
commit 9b312f04e5
5 changed files with 119 additions and 102 deletions

View File

@ -184,9 +184,9 @@ static void split_3pty(call_t *call)
}
}
/* Generate AOC-S facility IE from metering information */
/* Send AOC-S facility msg from metering information */
#define AOCS_CURRENCY_AMOUNT_PER_UNIT 5
static void generate_aocs_fac(call_t *call)
static void snd_msg_fac_aocs(call_t *call)
{
struct l3_msg *l3m;
uint8_t fac_ie[256];
@ -199,7 +199,7 @@ static void generate_aocs_fac(call_t *call)
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
fac.u.inv.operationValue = Fac_AOCSCurrency;
LOGP(DISDN, LOGL_DEBUG, "Sending AOC-S facility from metering data: connect_units=%d unit_period_decisecs=%d\n", call->metering_connect_units, call->metering_unit_period_decisecs);
LOGP(DISDN, LOGL_DEBUG, "Sending AOC-S facility from metering data: connect_units=%d unit_period_sec=%d.%03d\n", call->metering_connect_units, (uint16_t)call->metering_unit_period.tv_sec, (uint32_t)call->metering_unit_period.tv_usec / 1000);
if(call->metering_connect_units == 0) {
/* Free call */
@ -210,7 +210,7 @@ static void generate_aocs_fac(call_t *call)
fac.u.inv.o.AOCcuril.currencyInfo[1].chargedItem = 0x02; // CallSetup
fac.u.inv.o.AOCcuril.currencyInfo[1].currencyType = 0x84; // Free
}
else if(call->metering_unit_period_decisecs == 0) {
else if(call->metering_unit_period.tv_sec == 0 && call->metering_unit_period.tv_usec == 0) {
/* Connect-only FlatRate call; note some payphones (e.g. BluePhone) only interpret 'BasicComm' charged item, therefore we don't use 'CallSetup' charged item */
LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm FlatRate %d/100 EUR; CallSetup FreeOfCharge\n", AOCS_CURRENCY_AMOUNT_PER_UNIT * call->metering_connect_units);
fac.u.inv.o.AOCcuril.currencyInfoCount = 2;
@ -231,14 +231,14 @@ static void generate_aocs_fac(call_t *call)
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT;
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.multiplier = 1; // 1/100 EUR
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.typeOfCharging = 1; // StepFunction
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Unit duration
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeUnit = ((uint32_t)call->metering_unit_period.tv_sec * 100) + ((uint32_t)call->metering_unit_period.tv_usec / 10000); // Unit duration
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.durLengthTimeScale = 0; // 1/100 s
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = call->metering_unit_period_decisecs * 10; // Granularity
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeUnit = ((uint32_t)call->metering_unit_period.tv_sec * 100) + ((uint32_t)call->metering_unit_period.tv_usec / 10000); // Granularity
fac.u.inv.o.AOCcuril.currencyInfo[0].durationCurrency.granLengthTimeScale = 0; // 1/100 s
fac.u.inv.o.AOCcuril.currencyInfo[1].chargedItem = 0x02; // CallSetup
if(call->metering_connect_units > 1) {
/* Additional CallSetup charge */
LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency %d/100 EUR per %d/100 seconds; CallSetup FlatRate %d/100 EUR\n", AOCS_CURRENCY_AMOUNT_PER_UNIT, call->metering_unit_period_decisecs * 10, AOCS_CURRENCY_AMOUNT_PER_UNIT * (call->metering_connect_units - 1));
LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency %d/100 EUR per %d.%02d seconds; CallSetup FlatRate %d/100 EUR\n", AOCS_CURRENCY_AMOUNT_PER_UNIT, (uint16_t)call->metering_unit_period.tv_sec, (uint32_t)call->metering_unit_period.tv_usec / 10000, AOCS_CURRENCY_AMOUNT_PER_UNIT * (call->metering_connect_units - 1));
fac.u.inv.o.AOCcuril.currencyInfo[1].currencyType = 0xA2; // FlatRate
strncpy((char *)fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currency, "EUR", 10); // Currency value
fac.u.inv.o.AOCcuril.currencyInfo[1].FlatRateCurrency.currencyAmount = AOCS_CURRENCY_AMOUNT_PER_UNIT * (call->metering_connect_units - 1);
@ -246,7 +246,7 @@ static void generate_aocs_fac(call_t *call)
}
else {
/* No additional CallSetup charge */
LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency %d/100 EUR per %d/100 seconds; CallSetup FreeOfCharge\n", AOCS_CURRENCY_AMOUNT_PER_UNIT, call->metering_unit_period_decisecs * 10);
LOGP(DISDN, LOGL_DEBUG, "AOC-S currencyInfoList: BasicComm DurationCurrency %d/100 EUR per %d.%02d seconds; CallSetup FreeOfCharge\n", AOCS_CURRENCY_AMOUNT_PER_UNIT, (uint16_t)call->metering_unit_period.tv_sec, (uint32_t)call->metering_unit_period.tv_usec / 10000);
fac.u.inv.o.AOCcuril.currencyInfo[1].currencyType = 0x84; // Free
}
}
@ -260,33 +260,61 @@ static void generate_aocs_fac(call_t *call)
}
}
/* AOC-D timer callback */
static void aocd_timer_cb(void *data)
/* Send AOC-D facility msg */
static void snd_msg_fac_aocd(call_t *call)
{
struct l3_msg *l3m;
uint8_t fac_ie[256];
struct asn1_parm fac;
if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received) {
LOGP(DISDN, LOGL_DEBUG, "Sending AOC-D facility, subTotal units: %d\n", call->metering_total_units);
memset(&fac, 0, sizeof(fac));
fac.Valid = 1;
fac.comp = CompInvoke;
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
fac.u.inv.operationValue = Fac_AOCDChargingUnit;
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
encodeFac(fac_ie, &fac);
// sending facility
l3m = create_l3msg();
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m);
}
}
/* Encode AOC-E facility */
static void enc_ie_fac_aoce(call_t *call, struct l3_msg *l3m)
{
uint8_t fac_ie[256];
struct asn1_parm fac;
struct l3_msg *l3m;
if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received) {
LOGP(DISDN, LOGL_DEBUG, "Attaching AOC-E facility, total units: %d\n", call->metering_total_units);
memset(&fac, 0, sizeof(fac));
fac.Valid = 1;
fac.comp = CompInvoke;
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
fac.u.inv.operationValue = Fac_AOCEChargingUnit;
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
encodeFac(fac_ie, &fac);
// attach facility
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
}
}
/* Metering unit timer callback */
static void metering_unit_timer_cb(void *data)
{
call_t *call = data;
call->metering_total_units++;
LOGP(DISDN, LOGL_DEBUG, "Sending next AOC-D facility, total_units=%d\n", call->metering_total_units);
osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000);
memset(&fac, 0, sizeof(fac));
fac.Valid = 1;
fac.comp = CompInvoke;
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
fac.u.inv.operationValue = Fac_AOCDChargingUnit;
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
encodeFac(fac_ie, &fac);
// sending facility
l3m = create_l3msg();
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m);
osmo_timer_schedule(&call->metering_unit_timer, call->metering_unit_period.tv_sec, call->metering_unit_period.tv_usec);
snd_msg_fac_aocd(call);
}
/*
@ -461,7 +489,7 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
/* reset AOC information */
call->metering_info_received = 0;
call->aocd_unit_timer_started = 0;
call->metering_total_units = 0;
new_state(call, ISDN_STATE_IN_SETUP);
@ -876,12 +904,8 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
/* send message to osmo-cc */
osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg);
/* stop AOC-D timer */
if(call->aocd_unit_timer_started)
{
osmo_timer_del(&call->aocd_unit_timer);
call->aocd_unit_timer_started = 0;
}
/* stop AOC-D timer and send AOC-E facility */
osmo_timer_del(&call->metering_unit_timer);
}
/* CC-DISCONNECT INDICATION of child instance */
@ -2107,10 +2131,10 @@ skip_ies:
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CALL_PROCEEDING, call->l3_pid, l3m);
/* Metering handling and AOC-S generation */
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period));
if(rc >= 0) {
call->metering_info_received = 1;
generate_aocs_fac(call);
snd_msg_fac_aocs(call);
}
}
@ -2170,10 +2194,10 @@ void alert_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_ALERTING, call->l3_pid, l3m);
/* Metering handling and AOC-S generation */
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period));
if(rc >= 0) {
call->metering_info_received = 1;
generate_aocs_fac(call);
snd_msg_fac_aocs(call);
}
}
@ -2187,8 +2211,6 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
char connected[33];
char display[128];
int rc;
uint8_t fac_ie[256];
struct asn1_parm fac;
/* NT-MODE in setup state we must send PROCEEDING first */
if (!call->proceeding_sent && call->isdn_ep->ntmode) {
@ -2231,7 +2253,7 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
/* date & time, in NT mode only */
if (call->isdn_ep->ntmode) {
time(&current_time);
enc_ie_date(l3m, current_time, 0);
enc_ie_date(l3m, current_time, call->isdn_ep->time_no_sec);
}
/* connected number */
@ -2250,37 +2272,21 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_CONNECT, call->l3_pid, l3m);
/* Metering handling and AOC-S generation */
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period));
if(rc >= 0) {
call->metering_info_received = 1;
generate_aocs_fac(call);
snd_msg_fac_aocs(call);
}
/* AOC-D handling on connect */
if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received && call->metering_connect_units > 0)
{
LOGP(DISDN, LOGL_DEBUG, "Sending first AOC-D facility, units: %d\n", call->metering_connect_units);
if(call->metering_info_received) {
call->metering_total_units = call->metering_connect_units;
snd_msg_fac_aocd(call);
memset(&fac, 0, sizeof(fac));
fac.Valid = 1;
fac.comp = CompInvoke;
fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */
fac.u.inv.operationValue = Fac_AOCDChargingUnit;
fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units;
encodeFac(fac_ie, &fac);
// sending facility
l3m = create_l3msg();
enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]);
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m);
if(call->metering_unit_period_decisecs > 0) {
LOGP(DISDN, LOGL_DEBUG, "Scheduling AOC-D unit information every %d deciseconds.\n", call->metering_unit_period_decisecs);
call->aocd_unit_timer_started = 1;
osmo_timer_setup(&call->aocd_unit_timer, aocd_timer_cb, call);
osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000);
if(call->metering_unit_period.tv_sec > 0 || call->metering_unit_period.tv_usec > 0) {
LOGP(DISDN, LOGL_DEBUG, "Scheduling metering unit timer every %d.%03d seconds.\n", (uint16_t)call->metering_unit_period.tv_sec, (uint32_t)call->metering_unit_period.tv_usec / 1000);
osmo_timer_setup(&call->metering_unit_timer, metering_unit_timer_cb, call);
osmo_timer_schedule(&call->metering_unit_timer, call->metering_unit_period.tv_sec, call->metering_unit_period.tv_usec);
}
}
@ -2386,10 +2392,10 @@ void progress_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
free_l3msg(l3m);
/* Metering handling and AOC-S generation */
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs));
rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period));
if(rc >= 0) {
call->metering_info_received = 1;
generate_aocs_fac(call);
snd_msg_fac_aocs(call);
}
}
@ -2514,17 +2520,16 @@ void disc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
enc_ie_display(l3m, display);
}
/* stop AOC-D timer */
if(call->aocd_unit_timer_started)
{
osmo_timer_del(&call->aocd_unit_timer);
call->aocd_unit_timer_started = 0;
}
/* AOC-E */
enc_ie_fac_aoce(call, l3m);
new_state(call, ISDN_STATE_OUT_DISCONNECT);
/* send message to ISDN */
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_DISCONNECT, call->l3_pid, l3m);
/* stop AOC-D timer */
osmo_timer_del(&call->metering_unit_timer);
}
/* CC-RELEASE REQUEST */
@ -2557,6 +2562,9 @@ void rel_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg)
enc_ie_display(l3m, display);
}
/* AOC-E */
enc_ie_fac_aoce(call, l3m);
new_state(call, ISDN_STATE_OUT_RELEASE);
/* send message to ISDN */
@ -2729,4 +2737,3 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
osmo_cc_free_msg(msg);
}

View File

@ -690,7 +690,7 @@ void isdn_destroy(isdn_t *isdn_ep)
static void clock_timeout(void *data);
/* initialization and configuration to isdn interface instance */
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs)
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs, int time_no_sec)
{
int rc;
void *mui;
@ -728,6 +728,7 @@ int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const cha
isdn_ep->serving_location = serving_location;
isdn_ep->aocd = aocd;
isdn_ep->aocs = aocs;
isdn_ep->time_no_sec = time_no_sec;
/* channel selection list */
if (channel_out) {
@ -853,7 +854,7 @@ void call_destroy(call_t *call)
free((char *)call->sdp);
/* remove metering timer, if still active */
osmo_timer_del(&call->aocd_unit_timer);
osmo_timer_del(&call->metering_unit_timer);
/* detach */
call_p = &call->isdn_ep->call_list;

View File

@ -105,10 +105,11 @@ typedef struct isdn {
int l1hold;
int l2hold;
int l2sock;
int time_no_sec;
void *l2inst;
ph_socket_t *ph_socket;
pthread_mutex_t upqueue_lock;
struct mqueue upqueue;
struct mqueue upqueue;
int upqueue_initialized;
int upqueue_pipe[2];
struct osmo_fd upqueue_ofd;
@ -127,7 +128,7 @@ typedef struct isdn {
struct b_timer_inst b_timer_inst[128];
uint8_t b_buffer[128][160];
int b_buffer_pos[128];
unsigned char l2mask[16]; /* 128 bits for each tei */
unsigned char l2mask[16]; /* 128 bits for each tei */
/* bridging */
int bridge_possible;
@ -141,7 +142,7 @@ typedef struct isdn {
typedef struct call_list {
struct call_list *next;
isdn_t *isdn_ep;
isdn_t *isdn_ep;
/* mISDN states */
uint32_t l3_pid;
@ -184,11 +185,10 @@ typedef struct call_list {
int park_len;
uint8_t park_callid[8];
/* metering/AOC-D/AOC-S */
struct osmo_timer_list aocd_unit_timer;
int aocd_unit_timer_started;
struct osmo_timer_list metering_unit_timer;
int metering_info_received;
struct timeval metering_unit_period;
uint16_t metering_connect_units;
uint16_t metering_unit_period_decisecs;
uint16_t metering_total_units;
/* bridge states */
@ -214,7 +214,7 @@ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive
/* isdn instance */
isdn_t *isdn_create(void);
void isdn_destroy(isdn_t *isdn_ep);
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs);
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs, int time_no_sec);
int isdn_open(isdn_t *isdn_ep);
void isdn_close(isdn_t *isdn_ep);
void isdn_add_msn(isdn_t *isdn_ep, const char *msn);

View File

@ -48,6 +48,7 @@ static int layer1hold = 0;
static int layer2hold = 0;
static int aocd = 0;
static int aocs = 0;
static int time_no_sec = 0;
static const char *channel_out = NULL;
static const char *channel_in = NULL;
static const char *timeouts = NULL;
@ -131,6 +132,8 @@ static void print_help()
printf(" Send AOC-D charging information\n");
printf(" --aocs\n");
printf(" Send AOC-S charging information\n");
printf(" --time-no-seconds\n");
printf(" Send date/time IE without seconds\n");
printf(" -T --local-tones german | oldgerman | american\n");
printf(" Send locally generated tones, if not provided by remote interface.\n");
printf(" -D --debug-misdn\n");
@ -162,6 +165,7 @@ static void print_help()
#define OPT_BR_ONLY 267
#define OPT_AOCD 268
#define OPT_AOCS 269
#define OPT_TIME_NO_SEC 270
static void add_options(void)
{
@ -182,6 +186,7 @@ static void add_options(void)
option_add('T', "local-tones", 1);
option_add(OPT_AOCD, "aocd", 0);
option_add(OPT_AOCS, "aocs", 0);
option_add(OPT_TIME_NO_SEC, "time-no-seconds", 0);
option_add('D', "debug-misdn", 0);
option_add(OPT_SERVING, "serving-location", 1);
option_add('B', "bridging", 1);
@ -253,6 +258,9 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_AOCS:
aocs = 1;
break;
case OPT_TIME_NO_SEC:
time_no_sec = 1;
break;
case 'T':
if (!strcasecmp(argv[argi], "american"))
local_tones = TONES_TYPE_AMERICAN;
@ -392,7 +400,7 @@ int main(int argc, char *argv[])
isdn_tone_generate_ulaw_samples();
/* init instance */
rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs);
rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs, time_no_sec);
if (rc) {
LOGP(DISDN, LOGL_ERROR, "mISDN initializing failed!\n");
goto error;

View File

@ -423,14 +423,14 @@ static int encodeAOCDChargingUnitOperation(__u8 * dest, const struct asn1_parm *
p[i++] = 0x02; // Operation Tag
p[i++] = 0x01; // Tag Length
p[i++] = Fac_AOCDChargingUnit; // Operation Value
p[i++] = 0x30;
p[i++] = 0x30; // specificChargingUnits
p[i++] = (0x09 + len); // Length
p[i++] = 0xa1; // APDU
p[i++] = (0x04 + len); // Length
p[i++] = 0x30;
p[i++] = (0x02 + len);
p[i++] = 0x02; // Operation Tag
p[i++] = 0x01;
p[i++] = 0x30; // recordedUnits
p[i++] = (0x02 + len); // Length
p[i++] = 0x02; // recordedNumberOfUnits
p[i++] = len;
// Recorded units could take up as much as 3 bytes (0xFFFFFF)
p[i] = numberOfUnits & 0xFF;
@ -441,10 +441,10 @@ static int encodeAOCDChargingUnitOperation(__u8 * dest, const struct asn1_parm *
i += len;
p[i++] = 0x82; // Type of charging info
p[i++] = 0x01;
p[i++] = 0x01; // Length
p[i++] = 0x00; // AOC-D so Sub-Total
p[1] = (AOCD_CHARGE_UNIT_IE_LENGTH + len); // IE Payload Length
p[1] = i - 2; // IE Payload Length
p[4] = p[1] - 3; // Invoke Component Length
result = p[1] + 2; // Total Length of IE
}
@ -481,15 +481,16 @@ static int encodeAOCEChargingUnitOperation(__u8 * dest, const struct asn1_parm *
p[i++] = 0x02; // Operation Tag
p[i++] = 0x01; // Tag Length
p[i++] = Fac_AOCEChargingUnit; // Operation Value
p[i++] = 0xa1; // APDU
p[i++] = 0x30;
p[i++] = (0x09 + len); // Length
p[i++] = 0xa1; // APDU
p[i++] = 0x30; // ChargingUnitInfo
p[i++] = (0x08 + len); // Length
p[i++] = 0x30; // specificChargingUnits
p[i++] = (0x06 + len); // Length
p[i++] = 0xa1; // Constructor
p[i++] = (0x04 + len); // Length
p[i++] = 0x30;
p[i++] = (0x02 + len);
p[i++] = 0x02;
p[i++] = 0x02; // AOC-E so Total
p[i++] = 0x30; // recordedUnits
p[i++] = (0x02 + len); // Length
p[i++] = 0x02; // recordedNumberOfUnits
p[i++] = len;
// Recorded units could take up as much as 3 bytes (0xFFFFFF)
p[i] = numberOfUnits & 0xFF;
@ -499,7 +500,7 @@ static int encodeAOCEChargingUnitOperation(__u8 * dest, const struct asn1_parm *
p[i+2] = (numberOfUnits >> 16) & 0xFF;
i += len;
p[1] = (AOCE_CHARGE_UNIT_IE_LENGTH + len); // IE Payload Length
p[1] = i - 2; // IE Payload Length
p[4] = p[1] - 3; // Invoke Component Length
result = p[1] + 2; // Total Length of IE