Is there a picture or document describe communicate layer of Photon system firmware?

When I get source code of Photon system firmware I am very confuse how these code work and organize together.
1.What is the dynamic lib?Is there a dynamic loader?
2.Is there a picture or document describe the internal relationship between stm32-hal-lib,wiced,wiring…
3.I am trying to find out the protocol layer of system firmware but I get more confused,I think it should be wiced(wifi driver)-lwip(TCP/IP)-mbedtls(TLS)-CoAP(APP layter).

1 Like

There isn’t much documentation at that level because it’s generally not information you need to worry about. It’s (almost all) open source, however.

1.What is the dynamic lib?Is there a dynamic loader?

The dynamic libraries are standard gcc-arm shared libraries. It allows chunks of code to be dynamically linked at runtime. It’s how the system and user parts are able to call functions in the other parts.

2.Is there a picture or document describe the internal relationship between stm32-hal-lib,wiced,wiring…

Not that I know of. But the HAL is the hardware abstraction layer. The HAL isolates two different things: processor/hardware differences and network hardware differences.

The system firmware can be run on a lot of different hardware devices (Core, Photon, Electron), 3rd-party devices (Bluz, Oak, RedBear Duo), Raspberry Pi, and our new mesh devices.

The network HAL takes care of the difference between CC3000 (Core), WICED (Photon/P1/Duo), u-blox cellular (Electron/E Series), BLE (Bluz), and Thread Mesh (new mesh devices).

Wiring is just a set of functions (like digitalRead) and a preprocessor that converts .ino files to plain C++ by adding a default include and automatic generation of forward declarations. Wiring functions are linked into the user firmware binary at build time.

3.I am trying to find out the protocol layer of system firmware but I get more confused,I think it should be wiced(wifi driver)-lwip(TCP/IP)-mbedtls(TLS)-CoAP(APP layter).

WICED uses LWIP for its TCP stack in the current version, but I think it used something else in the past.

Cellular devices don’t use WICED or LWIP; the u-blox modem implements TCP, UDP, and DNS in the modem firmware.

The network HAL abstracts this and provides TCP, UDP, etc. in a hardware independent way so the upper layers don’t have to care whether it’s cellular or WICED or something else (like the Spark Core CC3000, the Digistump Oak ESP8266), or Unix sockets (Raspberry Pi).

There are two classes of devices currently. TCP devices (Wi-Fi devices and the Raspberry Pi) that use TCP to communicate with the Particle cloud and UDP devices (cellular). Both, however use CoAP for the Particle cloud connection, and the CoAP protocol works pretty much the same whether it’s running over TCP or UDP.

In between the CoAP layer and TCP or UDP, however, is the encryption layer.

On Wi-Fi devices, this is mutual RSA public/private key exchange and AES encryption with a session key.

On cellular devices, this is mutual RSA public/private key exchange with the DTLS protocol (datagram TLS) over UDP. This is implemented by mbedTLS on the Electron/E Series.


I find some arrays contain function pointer, I think It is the way to export functions from static libraries, after link the function address will be fixed,Am I right? But I can not find how those arrays be used.

for example ./hal/inc/hal_dynalib_i2c.h


// first edition of these functions that were released on the Photon/P1
// They are not needed on other platforms.
DYNALIB_FN(0, hal_i2c, HAL_I2C_Set_Speed_v1, void(uint32_t))
DYNALIB_FN(1, hal_i2c, HAL_I2C_Enable_DMA_Mode_v1, void(bool))
DYNALIB_FN(2, hal_i2c, HAL_I2C_Stretch_Clock_v1, void(bool))
DYNALIB_FN(3, hal_i2c, HAL_I2C_Begin_v1, void(I2C_Mode, uint8_t))
DYNALIB_FN(4, hal_i2c, HAL_I2C_End_v1, void(void))
DYNALIB_FN(5, hal_i2c, HAL_I2C_Request_Data_v1, uint32_t(uint8_t, uint8_t, uint8_t))
DYNALIB_FN(6, hal_i2c, HAL_I2C_Begin_Transmission_v1, void(uint8_t))
DYNALIB_FN(7, hal_i2c, HAL_I2C_End_Transmission_v1, uint8_t(uint8_t))
DYNALIB_FN(8, hal_i2c, HAL_I2C_Write_Data_v1, uint32_t(uint8_t))
DYNALIB_FN(9, hal_i2c, HAL_I2C_Available_Data_v1, int32_t(void))
DYNALIB_FN(10, hal_i2c, HAL_I2C_Read_Data_v1, int32_t(void))
DYNALIB_FN(11, hal_i2c, HAL_I2C_Peek_Data_v1, int32_t(void))
DYNALIB_FN(12, hal_i2c, HAL_I2C_Flush_Data_v1, void(void))
DYNALIB_FN(13, hal_i2c, HAL_I2C_Is_Enabled_v1, bool(void))
DYNALIB_FN(14, hal_i2c, HAL_I2C_Set_Callback_On_Receive_v1, void(void(*)(int)))
DYNALIB_FN(15, hal_i2c, HAL_I2C_Set_Callback_On_Request_v1, void(void(*)(void)))
#define BASE_IDX 16 // Base index for all subsequent functions
#define BASE_IDX 0

