Files
bacnet_stack/src/bacnet/ptransfer.c
T

308 lines
10 KiB
C

/**
* @file
* @brief BACnet PrivateTransfer encode and decode helper functions
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2009
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/ptransfer.h"
/* encode service */
static int pt_encode_apdu(uint8_t *apdu,
uint16_t max_apdu,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
/*
Unconfirmed/ConfirmedPrivateTransfer-Request ::= SEQUENCE {
vendorID [0] Unsigned,
serviceNumber [1] Unsigned,
serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
/* unused parameter */
(void)max_apdu;
if (apdu) {
len =
encode_context_unsigned(&apdu[apdu_len], 0, private_data->vendorID);
apdu_len += len;
len = encode_context_unsigned(
&apdu[apdu_len], 1, private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
}
return apdu_len;
}
int ptransfer_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_PRIVATE_TRANSFER;
apdu_len = 4;
len = pt_encode_apdu(
&apdu[apdu_len], (uint16_t)(MAX_APDU - apdu_len), private_data);
apdu_len += len;
}
return apdu_len;
}
int uptransfer_encode_apdu(
uint8_t *apdu, BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
if (apdu) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_PRIVATE_TRANSFER;
apdu_len = 2;
len = pt_encode_apdu(
&apdu[apdu_len], (uint16_t)(MAX_APDU - apdu_len), private_data);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
int ptransfer_decode_service_request(uint8_t *apdu,
unsigned apdu_len,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int len = 0; /* return value */
int decode_len = 0; /* return value */
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
/* check for value pointers */
if (apdu_len && private_data) {
/* Tag 0: vendorID */
decode_len = decode_context_unsigned(&apdu[len], 0, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len = decode_len;
private_data->vendorID = (uint16_t)unsigned_value;
/* Tag 1: serviceNumber */
decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->serviceNumber = unsigned_value;
/* Tag 2: serviceParameters */
if (decode_is_opening_tag_number(&apdu[len], 2)) {
/* a tag number of 2 is not extended so only one octet */
len++;
/* don't decode the serviceParameters here */
private_data->serviceParameters = &apdu[len];
private_data->serviceParametersLen =
(int)apdu_len - len - 1 /*closing tag */;
/* len includes the data and the closing tag */
len = (int)apdu_len;
} else {
return -1;
}
}
return len;
}
int ptransfer_error_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* length of the part of the encoding */
if (apdu) {
apdu[0] = PDU_TYPE_ERROR;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER;
apdu_len = 3;
/* service parameters */
/*
ConfirmedPrivateTransfer-Error ::= SEQUENCE {
errorType [0] Error,
vendorID [1] Unsigned,
serviceNumber [2] Unsigned,
errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
len = encode_application_enumerated(&apdu[apdu_len], error_class);
apdu_len += len;
len = encode_application_enumerated(&apdu[apdu_len], error_code);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
len =
encode_context_unsigned(&apdu[apdu_len], 1, private_data->vendorID);
apdu_len += len;
len = encode_context_unsigned(
&apdu[apdu_len], 2, private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 3);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
int ptransfer_error_decode_service_request(uint8_t *apdu,
unsigned apdu_len,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int len = 0; /* return value */
int decode_len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
uint32_t enum_value = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
/* check for value pointers */
if (apdu_len && private_data) {
/* Tag 0: Error */
if (decode_is_opening_tag_number(&apdu[len], 0)) {
/* a tag number of 0 is not extended so only one octet */
len++;
/* error class */
decode_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += decode_len;
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
return 0;
}
decode_len =
decode_enumerated(&apdu[len], len_value_type, &enum_value);
len += decode_len;
if (error_class) {
*error_class = (BACNET_ERROR_CLASS)enum_value;
}
/* error code */
decode_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len += decode_len;
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
return 0;
}
decode_len =
decode_enumerated(&apdu[len], len_value_type, &enum_value);
len += decode_len;
if (error_code) {
*error_code = (BACNET_ERROR_CODE)enum_value;
}
if (decode_is_closing_tag_number(&apdu[len], 0)) {
/* a tag number of 0 is not extended so only one octet */
len++;
} else {
return 0;
}
}
/* Tag 1: vendorID */
decode_len = decode_context_unsigned(&apdu[len], 1, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->vendorID = (uint16_t)unsigned_value;
/* Tag 2: serviceNumber */
decode_len = decode_context_unsigned(&apdu[len], 2, &unsigned_value);
if (decode_len < 0) {
return -1;
}
len += decode_len;
private_data->serviceNumber = (uint32_t)unsigned_value;
/* Tag 3: serviceParameters */
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 2 is not extended so only one octet */
len++;
/* don't decode the serviceParameters here */
private_data->serviceParameters = &apdu[len];
private_data->serviceParametersLen =
(int)apdu_len - len - 1 /*closing tag */;
} else {
return -1;
}
/* we could check for a closing tag of 3 */
}
return len;
}
int ptransfer_ack_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
BACNET_PRIVATE_TRANSFER_DATA *private_data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */
apdu[1] = invoke_id; /* original invoke id from request */
apdu[2] = SERVICE_CONFIRMED_PRIVATE_TRANSFER; /* service choice */
apdu_len = 3;
/* service ack follows */
/*
ConfirmedPrivateTransfer-ACK ::= SEQUENCE {
vendorID [0] Unsigned,
serviceNumber [1] Unsigned,
resultBlock [2] ABSTRACT-SYNTAX.&Type OPTIONAL
}
*/
len =
encode_context_unsigned(&apdu[apdu_len], 0, private_data->vendorID);
apdu_len += len;
len = encode_context_unsigned(
&apdu[apdu_len], 1, private_data->serviceNumber);
apdu_len += len;
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
for (len = 0; len < private_data->serviceParametersLen; len++) {
apdu[apdu_len] = private_data->serviceParameters[len];
apdu_len++;
}
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
}
return apdu_len;
}
/* ptransfer_ack_decode_service_request() is the same as
ptransfer_decode_service_request */