diff --git a/.gitignore b/.gitignore index 89a62f0..519bb5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ build/* test/build -.vscode/* \ No newline at end of file +.vscode/* +CMakeCache.txt +.cache +CMakeFiles/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 29868cd..9650198 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5.0) +cmake_minimum_required(VERSION 3.12) project(MDFU_Tools) @@ -36,5 +36,23 @@ include_directories("${KERNEL_HEADERS_DIR}/include") set_property(GLOBAL PROPERTY C_STANDARD 23) set_property(GLOBAL PROPERTY C_EXTENSIONS True) +option(WITH_TOOL_I2C "Build with I2C tools" ON) +option(WITH_TOOL_SPI "Build with SPI tools" ON) +option(WITH_TOOL_NETWORK "Build with Network tools" ON) +option(WITH_TOOL_SERIAL "Build with Serial tools" ON) + +if (WITH_TOOL_I2C) + add_compile_definitions(USE_TOOL_I2C) +endif() +if (WITH_TOOL_SPI) + add_compile_definitions(USE_TOOL_SPI) +endif() +if (WITH_TOOL_NETWORK) + add_compile_definitions(USE_TOOL_NETWORK) +endif() +if (WITH_TOOL_SERIAL) + add_compile_definitions(USE_TOOL_SERIAL) +endif() + add_subdirectory(apps) add_subdirectory(src) \ No newline at end of file diff --git a/apps/cmdfu/cli_parser.c b/apps/cmdfu/cli_parser.c index 948c2b3..b9ea8f0 100644 --- a/apps/cmdfu/cli_parser.c +++ b/apps/cmdfu/cli_parser.c @@ -68,9 +68,7 @@ static void print_options(const char *message, char **argv){ char buf[size]; char *pbuf = buf; for (char **ptr = argv; *ptr != NULL; ptr++){ - pbuf = stpcpy(pbuf, *ptr); - *pbuf = ' '; - pbuf++; + pbuf += sprintf(pbuf, "%s ", *ptr); } *(pbuf - 1) = '\0'; // Replace the last space with a null terminator DEBUG("%s %s", message, buf); diff --git a/apps/cmdfu/cmdfu.h b/apps/cmdfu/cmdfu.h index 4fbf8fc..2743822 100644 --- a/apps/cmdfu/cmdfu.h +++ b/apps/cmdfu/cmdfu.h @@ -1,6 +1,7 @@ #ifndef CMDFU_H #define CMDFU_H +#include #include "mdfu/tools/tools.h" /** diff --git a/include/mdfu/endian.h b/include/mdfu/endian.h new file mode 100644 index 0000000..ab95434 --- /dev/null +++ b/include/mdfu/endian.h @@ -0,0 +1,170 @@ +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. +// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to +// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it +// an example on how to get the endian conversion functions on different platforms. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include +# ifdef __GNUC__ +# include +# endif + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__QNXNTO__) + +# include + +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 + +# if defined(__BIGENDIAN__) + +# define __BYTE_ORDER __BIG_ENDIAN + +# define htobe16(x) (x) +# define htobe32(x) (x) +# define htobe64(x) (x) + +# define htole16(x) ENDIAN_SWAP16(x) +# define htole32(x) ENDIAN_SWAP32(x) +# define htole64(x) ENDIAN_SWAP64(x) + +# elif defined(__LITTLEENDIAN__) + +# define __BYTE_ORDER __LITTLE_ENDIAN + +# define htole16(x) (x) +# define htole32(x) (x) +# define htole64(x) (x) + +# define htobe16(x) ENDIAN_SWAP16(x) +# define htobe32(x) ENDIAN_SWAP32(x) +# define htobe64(x) ENDIAN_SWAP64(x) + +# else + +# error byte order not supported + +# endif + +# define be16toh(x) ENDIAN_BE16(x) +# define be32toh(x) ENDIAN_BE32(x) +# define be64toh(x) ENDIAN_BE64(x) +# define le16toh(x) ENDIAN_LE16(x) +# define le32toh(x) ENDIAN_LE32(x) +# define le64toh(x) ENDIAN_LE64(x) + +#else + +# error platform not supported + +#endif + +#endif diff --git a/include/mdfu/tools/tools.h b/include/mdfu/tools/tools.h index 6b63e86..098728c 100644 --- a/include/mdfu/tools/tools.h +++ b/include/mdfu/tools/tools.h @@ -12,11 +12,19 @@ typedef struct { }tool_t; typedef enum tool_type { - TOOL_SERIAL = 0, - TOOL_NETWORK = 1, - TOOL_SPIDEV = 2, - TOOL_I2CDEV = 3, - TOOL_NONE = 4 +#ifdef USE_TOOL_SERIAL + TOOL_SERIAL, +#endif +#ifdef USE_TOOL_NETWORK + TOOL_NETWORK, +#endif +#ifdef USE_TOOL_SPI + TOOL_SPIDEV, +#endif +#ifdef USE_TOOL_I2C + TOOL_I2CDEV, +#endif + TOOL_NONE }tool_type_t; extern const char *tool_names[]; diff --git a/readme.md b/readme.md index d2fe26e..b8b245d 100644 --- a/readme.md +++ b/readme.md @@ -11,12 +11,21 @@ cmake -B build - MDFU_MAX_COMMAND_DATA_LENGTH: Defines the maximum MDFU command data length that is supported. This must be at least the same size as the MDFU client reported size. - MDFU_MAX_RESPONSE_DATA_LENGTH: Defines the maximumd MDFU response data length that is supported. +- WITH_TOOL_I2C: Include I2C target device (Linux-only, not supported for Windows targets), default ON. +- WITH_TOOL_SPI: Include SPI target device (Linux-only, not supported for Windows targets), default ON. +- WITH_TOOL_SERIAL: Include serial target device (supported on Linux & Windows targets), default ON. +- WITH_TOOL_NETWORK: Include network target device (Linux-only, not yet supported for Windows targets), default ON. Creating the build tree and configuring maximum MDFU command data size. ```bash cmake -B build -D MDFU_MAX_COMMAND_DATA_LENGTH=1024 ``` +Creating a build for Win32 serial devices only: +```bash +cmake -B build -D WITH_TOOL_I2C=OFF -D WITH_TOOL_SPI=OFF -D WITH_TOOL_NETWORK=OFF +``` + ## Building Building by invocing native build tools through cmake @@ -29,6 +38,11 @@ cd build make ``` +Build `cmdfu` utility only (needed for Windows build which doesn't yet support tests): +```bash +cmake --build build --target cmdfu +``` + ## Running the application from the build tree ```bash @@ -56,4 +70,4 @@ The source and debian package will be available in the ./build directory. Build and packaging in one step. ```bash cmake --build build --target package -``` \ No newline at end of file +``` diff --git a/src/mac/CMakeLists.txt b/src/mac/CMakeLists.txt index 077ded0..57345e9 100644 --- a/src/mac/CMakeLists.txt +++ b/src/mac/CMakeLists.txt @@ -1,11 +1,39 @@ +if (WITH_TOOL_NETWORK) + set(NETWORK_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/mac/socket_mac.h") + set(NETWORK_SOURCE "socket_mac.c" "socket_packet_mac.c") +endif() +if (WITH_TOOL_SERIAL) + set(SERIAL_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/mac/serial_mac.h") + if (WIN32) + set(SPI_SOURCE "serial_mac_win.c") + else() + set(SERIAL_SOURCE "serial_mac.c") + endif() +endif() +if (WITH_TOOL_SPI) + set(SPI_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/mac/spidev_mac.h") + set(SPI_SOURCE "spidev_mac.c") +endif() +if (WITH_TOOL_I2C) + set(I2C_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/mac/i2cdev_mac.h") + set(I2C_SOURCE "i2cdev_mac.c") +endif() + set(HEADER_LIST "${CMAKE_SOURCE_DIR}/include/mdfu/mac/mac.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/mac/socket_mac.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/mac/serial_mac.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/mac/spidev_mac.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/mac/i2cdev_mac.h" + ${NETWORK_HEADER} + ${SERIAL_HEADER} + ${SPI_HEADER} + ${I2C_HEADER} +) + +set(SOURCE_LIST + ${NETWORK_SOURCE} + ${SERIAL_SOURCE} + ${SPI_SOURCE} + ${I2C_SOURCE} ) -add_library(maclib socket_mac.c socket_packet_mac.c serial_mac.c i2cdev_mac.c spidev_mac.c ${HEADER_LIST}) +add_library(maclib ${SOURCE_LIST} ${HEADER_LIST}) target_include_directories(maclib PUBLIC "${CMAKE_SOURCE_DIR}/include") target_include_directories(maclib PUBLIC "${CMAKE_BINARY_DIR}/include") diff --git a/src/mac/serial_mac_win.c b/src/mac/serial_mac_win.c new file mode 100644 index 0000000..45c03aa --- /dev/null +++ b/src/mac/serial_mac_win.c @@ -0,0 +1,191 @@ +#include +#include +#include +#undef ERROR +#include "mdfu/logging.h" +#include "mdfu/mac/serial_mac.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT_NAME_MAX_SIZE 256 + +static bool opened = false; +static char port[PORT_NAME_MAX_SIZE + 1]; +static int baudrate = 0; +static HANDLE hSerial = NULL; +static COMMTIMEOUTS timeouts = {0}; +static DCB params = {0}; + +static int mac_init(void *conf) { + struct serial_config *config = (struct serial_config *)conf; + if (opened) { + ERROR("Cannot initialize while MAC is opened."); + errno = EBUSY; + return -1; + } + DEBUG("Initializing serial MAC"); + int port_name_size = strlen(config->port); + if (PORT_NAME_MAX_SIZE < port_name_size) { + ERROR("This driver only supports serial port name lenght of max 256 " + "characters"); + errno = EINVAL; + return -1; + } + sprintf(port, "\\\\.\\%s", config->port); + baudrate = config->baudrate; + return 0; +} + +static int mac_open(void) { + DEBUG("Opening serial MAC"); + if (opened) { + errno = EBUSY; + return -1; + } + + hSerial = CreateFile(port, // port name + GENERIC_READ | GENERIC_WRITE, // Read/Write + 0, // No Sharing + NULL, // No Security + OPEN_EXISTING, // Open existing port only + 0, // Non Overlapped I/O + NULL); // Null for Comm Devices + + if (hSerial == INVALID_HANDLE_VALUE) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + // Ask Win32 to give us the string version of the error code. + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC CreateFile: 0x%08X %s %s", (uint32_t)errorId, + messageBuffer, port); + return -1; + } + + // Configure read and write operations to time out after 100 ms. + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutConstant = 100; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 100; + timeouts.WriteTotalTimeoutMultiplier = 0; + + if (!SetCommTimeouts(hSerial, &timeouts)) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC SetCommTimeouts: 0x%08X %s", (uint32_t)errorId, + messageBuffer); + CloseHandle(hSerial); + return -1; + } + + // Set the baud rate and other options. + params.DCBlength = sizeof(params); + if (!GetCommState(hSerial, ¶ms)) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC GetCommState: 0x%08X %s", (uint32_t)errorId, + messageBuffer); + CloseHandle(hSerial); + return -1; + } + params.BaudRate = baudrate; // Set Baud Rate + params.ByteSize = 8; // Data Size = 8 bits + params.StopBits = ONESTOPBIT; // One Stop Bit + params.Parity = NOPARITY; // No Parity + params.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR + params.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS + params.fOutxCtsFlow = FALSE; // Disable CTS output flow control + params.fOutxDsrFlow = FALSE; // Disable DSR output flow control + params.fDsrSensitivity = FALSE; // Disable DSR sensitivity + params.fOutX = FALSE; // Disable XON/XOFF output flow control + params.fInX = FALSE; // Disable XON/XOFF input flow control + params.fErrorChar = FALSE; // Disable error replacement + params.fNull = FALSE; // Disable null stripping + params.fBinary = TRUE; // Enable binary mode + params.fAbortOnError = FALSE; // Do not abort on error + params.fTXContinueOnXoff = + TRUE; // Continue transmitting when XOFF is received + + if (!SetCommState(hSerial, ¶ms)) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC SetCommState: 0x%08X %s", (uint32_t)errorId, + messageBuffer); + CloseHandle(hSerial); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, port, PORT_NAME_MAX_SIZE, NULL); + return -1; + } + + opened = true; + return 0; +} + +static int mac_close(void) { + DEBUG("Closing serial MAC"); + if (opened) { + CloseHandle(hSerial); + opened = false; + return 0; + } else { + errno = EBADF; + return -1; + } +} + +static int mac_read(int size, uint8_t *data) { + DWORD received; + if (!ReadFile(hSerial, data, size, &received, NULL)) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC read: 0x%08X %s", (uint32_t)errorId, messageBuffer); + return -1; + } + return (int)received; +} + +static int mac_write(int size, uint8_t *data) { + DWORD written; + if (!WriteFile(hSerial, data, size, &written, NULL)) { + LPSTR messageBuffer = NULL; + DWORD errorId = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + ERROR("Serial MAC write: 0x%08X %s", (uint32_t)errorId, messageBuffer); + } + return (int)written; +} + +mac_t serial_mac = {.open = mac_open, + .close = mac_close, + .init = mac_init, + .write = mac_write, + .read = mac_read}; + +void get_serial_mac(mac_t **mac) { *mac = &serial_mac; } diff --git a/src/mdfu/mdfu.c b/src/mdfu/mdfu.c index 6985a54..63c4c4c 100644 --- a/src/mdfu/mdfu.c +++ b/src/mdfu/mdfu.c @@ -12,6 +12,9 @@ #include #else #include "mdfu/endian.h" + #ifdef _WIN32 + #undef ERROR + #endif #endif #endif #include diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 8a81259..5d6e334 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,10 +1,35 @@ +if (WITH_TOOL_NETWORK) + set(NETWORK_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/tools/network.h") + set(NETWORK_SOURCE "network_tool.c") +endif() +if (WITH_TOOL_SERIAL) + set(SERIAL_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/tools/serial.h") + set(SERIAL_SOURCE "serial_tool.c") +endif() +if (WITH_TOOL_SPI) + set(SPI_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/tools/spidev.h") + set(SPI_SOURCE "spidev_tool.c") +endif() +if (WITH_TOOL_I2C) + set(I2C_HEADER "${CMAKE_SOURCE_DIR}/include/mdfu/tools/i2cdev.h") + set(I2C_SOURCE "i2cdev_tool.c") +endif() + set(HEADER_LIST "${CMAKE_SOURCE_DIR}/include/mdfu/tools/tools.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/tools/network.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/tools/serial.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/tools/spidev.h" - "${CMAKE_SOURCE_DIR}/include/mdfu/tools/i2cdev.h" + ${NETWORK_HEADER} + ${SERIAL_HEADER} + ${SPI_HEADER} + ${I2C_HEADER} +) + +set(SOURCE_LIST + "tools.c" + ${NETWORK_SOURCE} + ${SERIAL_SOURCE} + ${SPI_SOURCE} + ${I2C_SOURCE} ) -add_library(toolslib tools.c network_tool.c serial_tool.c spidev_tool.c i2cdev_tool.c ${HEADER_LIST}) +add_library(toolslib ${SOURCE_LIST} ${HEADER_LIST}) target_include_directories(toolslib PUBLIC "${CMAKE_SOURCE_DIR}/include") diff --git a/src/tools/tools.c b/src/tools/tools.c index 92ef511..e4bc6f2 100644 --- a/src/tools/tools.c +++ b/src/tools/tools.c @@ -16,7 +16,20 @@ * @note The order of the names in this array must match the order of the tools * in the `tools` array and in the tool_type_t enum. */ -const char *tool_names[] = {"serial", "network", "spidev", "i2cdev", NULL}; +const char *tool_names[] = { +#ifdef USE_TOOL_SERIAL + "serial", +#endif +#ifdef USE_TOOL_NETWORK + "network", +#endif +#ifdef USE_TOOL_SPI + "spidev", +#endif +#ifdef USE_TOOL_I2C + "i2cdev", +#endif + NULL}; /** * @brief Array of tool pointers. @@ -28,7 +41,20 @@ const char *tool_names[] = {"serial", "network", "spidev", "i2cdev", NULL}; * @note The order of the tools in this array should match the order of the names * in the `tool_names` array and in the tool_type_t enum. */ -static tool_t *tools[] = {&serial_tool, &network_tool, &spidev_tool, &i2cdev_tool, NULL}; +static tool_t *tools[] = { +#ifdef USE_TOOL_SERIAL + &serial_tool, +#endif +#ifdef USE_TOOL_NETWORK + &network_tool, +#endif +#ifdef USE_TOOL_SPI + &spidev_tool, +#endif +#ifdef USE_TOOL_I2C + &i2cdev_tool, +#endif + NULL}; /** * @brief Retrieves the name of a tool based on its type.