blob: 2c7173ecd86b5d994e89a2d6c650ebed810d2e37 [file] [log] [blame]
/**
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/chip-zcl-zpro-codec.h>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
static uint16_t doEncodeApsFrame(uint8_t * buffer, uint32_t buf_length, uint16_t profileID, uint16_t clusterId,
uint8_t sourceEndpoint, uint8_t destinationEndpoint, EmberApsOption options, uint16_t groupId,
uint8_t sequence, uint8_t radius)
{
size_t nextOutByte = 0;
#define TRY_WRITE(dataItem) \
do \
{ \
size_t neededSize = nextOutByte + sizeof(dataItem); \
if (buffer) \
{ \
if (neededSize > buf_length) \
{ \
return 0; \
} \
memcpy(buffer + nextOutByte, &dataItem, sizeof(dataItem)); \
} \
nextOutByte = neededSize; \
} while (0) /* No semicolon so callers have to provide it. */
// Simulated APS "frame control" byte.
uint8_t controlByte = 0;
TRY_WRITE(controlByte);
TRY_WRITE(profileID);
TRY_WRITE(clusterId);
TRY_WRITE(sourceEndpoint);
TRY_WRITE(destinationEndpoint);
TRY_WRITE(options);
TRY_WRITE(groupId);
TRY_WRITE(sequence);
TRY_WRITE(radius);
#undef TRY_WRITE
assert(nextOutByte < UINT16_MAX);
buf_length = nextOutByte;
printf("Encoded %" PRIu32 " bytes of aps frame\n", buf_length);
return buf_length;
}
uint16_t encodeApsFrame(uint8_t * buffer, uint16_t buf_length, EmberApsFrame * apsFrame)
{
return doEncodeApsFrame(buffer, buf_length, apsFrame->profileId, apsFrame->clusterId, apsFrame->sourceEndpoint,
apsFrame->destinationEndpoint, apsFrame->options, apsFrame->groupId, apsFrame->sequence,
apsFrame->radius);
}
uint32_t _encodeOnOffCommand(uint8_t * buffer, uint32_t buf_length, int command, uint8_t destination_endpoint)
{
uint32_t result = 0;
// pick cluster id as 6 for now.
// pick source and destination end points as 1 for now.
// Profile is 65535 because that matches our simple generated code, but we
// should sort out the profile situation.
result = doEncodeApsFrame(buffer, buf_length, 65535, 6, 1, destination_endpoint, 0, 0, 0, 0);
if (result == 0 || result > buf_length)
{
printf("Error encoding aps frame result %" PRIu32 "\n", result);
result = 0;
return result;
}
uint32_t indexToWrite = result;
// This is a cluster-specific command so low two bits are 0b01. The command
// is standard, so does not need a manufacturer code, and we're sending
// client to server, so all the remaining bits are 0.
uint8_t * val = buffer + indexToWrite++;
*val = 0x1;
if (indexToWrite >= buf_length)
{
printf("indexToWrite %" PRIu32 "\n", indexToWrite);
return 0;
}
// Transaction sequence number. Just pick something.
val = buffer + indexToWrite++;
*val = 0x1;
if (indexToWrite >= buf_length)
{
return 0;
}
val = buffer + indexToWrite++;
*val = command;
if (indexToWrite >= buf_length)
{
return 0;
}
return indexToWrite;
}
uint32_t encodeOffCommand(uint8_t * buffer, uint32_t buf_length, uint8_t destination_endpoint)
{
return _encodeOnOffCommand(buffer, buf_length, 0, destination_endpoint);
};
uint32_t encodeOnCommand(uint8_t * buffer, uint32_t buf_length, uint8_t destination_endpoint)
{
return _encodeOnOffCommand(buffer, buf_length, 1, destination_endpoint);
}
uint32_t encodeToggleCommand(uint8_t * buffer, uint32_t buf_length, uint8_t destination_endpoint)
{
return _encodeOnOffCommand(buffer, buf_length, 2, destination_endpoint);
}
uint16_t encodeReadAttributesCommand(uint8_t * buffer, uint16_t buf_length, uint8_t destination_endpoint, uint8_t cluster_id,
uint16_t * attr_ids, uint16_t attr_id_count)
{
uint16_t indexToWrite = doEncodeApsFrame(buffer, buf_length, 65535, 6, 1, destination_endpoint, 0, 0, 0, 0);
if (indexToWrite == 0)
{
printf("Error encoding aps frame\n");
return 0;
}
#define TRY_WRITE(val) \
do \
{ \
size_t neededSize = indexToWrite + sizeof(val); \
if (neededSize > buf_length) \
{ \
printf("Can't put %zu bytes in buffer\n", neededSize); \
return 0; \
} \
memcpy(buffer + indexToWrite, &(val), sizeof(val)); \
indexToWrite = neededSize; \
} while (0)
// This is a global command, so the low bits are 0b00. The command is
// standard, so does not need a manufacturer code, and we're sending client
// to server, so all the remaining bits are 0.
uint8_t frameControl = 0x00;
TRY_WRITE(frameControl);
// Transaction sequence number. Just pick something.
uint8_t seqNum = 0x1;
TRY_WRITE(seqNum);
uint8_t readAttributesCommandId = 0x00;
TRY_WRITE(readAttributesCommandId);
for (uint16_t i = 0; i < attr_id_count; ++i)
{
uint16_t attr_id = attr_ids[i];
TRY_WRITE(attr_id);
}
#undef TRY_WRITE
return indexToWrite;
}
uint16_t encodeReadOnOffCommand(uint8_t * buffer, uint16_t buf_length, uint8_t destination_endpoint)
{
uint16_t attr_id = 0x0000; /* OnOff attribute */
return encodeReadAttributesCommand(buffer, buf_length, destination_endpoint, 0x6 /* cluster_id */, &attr_id,
1 /* attr_id_count */);
}