DYNALIB_FN(BASE_IDX + 0, hal_i2c, HAL_I2C_Set_Speed, void(HAL_I2C_Interface, uint32_t, void*))
DYNALIB_FN(BASE_IDX + 1, hal_i2c, HAL_I2C_Enable_DMA_Mode, void(HAL_I2C_Interface, bool, void*))
DYNALIB_FN(BASE_IDX + 2, hal_i2c, HAL_I2C_Stretch_Clock, void(HAL_I2C_Interface, bool, void*))
DYNALIB_FN(BASE_IDX + 3, hal_i2c, HAL_I2C_Begin, void(HAL_I2C_Interface, I2C_Mode, uint8_t, void*))
DYNALIB_FN(BASE_IDX + 4, hal_i2c, HAL_I2C_End, void(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 5, hal_i2c, HAL_I2C_Request_Data, uint32_t(HAL_I2C_Interface, uint8_t, uint8_t, uint8_t, void*))
DYNALIB_FN(BASE_IDX + 6, hal_i2c, HAL_I2C_Begin_Transmission, void(HAL_I2C_Interface, uint8_t, void*))
DYNALIB_FN(BASE_IDX + 7, hal_i2c, HAL_I2C_End_Transmission, uint8_t(HAL_I2C_Interface, uint8_t, void*))
DYNALIB_FN(BASE_IDX + 8, hal_i2c, HAL_I2C_Write_Data, uint32_t(HAL_I2C_Interface, uint8_t, void*))
DYNALIB_FN(BASE_IDX + 9, hal_i2c, HAL_I2C_Available_Data, int32_t(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 10, hal_i2c, HAL_I2C_Read_Data, int32_t(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 11, hal_i2c, HAL_I2C_Peek_Data, int32_t(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 12, hal_i2c, HAL_I2C_Flush_Data, void(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 13, hal_i2c, HAL_I2C_Is_Enabled, bool(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 14, hal_i2c, HAL_I2C_Set_Callback_On_Receive, void(HAL_I2C_Interface, void(*)(int), void*))
DYNALIB_FN(BASE_IDX + 15, hal_i2c, HAL_I2C_Set_Callback_On_Request, void(HAL_I2C_Interface, void(*)(void), void*))
DYNALIB_FN(BASE_IDX + 16, hal_i2c, HAL_I2C_Init, void(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 17, hal_i2c, HAL_I2C_Reset, uint8_t(HAL_I2C_Interface, uint32_t, void*))
DYNALIB_FN(BASE_IDX + 18, hal_i2c, HAL_I2C_Acquire, int32_t(HAL_I2C_Interface, void*))
DYNALIB_FN(BASE_IDX + 19, hal_i2c, HAL_I2C_Release, int32_t(HAL_I2C_Interface, void*))


It works essentially the same way Microsoft Windows COM exports functions.

Only the functions that are in the table, such as HAL_I2C_Set_Speed_v1, can be called from another module. Also note that the table entries immutable, so once we’ve released a version with a specific function signature that won’t change. That’s why those are all labeled v1; if we ever have to add a parameter, for example, the new signature will be v2. New functions are added at the end.

The reason is that user firmware binaries are designed to work against the same or newer system versions. For example, the Photon tinker binary works with system firmware 0.4.2 or later. This is only possible when the exports are immutable.

gun ld maintain this feature?Or other tools?

Very smart design!
I did not read through the MACRO, Now I got it!
DYNALIB_FN can be performed as two macro by switch EXPORT or IMPORT

     * Begin the jump table definition
    #define DYNALIB_BEGIN(tablename) \
        DYNALIB_EXTERN_C const void* const dynalib_##tablename[] = {

    #define DYNALIB_FN(index, tablename, name, type) \
        DYNALIB_FN_EXPORT(index, tablename, name, type)

    #define DYNALIB_FN_PLACEHOLDER(index, tablename) \

    #define DYNALIB_END(name)   \
        0 };

#elif defined(DYNALIB_IMPORT)

    #ifdef __arm__

        #define DYNALIB_BEGIN(tablename)    \
            EXTERN_C const void* dynalib_location_##tablename;

        #define __S(x) #x
        #define __SX(x) __S(x)

        #define DYNALIB_FN_IMPORT(index, tablename, name, counter) \
            DYNALIB_STATIC_ASSERT(index == counter, "Index of the dynamically exported function has changed"); \
            const char check_name_##tablename_##name[0]={}; /* this will fail if the name is already defined */ \
            void name() __attribute__((naked)); \
            void name() { \
                asm volatile ( \
                    ".equ offset, ( " __SX(counter) " * 4)\n" \
                    ".extern dynalib_location_" #tablename "\n" \
                    "push {r3, lr}\n"           /* save register we will change plus storage for sp value */ \
                                                /* pushes highest register first, so lowest register is at lowest memory address */ \
                                                /* SP points to the last pushed item, which is r3. sp+4 is then the pushed lr value */ \
                    "ldr r3, =dynalib_location_" #tablename "\n" \
                    "ldr r3, [r3]\n"                    /* the address of the jump table */ \
                    "ldr r3, [r3, #offset]\n"    /* the address at index __COUNTER__ */ \
                    "str r3, [sp, #4]\n"                /* patch the link address on the stack */ \
                    "pop {r3, pc}\n"                    /* restore register and jump to function */ \
                ); \

        #define DYNALIB_FN(index, tablename, name, type) \
            DYNALIB_FN_IMPORT(index, tablename, name, __COUNTER__)

        #define DYNALIB_FN_PLACEHOLDER(index, tablename) \
            DYNALIB_STATIC_ASSERT(index == __COUNTER__, "Index of the dynamically exported function has changed"); // Ensures that __COUNTER__ is incremented

        #define DYNALIB_END(name)
        #error Unknown architecture
    #endif // __arm__