I'm working with an USART device that send to my MCU a series of different commands (also different is size) and I want to try the best way to parse the commands.
I defined two packed structure (one for each command)
typedef ccport_PACKED( struct TASK_CommandStandard
{
UINT8 startByte;
UINT16 length;
UINT8 command;
UINT16 crc16;
}) TASK_CommandStandard_t;
typedef ccport_PACKED( struct TASK_CommandExitBootloader
{
UINT8 startByte;
UINT16 length;
UINT8 command;
UINT8 reserved;
UINT16 crc16;
}) TASK_CommandExitBootloader_t;
and one Union:
typedef union TASK_Command
{
TASK_CommandStandard_t standard;
TASK_CommandExitBootloader_t exitbootloader;
} TASK_Command_t;
My application receives the USART command inside a UINT8 buffer and after that, looking into the 4th byte I can detect the type of the command (standard or exitbootloader).
To parse the command, my idea is to use one pointer TASK_Command_t *newCommand and based on the command code, assign the address of instance.rxFrameBuffer to:
newCommand->exitbootloader = (TASK_CommandExitBootloader_t *)instance.rxFrameBuffer
or
newCommand->standard = (TASK_CommandStandard_t *)instance.rxFrameBuffer
This is my function:
static void TASK_FSM_FrameReceived( void )
{
UINT8 commandCode;
TASK_Command_t *newCommand;
commandCode = instance.rxFrameBuffer[TASK_COMMAND_CODE_INDEX];
if( commandCode == TASK_COMMAND_CODE_EXIT_BOOTLOADER )
{
newCommand->exitbootloader = (TASK_CommandExitBootloader_t *)instance.rxFrameBuffer;
}
else
{
newCommand->standard = (TASK_CommandStandard_t *)instance.rxFrameBuffer;
}
......
}
Unfortunately, the compiler returns this error:
incompatible types when assigning to type 'TASK_CommandExitBootloader_t' {aka 'struct TASK_CommandExitBootloader'} from type 'TASK_CommandExitBootloader_t *' {aka 'struct TASK_CommandExitBootloader *'}
Can someone give me a hint?
newCommand->exitbootloader isn't a pointer, it's a struct, thus if you want to copy data of instance.rxFrameBuffer to this struct, you need to use memcpy, or union. You could also dereference like so *((TASK_CommandExitBootloader_t *)instance.rxFrameBuffer) however this may be undefined behaviour depending the type of rxFrameBuffer, so I don't recommend it.
newCommand is an uninitialized pointer so it can't be used until you point at valid memory somewhere. The code will crash & burn when you attempt newCommand->exitbootloader.
You can't just wildly point into an UART rx buffer and merrily be on your way. Where is this data coming from, interrupts or DMA? How do you handle re-entrancy? Where are the actual volatile qualifier registers and how did you get the data from there?
Strict aliasing is real and it is nasty, particularly if using gcc. So you can't point into a pre-declared uint8_t array buffer for that reason. You can cast between those two different struct types if the union containing them both is present, but better to avoid all such conversions.
I'd also strongly recommend dropping "my local garage standard" types UINT8 or whatever in favour for internationally standardized, well-known C standard types uint8_t etc from stdint.h.
Also regarding hard copy of raw UART buffers on low-end microcontroller systems, that's a very common beginner mistake. See this answer for an example of how to do it properly.
I had to do something similar (pointer to raw data to save memory)
I did something like this :
typedef struct TASK_CommandStandard
{
volatile UINT8 startByte;
volatile UINT16 length;
volatile UINT8 command;
volatile UINT16 crc16;
} TASK_CommandStandard_t;
typedef struct TASK_CommandExitBootloader
{
volatile UINT8 startByte;
volatile UINT16 length;
volatile UINT8 command;
volatile UINT8 reserved;
volatile UINT16 crc16;
} TASK_CommandExitBootloader_t;
#define USART_Foo_Address 0x0F00000
#define USART_cmd ((TASK_CommandStandard_t*) USART_Foo_Address)
#define USART_exit ((TASK_CommandExitBootloader_t*) USART_Foo_Address)
Don't forget to check padding/alignment, it was working fine on my MCU
You use it like this :
static void TASK_FSM_FrameReceived( void )
{
if( instance.rxFrameBuffer[TASK_COMMAND_CODE_INDEX] == TASK_COMMAND_CODE_EXIT_BOOTLOADER )
{
USART_exit->length;
....
}
else
{
USART_cmd->length;
....
}
......
}
I solved modifing the union:
typedef union TASK_Command
{
TASK_CommandStandard_t *standard;
TASK_CommandExitBootloader_t *exitbootloader;
} TASK_Command_t;
and in my function insted using a
TASK_Command_t *newCommand;
I used
TASK_Command_t newCommand;
In this way I can use the same variable to cast my different messages without make any buffer copy.
I can access to the UART buffer with
newCommand.standard = (TASK_CommandStandard_t *)instance.rxFrameBuffer;
Related
I have the following struct definition:
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
} __attribute__((packed)) mb32_packet_t;
Now I would like to have another union, so that I can get an uint8_t body[] pointer to the entire packet object. Something like this:
typedef struct mb32_packet_t {
union {
struct {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
};
uint8_t body[?];
};
} __attribute__((packed)) mb32_packet_t;
The problem is that the payload field size is dynamically determined at runtime. Is there another way to accomplish this other than making payload fixed sized?
I basically want to send objects of this type through a network socket, so I need a uint8_t pointer that points to an object of this type. At the time of sending the object, I know the size of the entire object in bytes.
Introduction
The question is unclear, so I will discuss three apparent possibilities.
Fixed-length header followed by variable-length payload
A typical way to define a packet for a networking or messaging service is to have a fixed-length header followed by a variable-length payload. In modern C, the variable-length payload may be defined using a flexible array member, which is an array with no dimension at the end of a structure:
typedef struct
{
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
uint8_t payload[];
} mb32_packet_t;
Memory for such a structure is allocated use the base size provided by sizeof plus additional memory for the payload:
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength);
When you pass such an object to a routine that requires a char * or uint8_t * or similar type for its argument, you can simply convert the pointer:
SendMyMessage(…, (uint8_t *) MyPacket,…);
That cast, (uint8_t *) MyPacket, provides the pointer to the first byte of the packet requested in the question. There is no need to wedge another member into the structure or layer on a union or other declaration.
Prior to the introduction of flexible array members in C 1999, people would use one of two workarounds to create structures with variable amounts of data. One, they might just define a member array with one element and adjust the space calculations accordingly:
typedef struct
{
…
unsigned char payload[1];
} mb32_packet_t;
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength - 1);
Technically, that violated the C standard, since the structure contained an array of only one element even though more space was allocated for it. However, compilers were not as aggressive in their analysis of program semantics and their optimization as they are now, so it generally worked. So you may still see old code using that method.
Two, GCC had its own pre-standard implementation of flexible array members, just using an array dimension of zero instead of omitting a dimension:
typedef struct
{
…
unsigned char payload[0];
} mb32_packet_t;
Again, you may see old code using that, but new code should use the standard flexible array member.
Fixed-length header with pointer to variable-length payload
The payload-after-header form shown above is the form of packet I would most expect in a messaging packet, because it matches what the hardware has to put “on the wire” when sending bytes across a network: It writes the header bytes followed by the data bytes. So it is convenient to have them arranged that way in memory.
However, your code shows another option: The data is not in the packet but is pointed to by a pointer in the packet, with uint8_t *payload;. I would suspect that is a mistake, that the network or messaging service really wants a flexible array member, but you show it followed by another member, uint16_t checksum. A flexible array member must be the last member in a structure, so the fact that there is another member after the payload suggests this definition with a pointer may be correct for the messaging service you are working with.
However, if that is the case, it is not possible to get a pointer to the complete packet object, because the object is in two pieces. One contains the header, and the other, at some unrelated location in memory, contains the data.
As above, you can produce a uint8_t * pointer to the start of the packet with (uint8_t) MyPacket. If the messaging system knows about the pointer in the structure, that should work. If you have mistaken what the packet structure must be, it will fail.
Fixed-length header followed by fixed-length payload space
Code elsewhere on Stack Overflow shows a struct mb32_packet_t with a fixed amount of space for a payload:
typedef struct mb32_packet_t {
uint8_t compid;
uint8_t servid;
uint8_t payload[248];
uint8_t checksum;
} __attribute__((packed)) mb32_packet_s;
In this form, the packet is always a fixed size, although the amount of space used for the payload could vary. Again, you would obtain a uint8_t * pointer to the packet by a cast. There is no need for a special member for that.
This is possible, but not with a struct or union, because all parts of a struct or union need to have a known size. You can still use a struct for the header.
Because the body starts at a known location, there's a trick you can use to access it as if it was part of the structure. You can declare it with no size at all (a "flexible array member") or as 0 bytes (a GCC extension that predates the standard). The compiler will not allocate any space for it, but it will still let you use the name to refer to the end of the struct. The trick is that you can malloc extra bytes after the end of the struct, and then use body to refer to them.
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t body[]; // flexible array member
} __attribute__((packed)) mb32_packet_t;
// This is not valid. The body is 0 bytes long, so the write is out of bounds.
mb32_packet_t my_packet;
my_packet.body[0] = 1;
// This is valid though!
mb32_packet_t *my_packet2 = malloc(sizeof(*my_packet2) + 50);
my_packet2->body[49] = 1;
// Alternative way to calculate size
mb32_packet_t *my_packet3 = malloc(offsetof(mb32_packet_t, body[50]));
my_packet3->body[49] = 1;
The flexible array member must be last. To access the checksum, you will need to allocate an extra 2 bytes, and use pointer arithmetic. Fortunately, this is just for the checksum, and not the entire header.
mb32_packet_t *my_packet = malloc(sizeof(*my_packet) + body_size + 2);
uint16_t *pchecksum = (uint16_t*)&my_packet.body[body_size];
// or
uint16_t *pchecksum = (uint16_t*)(my_packet.body + body_size);
After you fill in the header, body and checksum, then because they are contiguous in memory, a pointer to the header is also a pointer to the entire packet object.
I usually do it this way:
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[];
}my_packet_t;
or if your compiler does not support FAMs
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[0];
}my_packet_t;
So it the payload can be at the end of the header structure
Consider this typedef:
#pragma pack(4)
typedef struct
{
uint8 dataArea0[11];
uint8 dataArea1[12];
uint8 dataArea2[13];
uint8 dataArea3[14];
} myStruct;
I have some non-2^n sized arrays that I'd like to use from other libs. From those libs these dataAreas can be cast as e.g., structs or whatever i need. The problem occurs when one of these struct members land on a non 4-byte aligned address AND contain data types that are not happy about their address alignment.
Therefore I'd like to force the alignment with the pack pragma, but this does not help (at least in the IAR compiler -- from the manual: Use this pragma directive to specify the maximum alignment of struct and union members.). I also tried to use the data_alignment pragma, but this seems to be for variables and not struct members.
Does anyone know a nice compiler trick to force the alignment of the struct members?
Quicklink to compiler manual for those interested: IAR AVR32 Compiler Ref
Edit:
I ended up using this as an alternative
#define ROUND_UP_NEXT_FOUR(x) ((x + 3) & ~0x03)
typedef struct
{
uint8 dataArea0[ROUND_UP_NEXT_FOUR(11)];
uint8 dataArea1[ROUND_UP_NEXT_FOUR(12)];
uint8 dataArea2[ROUND_UP_NEXT_FOUR(13)];
uint8 dataArea3[ROUND_UP_NEXT_FOUR(14)];
} myStruct;
In this way I'm sure that the padding will take place at a 4-aligned address.
Edit2:
An example of how this can go wrong:
struct otherStruct
{
uint16 dataBuf0;
uint32 dataBuf1;
uint32 dataBuf2;
uint32 dataBuf3;
uint32 dataBuf4[10];
};
myStruct* myStructInstance = 0x00000000; //some address
//address of this is 0x0B
struct otherStruct* oS = (struct otherStruct*) myStructInstance.dataArea1;
//we assign to a 2 byte variable that is
//located at address that is not 2 byte aligned -> error!
os->dataBuf0 = 10;
In this case we get a runtime error (worst (or best?) case, crash).
Unfortunately the IAR AVR32 compiler does not support the _Alignas keyword. However, when IAR language extensions are enabled it supports anonymous unions and this can be used to force the alignment of individual fields of a struct. The trick is that the alignment of a union is the strictest (largest) alignment of any of its fields. Thus, by wrapping each field dataArea? in an anonymous union with a dummy field with 32-bit alignment it is possible to force the alignment of each dataArea? field to 32-bit. An example is shown below. It include both raw anonymous-union declarations as well as macro-magic to simplify declaration when the number of fields is large.
#include <stdint.h>
#define GLUE_B(x,y) x##y
#define GLUE(x,y) GLUE_B(x,y)
#define ALIGNED(FIELD, ALIGN_TYPE) union { FIELD; ALIGN_TYPE GLUE(a,__LINE__); }
#define ALIGNED32(FIELD) ALIGNED(FIELD, uint32_t)
typedef struct
{
ALIGNED(uint8_t dataArea0[11], uint32_t);
ALIGNED32(uint8_t dataArea1[12]);
union { uint8_t dataArea2[13]; uint32_t a2;};
union { uint8_t dataArea3[12]; uint32_t a3;};
} myStruct;
I have a 1-byte pragma packed struct in C which I want to copy into a byte array for serialization purpose to be sent over a serial port.
#pragma pack(push, 1)
typedef struct {
uint8_t ck_a;
uint8_t ck_b;
} UBXChecksum_t ;
#pragma pack(pop)
What is the best way of serializing it into a byte array, should I just use memcpy()?
void writeStructToArray(const void* inStruct,
const uint16_t inLenStruct,
uint8_t* const outArray)
{
memcpy(outArray, inStruct, inLenStruct);
}
or better use byte-by-byte copying doing pointer typecasting?
void writeStructToArray(const void* inStruct,
const uint16_t inLenStruct,
uint8_t* const outArray)
{
for(uint16_t i = 0; i < inLenStruct; i++)
{
outArray[i] = ((uint8_t*)inStruct)[i];
}
}
As Kamil Cuk commented, your two proposals are nearly the same with some possible speed difference.
Another option would be to use a union:
typedef struct {
uint8_t ck_a;
uint8_t ck_b;
} UBXChecksum_t ;
union convert {
UBXChecksum_t checksum;
char buffer[sizeof UBXChecksum_t];
};
UBXChecksum_t checksum;
union convert converter;
converter.checksum = checksum;
passArrayToSomeFunction(converter.buffer, sizeof(converter.buffer));
You don't have to copy the data to convert it to an array. You could pass a pointer to the structure (if necessary casted to char* or void*) and the structure size to a function that sends the data to the serial port. Example:
typedef struct {
uint8_t ck_a;
uint8_t ck_b;
} UBXChecksum_t ;
int sendData(void *buf, size_t size);
UBXChecksum_t checksum;
/* ... */
int rc = sendData(&checksum, sizeof(checksum));
All these variants send the structure's internal representation as binary data. Normally "serializing" is understood as a way to convert the data into a platform-independent format.
Sending binary data structures works if the receiving system is of the same type and using the same compiler. You might get problems when the receiving system uses different byte order or different data type sizes.
In your case you have a structure of two uint8_t values, so the size is fixed and the byte order is not a problem.
It is OK to send binary data if the requirement for the structure is to match a specified binary data protocol and you are prepared to handle the byte order if necessary.
memcpy() will not consider endiannsess of the system. so if Sender is big endian and receiver is little endian then then will be a conflict in the receiver for the structure variable value.
With the second method you know how the byte stream is prepared at sender so at the receiving end also it can receive accordingly to make sure of the proper structure variable value.
If the endianness of the systems is same and endianness is not a concern then both the method will serve the purpose and memcpy() will be faster compare to the assigning the byte value in a loop.
I have a structure used to contruct messages to a control board I need to maintain software compatibility between a C167 16-bit Keil compiler and a 32-bit Tricore gcc compiler.
typedef struct
{
unsigned char new_weld_status[2];
UINT32 new_weld_count;
UINT16 new_weld_fail_count;
} NEW_PULSE_DATA;
The array new_weld_status[2] takes up 2 bytes on the 16-bit compiler but 4 bytes on the 32-bit compiler. I was thinking of replacing all the new_weld_status[2] with a union when compiling with gcc. But is there a switch I can use for gcc that makes the chars fits/aligns in 2 bytes?
Thanks
Note that your structure layout creates the problem on a 32-bit system. Many (most) 32-bit CPU architectures require 4-byte alignment for 32-bit words, thus the new_weld_count requires 'padding' to provide proper memory alignment.
typedef struct
{
unsigned char new_weld_status[2]; //a
//char padding_1[2]; //hidden padding
UINT32 new_weld_count; //a
UINT16 new_weld_fail_count; //a
} NEW_PULSE_DATA;
The following redefinition of your structure completely avoids the problem.
typedef struct
{
UINT32 new_weld_count; //a
UINT16 new_weld_fail_count; //a
unsigned char new_weld_status[2]; //a
} NEW_PULSE_DATA;
NEW_PULSE_DATA ex_PULSE_DATA;
However, the above approach is not the approach typically to transport struct(ured) data across networks/over message transports. A more common and much better approach is to use a serialization/deserialization layer (aka marshalling) to place the structures into 'over the wire' formats. Your current approach is conflating the in-memory storage and addressing with the communication format.
//you need to decide on the size of wire format data,
//Both ends of the protocol must agree on these sizes,
#define new_weld_count_SZ sizeof(ex_PULSE_DATA.new_weld_count)
#define new_weld_fail_count_SZ sizeof(ex_PULSE_DATA.new_weld_fail_count)
#define new_weld_status_SZ sizeof(ex_PULSE_DATA.new_weld_status)
//Then you define a network/message format
typedef struct
{
byte new_weld_count[new_weld_count_SZ];
byte new_weld_fail_count[new_weld_count_SZ];
byte new_weld_status[new_weld_count_SZ];
} MESSAGE_FORMAT_PULSE_DATA;
Then you would implement serialization & deserialization functions on both ends of the transport. The following example is simplistic, but conveys the gist of what you need.
byte*
PULSE_DATA_serialize( MESSAGE_FORMAT_PULSE_DATA* msg, NEW_PULSE_DATA* data )
{
memcpy(&(msg->new_weld_count), data->new_weld_count, new_weld_count_SZ);
memcpy(&(msg->new_weld_fail_count), data->new_weld_fail_count, new_weld_fail_count_SZ);
memcpy(&(msg->new_weld_status), data->new_weld_status, new_weld_status_SZ);
return msg;
}
NEW_PULSE_DATA*
PULSE_DATA_deserialize( NEW_PULSE_DATA* data, MESSAGE_FORMAT_PULSE_DATA* msg )
{
memcpy(data->new_weld_count, &(msg->new_weld_count), new_weld_count_SZ);
memcpy(data->new_weld_fail_count, &(msg->new_weld_fail_count), new_weld_fail_count_SZ);
memcpy(data->new_weld_status, &(msg->new_weld_status), new_weld_status_SZ);
return msg;
}
Note that I have omitted the obligatory network byte order conversions, because I assume your have already worked out your byte order issues between the two cpu domains. If you have not considered byte-order (big-endian vs. little-endian), then you need to address that issue as well.
When you send a message, the sender does the following,
//you need this declared & assigned somewhere
NEW_PULSE_DATA data;
//You need space for your message
MESSAGE_FORMAT_PULSE_DATA msg;
result = send(PULSE_DATA_deserialize( &data, &msg ));
When you receive a message, the recipient does the following,
//recipient needs this declared somewhere
NEW_PULSE_DATA data;
//Need buffer to store received data
MESSAGE_FORMAT_PULSE_DATA msg;
result = receive(&msg,sizeof(msg));
//appropriate receipt checking here...
PULSE_DATA_deserialize( &data, &msg );
Union wouldn't change the members alignment inside a struct. You are interested in padding. The compiler may insert any number of bytes/bits between struct members to satisfy alignment requiremens. On gcc compatible compilers you may use __attribute__((__packed__)) as Acorn already pointed out, but this does not take care of endianess. The most compatible version between platforms (including platforms with different alignment and different endianess) would be to use (sadly!) get/set functions that look like this:
typedef struct {
unsigned char data[2+4+2];
} NEW_PULSE_DATA;
unsigned char NEW_PULSE_DATA_get_new_weld_status(NEW_PULSE_DATA *t, size_t idx) {
return t->data[idx];
}
void NEW_PULSE_DATA_set_new_weld_status(NEW_PULSE_DATA *t, size_t idx, unsigned char value) {
t[idx] = value;
}
UINT32 NEW_PULSE_DATA_get_new_weld_count(NEW_PULSE_DATA *t) {
return (UINT32)t->data[2]<<24
| (UINT32)t->data[3]<<16
| (UINT32)t->data[4]<<8
| (UINT32)t->data[5];
}
void NEW_PULSE_DATA_set_new_weld_count(NEW_PULSE_DATA *t, UINT32 val) {
t->data[2] = val>>24;
t->data[3] = val>>16;
t->data[4] = val>>8;
t->data[5] = val;
}
UINT16 NEW_PULSE_DATA_get_new_weld_fail_count(NEW_PULSE_DATA *t) {
return (UINT16)t->data[6]<<8
| (UINT16)t->data[7];
}
void NEW_PULSE_DATA_set_new_weld_fail_count(NEW_PULSE_DATA *t, UINT16 val) {
t->data[6] = val>>8;
t->data[7] = val;
}
This is the only "good" way of being 100% sure, that NEW_PULSE_DATA looks exactly the same on different platforms (at least on platforms with the same number of bits per char/CHAR_BIT value). However sizeof(NEW_PULSE_DATA) may be still different between platforms, because compiler may insert padding on the end of the struct (after the last member of the structure). So you may want to change NEW_PULSE_DATA type to be just an array of bytes:
typedef unsigned char NEW_PULSE_DATA[2+4+2];
unsigned char NEW_PULSE_DATA_get_new_weld_status(NEW_PULSE_DATA t, size_t idx) {
return t[idx];
}
unsigned char NEW_PULSE_DATA_set_new_weld_status(NEW_PULSE_DATA t, size_t idx, unsigned char value) {
t[idx] = value;
}
UINT32 NEW_PULSE_DATA_get_new_weld_count(NEW_PULSE_DATA t) {
return (UINT32)t[2]<<24
| (UINT32)t[3]<<16
| (UINT32)t[4]<<8
| (UINT32)t[5];
}
void NEW_PULSE_DATA_set_new_weld_count(NEW_PULSE_DATA t, UINT32 val) {
t[2] = val>>24;
t[3] = val>>16;
t[4] = val>>8;
t[5] = val;
}
UINT16 NEW_PULSE_DATA_get_new_weld_fail_count(NEW_PULSE_DATA t) {
return (UINT16)t[6]<<8
| (UINT16)t[7];
}
void NEW_PULSE_DATA_set_new_weld_fail_count(NEW_PULSE_DATA t, UINT16 val)
{
t[6] = val>>8;
t[7] = val;
}
For gcc and other compilers, you can use __attribute__((packed)):
This attribute, attached to struct or union type definition, specifies that each member (other than zero-width bit-fields) of the structure or union is placed to minimize the memory required.
I'm using the SiliconLabs's BGLIB static library for comunicate via SerialPort, with BLED112.
For use this library it's necessary to define two function: one for send and another for receive bytes to and from SerialPort. All data received from serial can be divided in two part: header and data.
Following the "main.c" code of "thermometer-demo" example the function that receive data from serial, receive first of all header and than data. When the header and data has been received, the code parse the header by follow code and initialize a *msg variable:
unsigned char data[256];
struct ble_header hdr;
int r;
r = uart_rx(sizeof(hdr), (unsigned char *)&hdr, UART_TIMEOUT);
.
.
.
if (hdr.lolen) {
r = uart_rx(hdr.lolen, data, UART_TIMEOUT);
.
.
.
}
const struct ble_msg *msg = ble_get_msg_hdr(hdr);
The *msg variable is a ble_msg struct pointer and this struct is so defined:
struct ble_msg
{
struct ble_header hdr;
uint32 params;
ble_cmd_handler handler;
};
handler is a function pointer that is initialized when *msg is inizialized. After parse header the code call this function pointer:
The functions used to initialize this function pointer are defined empty from the library and if you want to use one of this, it's necessary to delete it and rewrite.
In the "thermometer-demo" example is used void ble_evt_gap_scan_response(const struct ble_msg_gap_scan_response_evt_t *msg) function to receive all visibled devices and the ble_msg_gap_scan_response_evt_t are so defined:
PACKSTRUCT(struct ble_msg_gap_scan_response_evt_t
{
int8 rssi;
uint8 packet_type;
bd_addr sender;
uint8 address_type;
uint8 bond;
uint8array data;
});
with uint8array so defined:
typedef struct
{
uint8 len;
uint8 *data;
}uint8array;
Here for me there is the problem: when the msg->handler(data) code is executed and than the ble_evt_gap_scan_response function is called, a cast is executed between data buffer and ble_msg_gap_scan_response_evt_t struct and the content of uint8 *data (in uint8array struct) is initialized with data received from SerialPort and than, will point to a non correct RAM location.
I think the intention is to provide a way to have a directly access to the data but for me is not the correct way.
It's my wrong evaluation or is a bglib bug?
I have contacted SiliconLabs support and that the original libraries are correct and it seem I have a incorrect version of the libraries.
Like the SiliconLabs support suggest me (and like explained very good in this link: http://www.drdobbs.com/questions-answers-creating-variable-siz/184403480), there are 3 possible kind of declarations:
1° declaration:
typedef struct
{
uint8 len;
uint8 data[0];
}uint8array;
and I access to data with:
msg->data->data[0]
msg->data->data[1]
msg->data->data[2]
...
msg->data->data[n]
In this case could happen compiler warns that the buffer index exceeds the limits.
2° declaration:
typedef struct
{
uint8 len;
uint8 data[1000];
}uint8array;
In this case it's possible to access to data in the same way as previous one but or more space has been allocaded than necessary (in embedded sometime it's important optimize because the resources are limited) or, like previous, you could exceed the buffer limits.
3° declaration:
typedef struct
{
uint8 len;
uint8 data[];
}uint8array;
The name of this declaration is Flexible array member (https://en.wikipedia.org/wiki/Flexible_array_member) and are introduced in the C99 standard. In this case it's possible to access to data in the same way as previouses one but the compiler can warns that isn't a standard declaration.
This declaration is the same adopted by SiliconLabs for the library.
In my case I'm using VisualStudio and I have adopted the third solution but compiler return me the warning C4200 (non standard declaration). I tried the first solution and compiler doesn't return me no warns but for me, the clarity of code is missing and could be a problem for future uses.