diff --git a/cmake/iMX8MM.cmake b/cmake/iMX8MM.cmake new file mode 100644 index 00000000..d7922144 --- /dev/null +++ b/cmake/iMX8MM.cmake @@ -0,0 +1,62 @@ +#******************************************************************** +# _ _ _ +# _ __ | |_ _ | | __ _ | |__ ___ +# | '__|| __|(_)| | / _` || '_ \ / __| +# | | | |_ _ | || (_| || |_) |\__ \ +# |_| \__|(_)|_| \__,_||_.__/ |___/ +# +# www.rt-labs.com +# Copyright 2021 rt-labs AB, Sweden. +# Copyright 2023 NXP +# +# This software is dual-licensed under GPLv3 and a commercial +# license. See the file LICENSE.md distributed with this software for +# full license information. +#*******************************************************************/ + +target_include_directories(profinet + PRIVATE + src/ports/iMX8M + ) + +target_sources(profinet + PRIVATE + src/ports/iMX8M/pnal.c + src/ports/iMX8M/pnal_eth.c + src/ports/iMX8M/pnal_udp.c + ) + +target_include_directories(pn_dev + PRIVATE + samples/pn_dev + src/ports/iMX8M + ) + +target_sources(pn_dev + PRIVATE + samples/pn_dev/sampleapp_common.c + samples/pn_dev/app_utils.c + samples/pn_dev/app_log.c + samples/pn_dev/app_gsdml.c + samples/pn_dev/app_data.c + src/ports/iMX8M/sampleapp_main.c + src/ports/iMX8M/sampleapp_imx8mmevk.c + ) + +target_link_libraries(pn_dev PRIVATE mcuxsdk-bsp) + +install (FILES + src/ports/iMX8M/pnal_config.h + DESTINATION include + ) + +generate_bin(pn_dev) + +if (BUILD_TESTING) + target_include_directories(pf_test + PRIVATE + src/ports/iMX8M + ) + target_link_libraries(pf_test PRIVATE mcuxsdk-bsp) + generate_bin(pf_test) +endif() diff --git a/src/ports/iMX8M/pnal.c b/src/ports/iMX8M/pnal.c new file mode 100644 index 00000000..cf1efaad --- /dev/null +++ b/src/ports/iMX8M/pnal.c @@ -0,0 +1,185 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * Copyright 2023 NXP + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +#include "pnal.h" + +#include "options.h" +#include "osal.h" +#include "osal_log.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +int pnal_set_ip_suite ( + const char * interface_name, + const pnal_ipaddr_t * p_ipaddr, + const pnal_ipaddr_t * p_netmask, + const pnal_ipaddr_t * p_gw, + const char * hostname, + bool permanent) +{ + ip_addr_t ip_addr; + ip_addr_t ip_mask; + ip_addr_t ip_gw; + + ip_addr.addr = htonl (*p_ipaddr); + ip_mask.addr = htonl (*p_netmask); + ip_gw.addr = htonl (*p_gw); + + LOCK_TCPIP_CORE(); + netif_set_addr (netif_default, &ip_addr, &ip_mask, &ip_gw); + UNLOCK_TCPIP_CORE(); + + return 0; +} + +int pnal_get_macaddress (const char * interface_name, pnal_ethaddr_t * mac_addr) +{ + memcpy (mac_addr, netif_default->hwaddr, sizeof (pnal_ethaddr_t)); + return 0; +} + +pnal_ipaddr_t pnal_get_ip_address (const char * interface_name) +{ + return htonl (netif_default->ip_addr.addr); +} + +pnal_ipaddr_t pnal_get_netmask (const char * interface_name) +{ + return htonl (netif_default->netmask.addr); +} + +pnal_ipaddr_t pnal_get_gateway (const char * interface_name) +{ + /* TODO Read the actual default gateway */ + + pnal_ipaddr_t ip; + pnal_ipaddr_t gateway; + + ip = pnal_get_ip_address (interface_name); + gateway = (ip & 0xFFFFFF00) | 0x00000001; + + return gateway; +} + +int pnal_get_hostname (char * hostname) +{ + strcpy (hostname, netif_default->hostname); + return 0; +} + +int pnal_get_ip_suite ( + const char * interface_name, + pnal_ipaddr_t * p_ipaddr, + pnal_ipaddr_t * p_netmask, + pnal_ipaddr_t * p_gw, + char * hostname) +{ + int ret = -1; + + *p_ipaddr = pnal_get_ip_address (interface_name); + *p_netmask = pnal_get_netmask (interface_name); + *p_gw = pnal_get_gateway (interface_name); + ret = pnal_get_hostname (hostname); + + return ret; +} + +int pnal_get_port_statistics ( + const char * interface_name, + pnal_port_stats_t * port_stats) +{ + port_stats->if_in_octets = netif_default->mib2_counters.ifinoctets; + port_stats->if_in_errors = netif_default->mib2_counters.ifinerrors; + port_stats->if_in_discards = netif_default->mib2_counters.ifindiscards; + port_stats->if_out_octets = netif_default->mib2_counters.ifoutoctets; + port_stats->if_out_errors = netif_default->mib2_counters.ifouterrors; + port_stats->if_out_discards = netif_default->mib2_counters.ifoutdiscards; + + return 0; +} + +int pnal_get_interface_index (const char * interface_name) +{ + return 0; +} + +int pnal_eth_get_status (const char * interface_name, pnal_eth_status_t * status) +{ + status->is_autonegotiation_supported = false; + status->is_autonegotiation_enabled = false; + status->autonegotiation_advertised_capabilities = 0; + + status->operational_mau_type = PNAL_ETH_MAU_COPPER_100BaseTX_FULL_DUPLEX; + status->running = true; + + return 0; +} + +int pnal_save_file ( + const char * fullpath, + const void * object_1, + size_t size_1, + const void * object_2, + size_t size_2) +{ + return -1; +} + +void pnal_clear_file (const char * fullpath) +{ + +} + +int pnal_load_file ( + const char * fullpath, + void * object_1, + size_t size_1, + void * object_2, + size_t size_2) +{ + return -1; +} + +uint32_t pnal_get_system_uptime_10ms (void) +{ + uint32_t uptime = 0; + + MIB2_COPY_SYSUPTIME_TO (&uptime); + return uptime; +} + +pnal_buf_t * pnal_buf_alloc (uint16_t length) +{ + return pbuf_alloc (PBUF_RAW, length, PBUF_POOL); +} + +void pnal_buf_free (pnal_buf_t * p) +{ + CC_ASSERT (pbuf_free (p) == 1); +} + +uint8_t pnal_buf_header (pnal_buf_t * p, int16_t header_size_increment) +{ + return pbuf_header (p, header_size_increment); +} \ No newline at end of file diff --git a/src/ports/iMX8M/pnal_config.h b/src/ports/iMX8M/pnal_config.h new file mode 100644 index 00000000..3eed49ef --- /dev/null +++ b/src/ports/iMX8M/pnal_config.h @@ -0,0 +1,49 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +/** + * @file + * @brief PNAL-specific configuration + * + * This file contains definitions of configuration settings for the + * PNAL layer. + */ + +#ifndef PNAL_CONFIG_H +#define PNAL_CONFIG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct pnal_thread_cfg +{ + uint32_t prio; + size_t stack_size; +} pnal_thread_cfg_t; + +typedef struct pnal_cfg +{ + pnal_thread_cfg_t bg_worker_thread; +} pnal_cfg_t; + +#ifdef __cplusplus +} +#endif + +#endif /* PNAL_CONFIG_H */ diff --git a/src/ports/iMX8M/pnal_eth.c b/src/ports/iMX8M/pnal_eth.c new file mode 100644 index 00000000..7feaf44c --- /dev/null +++ b/src/ports/iMX8M/pnal_eth.c @@ -0,0 +1,191 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * Copyright 2023 NXP + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +/** + * @file + * @brief i.MX8M Ethernet related functions that use \a pnal_eth_handle_t + */ + +#include "pnal.h" +#include "osal_log.h" + +#include +#include + +#define MAX_NUMBER_OF_IF 1 + +struct pnal_eth_handle +{ + struct netif * netif; + pnal_eth_callback_t * eth_rx_callback; + void * arg; +}; + +static pnal_eth_handle_t interface[MAX_NUMBER_OF_IF]; +static int nic_index = 0; + +/** + * Find PNAL network interface handle + * + * @param netif In: lwip network interface. + * @return PNAL network interface handle corresponding to \a netif, + * NULL otherwise. + */ +static pnal_eth_handle_t * pnal_eth_find_handle (struct netif * netif) +{ + pnal_eth_handle_t * handle; + int i; + + for (i = 0; i < MAX_NUMBER_OF_IF; i++) + { + handle = &interface[i]; + if (handle->netif == netif) + { + return handle; + } + } + + return NULL; +} + +/** + * Allocate PNAL network interface handle + * + * Handles are allocated from a static array and need never be freed. + * + * @return PNAL network interface handle if available, + * NULL if too many handles were allocated. + */ +static pnal_eth_handle_t * pnal_eth_allocate_handle (void) +{ + pnal_eth_handle_t * handle; + + if (nic_index < MAX_NUMBER_OF_IF) + { + handle = &interface[nic_index]; + nic_index++; + return handle; + } + else + { + return NULL; + } +} + +/** + * Process received Ethernet frame + * + * Called from lwip when an Ethernet frame is received with an EtherType + * lwip is not aware of (e.g. Profinet and LLDP). + * + * @param p_buf InOut: Packet buffer containing Ethernet frame. + * @param netif InOut: Network interface receiving the frame. + * @return ERR_OK if frame was processed and freed, + * ERR_IF if it was ignored. + */ +static err_t pnal_eth_sys_recv (struct pbuf * p_buf, struct netif * netif) +{ + int processed; + pnal_eth_handle_t * handle; + + handle = pnal_eth_find_handle (netif); + if (handle == NULL) + { + /* p-net not started yet, let lwIP handle frame */ + return ERR_IF; + } + + processed = handle->eth_rx_callback (handle, handle->arg, p_buf); + //uint8_t *data = (uint8_t*)p_buf->payload; + + /*printf("hook: "); + for(int i = 0; i < 50; i++) + printf("%02x ", data[i]); + printf("\n");*/ + + if (processed) + { + //printf("Frame processed\n"); + /* Frame handled and freed */ + return ERR_OK; + } + else + { + //printf("Frame not processed\n"); + /* Frame not handled */ + return ERR_IF; + } +} + +err_enum_t lwip_hook_unknown_eth_protocol ( + struct pbuf * pbuf, + struct netif * netif) +{ + return pnal_eth_sys_recv (pbuf, netif); +} + +pnal_eth_handle_t * pnal_eth_init ( + const char * if_name, + pnal_ethertype_t receive_type, + const pnal_cfg_t * pnal_cfg, + pnal_eth_callback_t * callback, + void * arg) +{ + pnal_eth_handle_t * handle; + struct netif * netif; + + (void)receive_type; /* Ignore, for now all frames will be received. */ + + LOCK_TCPIP_CORE(); + + netif = netif_find (if_name); + if (netif == NULL) + { + os_log (LOG_LEVEL_ERROR, "Network interface \"%s\" not found!\n", if_name); + return NULL; + } + + UNLOCK_TCPIP_CORE(); + + handle = pnal_eth_allocate_handle(); + if (handle == NULL) + { + os_log (LOG_LEVEL_ERROR, "Too many network interfaces\n"); + return NULL; + } + + handle->arg = arg; + handle->eth_rx_callback = callback; + handle->netif = netif; + + return handle; +} + +int pnal_eth_send (pnal_eth_handle_t * handle, pnal_buf_t * buf) +{ + int ret = -1; + + CC_ASSERT (handle->netif->linkoutput != NULL); + + if (buf != NULL) + { + buf->tot_len = buf->len; + + handle->netif->linkoutput (handle->netif, buf); + ret = buf->len; + } + return ret; +} diff --git a/src/ports/iMX8M/pnal_sys.h b/src/ports/iMX8M/pnal_sys.h new file mode 100644 index 00000000..ad9bfc4f --- /dev/null +++ b/src/ports/iMX8M/pnal_sys.h @@ -0,0 +1,35 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +#ifndef PNAL_SYS_H +#define PNAL_SYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* For htonl etc */ +#include + +#define PNAL_BUF_MAX_SIZE PBUF_POOL_BUFSIZE + +/* Re-use lwIP pbuf for rt-kernel */ +typedef struct pbuf pnal_buf_t; + +#ifdef __cplusplus +} +#endif + +#endif /* PNAL_SYS_H */ diff --git a/src/ports/iMX8M/pnal_udp.c b/src/ports/iMX8M/pnal_udp.c new file mode 100644 index 00000000..0d0cd6ce --- /dev/null +++ b/src/ports/iMX8M/pnal_udp.c @@ -0,0 +1,110 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +#include "pnal.h" +#include "osal_log.h" + +#include +#include + +int pnal_udp_open (pnal_ipaddr_t addr, pnal_ipport_t port) +{ + struct sockaddr_in local; + int id; + const int enable = 1; + + id = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (id == -1) + { + return -1; + } + + if (setsockopt (id, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (enable)) != 0) + { + goto error; + } + + /* set IP and port number */ + local = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (addr), + .sin_port = htons (port), + }; + + if (bind (id, (struct sockaddr *)&local, sizeof (local)) != 0) + { + goto error; + } + + return id; + +error: + close (id); + return -1; +} + +int pnal_udp_sendto ( + uint32_t id, + pnal_ipaddr_t dst_addr, + pnal_ipport_t dst_port, + const uint8_t * data, + int size) +{ + struct sockaddr_in remote; + int len; + + remote = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (dst_addr), + .sin_port = htons (dst_port), + }; + len = + sendto (id, data, size, 0, (struct sockaddr *)&remote, sizeof (remote)); + + return len; +} + +int pnal_udp_recvfrom ( + uint32_t id, + pnal_ipaddr_t * src_addr, + pnal_ipport_t * src_port, + uint8_t * data, + int size) +{ + struct sockaddr_in remote; + socklen_t addr_len = sizeof (remote); + int len; + + memset (&remote, 0, sizeof (remote)); + len = recvfrom ( + id, + data, + size, + MSG_DONTWAIT, + (struct sockaddr *)&remote, + &addr_len); + if (len > 0) + { + *src_addr = ntohl (remote.sin_addr.s_addr); + *src_port = ntohs (remote.sin_port); + } + + return len; +} + +void pnal_udp_close (uint32_t id) +{ + close (id); +} diff --git a/src/ports/iMX8M/sampleapp_imx8mmevk.c b/src/ports/iMX8M/sampleapp_imx8mmevk.c new file mode 100644 index 00000000..04003214 --- /dev/null +++ b/src/ports/iMX8M/sampleapp_imx8mmevk.c @@ -0,0 +1,42 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * Copyright 2023 NXP + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +#include "sampleapp_common.h" +#include "fsl_gpio.h" + +/************************* Utilities ******************************************/ + +void app_set_led (uint16_t pin, bool led_state) +{ + if (pin == APP_DATA_LED_ID) + GPIO_WritePinOutput(GPIO5, 13, led_state ? true : false); + else if (pin == APP_PROFINET_SIGNAL_LED_ID) + GPIO_WritePinOutput(GPIO5, 10, led_state ? true : false); +} + +bool app_get_button (uint16_t id) +{ + if (id == 0) + { + return (bool)GPIO_ReadPinInput(GPIO5, 12); + } + else if (id == 1) + { + // No more buttons on i.MX 8M Mini + } + return false; +} + diff --git a/src/ports/iMX8M/sampleapp_main.c b/src/ports/iMX8M/sampleapp_main.c new file mode 100644 index 00000000..ace9c60e --- /dev/null +++ b/src/ports/iMX8M/sampleapp_main.c @@ -0,0 +1,108 @@ +/********************************************************************* + * _ _ _ + * _ __ | |_ _ | | __ _ | |__ ___ + * | '__|| __|(_)| | / _` || '_ \ / __| + * | | | |_ _ | || (_| || |_) |\__ \ + * |_| \__|(_)|_| \__,_||_.__/ |___/ + * + * www.rt-labs.com + * Copyright 2021 rt-labs AB, Sweden. + * Copyright 2023 NXP + * + * This software is dual-licensed under GPLv3 and a commercial + * license. See the file LICENSE.md distributed with this software for + * full license information. + ********************************************************************/ + +#include "sampleapp_common.h" +#include "app_utils.h" +#include "app_log.h" +#include "app_gsdml.h" + +#include "osal_log.h" +#include "osal.h" +#include + +#define APP_DEFAULT_ETHERNET_INTERFACE "en0" +#define APP_DEFAULT_FILE_DIRECTORY "" +#define APP_LOG_LEVEL APP_LOG_LEVEL_INFO + +#define APP_BG_WORKER_THREAD_PRIORITY 2 +#define APP_BG_WORKER_THREAD_STACKSIZE 4096 /* bytes */ + +/********************************** Globals ***********************************/ + +static app_data_t * sample_app = NULL; +static pnet_cfg_t pnet_cfg = {0}; +app_args_t app_args = {0}; + +/****************************** Main ******************************************/ + +int _main (void) +{ + int ret; + app_utils_netif_namelist_t netif_name_list; + pnet_if_cfg_t netif_cfg = {0}; + uint16_t number_of_ports; + + strcpy (app_args.eth_interfaces, APP_DEFAULT_ETHERNET_INTERFACE); + strcpy (app_args.station_name, APP_GSDML_DEFAULT_STATION_NAME); + app_log_set_log_level (APP_LOG_LEVEL); + + APP_LOG_INFO ("\n** Starting P-Net sample application " PNET_VERSION + " **\n"); + APP_LOG_INFO ( + "Number of slots: %u (incl slot for DAP module)\n", + PNET_MAX_SLOTS); + APP_LOG_INFO ("P-net log level: %u (DEBUG=0, FATAL=4)\n", LOG_LEVEL); + APP_LOG_INFO ("App log level: %u (DEBUG=0, FATAL=4)\n", APP_LOG_LEVEL); + APP_LOG_INFO ("Max number of ports: %u\n", PNET_MAX_PHYSICAL_PORTS); + APP_LOG_INFO ("Network interfaces: %s\n", app_args.eth_interfaces); + APP_LOG_INFO ("Default station name: %s\n", app_args.station_name); + + app_pnet_cfg_init_default (&pnet_cfg); + + /* Note: station name is defined by app_gsdml.h */ + + strcpy (pnet_cfg.file_directory, APP_DEFAULT_FILE_DIRECTORY); + + ret = app_utils_pnet_cfg_init_netifs ( + app_args.eth_interfaces, + &netif_name_list, + &number_of_ports, + &netif_cfg); + if (ret != 0) + { + return -1; + } + + pnet_cfg.if_cfg = netif_cfg; + pnet_cfg.num_physical_ports = number_of_ports; + + app_utils_print_network_config (&netif_cfg, number_of_ports); + + pnet_cfg.pnal_cfg.bg_worker_thread.prio = APP_BG_WORKER_THREAD_PRIORITY; + pnet_cfg.pnal_cfg.bg_worker_thread.stack_size = + APP_BG_WORKER_THREAD_STACKSIZE; + + /* Initialize profinet stack */ + sample_app = app_init (&pnet_cfg, &app_args); + if (sample_app == NULL) + { + printf ("Failed to initialize P-Net.\n"); + printf ("Aborting application\n"); + return -1; + } + + /* Start main loop */ + if (app_start (sample_app, RUN_IN_MAIN_THREAD) != 0) + { + printf ("Failed to start\n"); + printf ("Aborting application\n"); + return -1; + } + + app_loop_forever (sample_app); + + return 0; +}