Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiport linux support #303

Merged
merged 1 commit into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions doc/multiple_ports.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Multiple ports
==============
This section describes how to configure the p-net stack, sample application
and system for multiple network interfaces or ports.
So far, multiple port has only been tested using the linux port.


Terminology
Expand Down Expand Up @@ -92,12 +93,40 @@ Run ``ifconfig`` to check that the bridge is up and its network interfaces are a
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

Configuration of p-net stack and sample application (TBD)
Configuration of p-net stack and sample application
---------------------------------------------------------
To run p-net and the sample application with multiple ports a couple
of things need to be done:

* Reconfigure setting ``PNET_MAX_PORT`` to the actual number of physical ports available in the system.
For this example ``PNET_MAX_PORT`` shall be set to 2.

* TBD - update this document when multi-port support is implemented
Reconfigure setting ``PNET_MAX_PORT`` to the actual number of physical ports available in the system.
For this example ``PNET_MAX_PORT`` shall be set to 2.

Example of initial log when starting the demo application with a multi port configuration::

pi@pndevice-pi:~/profinet/build $ sudo ./pn_dev -v
** Starting Profinet demo application **
Number of slots: 5 (incl slot for DAP module)
P-net log level: 0 (DEBUG=0, FATAL=4)
App verbosity level: 1
Nbr of ports: 2
Network interfaces: br0,eth0,eth1
Button1 file:
Button2 file:
Station name: rt-labs-dev
Management port: br0 C2:38:F3:A6:0A:66
Physical port [1]: eth0 B8:27:EB:67:14:8A
Physical port [2]: eth1 58:EF:68:B5:11:0F
Current hostname: pndevice-pi
Current IP address: 192.168.0.50
Current Netmask: 255.255.255.0
Current Gateway: 192.168.0.1
Storage directory: /home/pi/profinet/build

Update gsdml file
-----------------
The sample app gsdml file contains a commented out block that defines
a second physical port. In the sample application gsdml file, search for "IDS_P2"
and enable commented out lines as described in the gsdml file.

Note that you will have to the reload gsdml file in all tools you are using and
also the Automated RT tester any time the file is changed.
10 changes: 10 additions & 0 deletions sample_app/GSDML-V2.4-rtlabs-IODevice-20201211.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@
<MAUTypeItem Value="16"/>
</MAUTypeList>
</PortSubmoduleItem>
<!-- Enable to support additional port. (PNET_MAX_PORT == 2) -->
<!-- Add additional PortSubmoduleItems to support additional ports -->
<!--
<PortSubmoduleItem ID="IDS_P2" SubmoduleIdentNumber="0x00008002" SubslotNumber="32770" TextId="IDT_NAME_PS2" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="16"/>
</MAUTypeList>
</PortSubmoduleItem>
-->
</SystemDefinedSubmoduleList>
</DeviceAccessPointItem>
</DeviceAccessPointList>
Expand Down Expand Up @@ -195,6 +204,7 @@
<Text TextId="IDT_CUSTOM_LOGBOOK_1" Value="Custom Logbook entry"/>
<Text TextId="IDT_NAME_IS" Value="I"/>
<Text TextId="IDT_NAME_PS1" Value="P1"/>
<Text TextId="IDT_NAME_PS2" Value="P2"/>
<!--module name-->
<Text TextId="TOK_TextId_Module_I8" Value="8 bits I"/>
<Text TextId="TOK_TextId_Module_O8" Value="8 bits O"/>
Expand Down
250 changes: 235 additions & 15 deletions sample_app/sampleapp_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,7 @@ static void ip_to_string (pnal_ipaddr_t ip, char * outputstring)
(uint8_t) (ip & 0xFF));
}

/**
* Convert MAC address to string
* @param mac In: MAC address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_ETH_ADDRSTR_SIZE.
*/
static void mac_to_string (pnal_ethaddr_t mac, char * outputstring)
void app_mac_to_string (pnal_ethaddr_t mac, char * outputstring)
{
snprintf (
outputstring,
Expand Down Expand Up @@ -189,24 +183,20 @@ static const char * ioxs_to_string (pnet_ioxs_values_t ioxs)
}

void app_print_network_details (
pnal_ethaddr_t * p_macbuffer,
pnal_ipaddr_t ip,
pnal_ipaddr_t netmask,
pnal_ipaddr_t gateway)
{
char ip_string[PNAL_INET_ADDRSTR_SIZE]; /* Terminated string */
char netmask_string[PNAL_INET_ADDRSTR_SIZE]; /* Terminated string */
char gateway_string[PNAL_INET_ADDRSTR_SIZE]; /* Terminated string */
char mac_string[PNAL_ETH_ADDRSTR_SIZE]; /* Terminated string */
char hostname_string[PNAL_HOSTNAME_MAX_SIZE]; /* Terminated string */

mac_to_string (*p_macbuffer, mac_string);
ip_to_string (ip, ip_string);
ip_to_string (netmask, netmask_string);
ip_to_string (gateway, gateway_string);
pnal_get_hostname (hostname_string);

printf ("MAC address: %s\n", mac_string);
printf ("Current hostname: %s\n", hostname_string);
printf ("Current IP address: %s\n", ip_string);
printf ("Current Netmask: %s\n", netmask_string);
Expand Down Expand Up @@ -1265,6 +1255,7 @@ void app_plug_dap (pnet_t * net, void * arg)
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_IDENT,
&cfg_dap_data);

app_exp_submodule_ind (
net,
arg,
Expand All @@ -1274,6 +1265,7 @@ void app_plug_dap (pnet_t * net, void * arg)
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
&cfg_dap_data);

app_exp_submodule_ind (
net,
arg,
Expand All @@ -1283,12 +1275,49 @@ void app_plug_dap (pnet_t * net, void * arg)
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
&cfg_dap_data);

#if PNET_MAX_PORT >= 2
app_exp_submodule_ind (
net,
arg,
APP_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_2_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
&cfg_dap_data);
#endif

#if PNET_MAX_PORT >= 3
app_exp_submodule_ind (
net,
arg,
APP_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_3_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
&cfg_dap_data);
#endif

#if PNET_MAX_PORT >= 4
app_exp_submodule_ind (
net,
arg,
APP_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_4_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
&cfg_dap_data);
#endif
}

/************ Configuration of product ID, software version etc **************/

int app_adjust_stack_configuration (pnet_cfg_t * stack_config)
int app_pnet_cfg_init_default (pnet_cfg_t * stack_config)
{
uint16_t i;
memset (stack_config, 0, sizeof (pnet_cfg_t));
stack_config->tick_us = APP_TICK_INTERVAL_US;

Expand Down Expand Up @@ -1363,9 +1392,16 @@ int app_adjust_stack_configuration (pnet_cfg_t * stack_config)
stack_config->min_device_interval = 32; /* Corresponds to 1 ms */

/* LLDP settings */
strcpy (stack_config->if_cfg.ports[0].port_id, "port-001");
stack_config->if_cfg.ports[0].rtclass_2_status = 0;
stack_config->if_cfg.ports[0].rtclass_3_status = 0;
for (i = 0; i < PNET_MAX_PORT; i++)
{
snprintf (
stack_config->if_cfg.ports[i].port_id,
PNET_LLDP_PORT_ID_MAX_SIZE,
"port-%03d",
i + 1);
stack_config->if_cfg.ports[i].rtclass_2_status = 0;
stack_config->if_cfg.ports[i].rtclass_3_status = 0;
}

/* Network configuration */
stack_config->send_hello = true;
Expand All @@ -1379,6 +1415,190 @@ int app_adjust_stack_configuration (pnet_cfg_t * stack_config)
return 0;
}

/**
* Initialize a network interface configuration from a network
* interface name.
* This includes reading the Ethernet MAC address.
* If the network interface can not be found or the operation
* fails to read the mac address from the network interface,
* error is returned.
* @param netif_name In: Network interface name
* @param p_netif Out: Network interface configuration
* @param verbosity In: Verbosity
* @return 0 on success
* -1 on error
*/
int app_port_cfg_init (
const char * netif_name,
pnet_netif_t * p_netif,
int verbosity)
{
pnal_ethaddr_t mac;
char mac_string[PNAL_ETH_ADDRSTR_SIZE]; /* Terminated string */

int ret = pnal_get_macaddress (netif_name, &mac);
if (ret != 0)
{
printf (
"Error: The given network interface does not exist: \"%s\"\n",
netif_name);
return -1;
}
else
{
if (verbosity > 0)
{
app_mac_to_string (mac, mac_string);
printf ("%-10s %s\n", netif_name, mac_string);
}
}

strncpy (p_netif->if_name, netif_name, PNET_INTERFACE_NAME_MAX_SIZE);
memcpy (p_netif->eth_addr.addr, mac.addr, sizeof (pnal_ethaddr_t));

return 0;
}

int app_get_netif_namelist (
const char * arg_str,
app_netif_namelist_t * p_if_list,
int max_port)
{
int ret = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t if_index = 0;
uint16_t if_list_size = max_port + 1; /* NELEMENTS (p_if_list->netif) dynamic
for test. */
char c;

memset (p_if_list, 0, sizeof (*p_if_list));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to use strtok(_r)? here instead? Something along the lines of:

   char * argv[16];
   char * p;
   char * saveptr;

   /* Split line into argv using space as delimiter */
   p = strtok_r (line, " ", &saveptr);
   while (p != NULL && argc < NELEMENTS (argv) - 1)
   {
      argv[argc++] = p;
      p = strtok_r (NULL, " ", &saveptr);
   }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, possibly. My first idea was to use strtok, but then I needed to copy arg_str for strtok to be able to insert '\0' and I also need to copy the strings so I ended up with this implementation.

Do you prefer that I use a strtok-based implementation instead?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When possible we should use the standard libraries. Unfortunately strok is not reentrant and strok_r (which is reentrant) might not be portable (it's not available on windows for instance). So let's stick with this for now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This network initialization / configuration from the application is messy. We will try to improvement according to #316. In this case this code may disappear.

c = arg_str[i++];
while (c != '\0' && if_index < if_list_size)
{
if (c != ',')
{
p_if_list->netif[if_index].name[j++] = c;
}
else
{
p_if_list->netif[if_index].name[j++] = '\0';
j = 0;
if_index++;
}
c = arg_str[i++];
}

if (max_port == 1)
{
if (if_index >= max_port)
{
printf ("Error: Only 1 network interface expected.\n");
ret = -1;
}

if (strlen (p_if_list->netif[0].name) == 0)
{
printf ("Error: Zero length network interface name.\n");
ret = -1;
}
p_if_list->netif[1] = p_if_list->netif[0];
}

if (max_port > 1)
{
if (if_index != max_port)
{
printf ("Error: %u network interfaces expected.\n", max_port + 1);
ret = -1;
}

for (i = 0; i <= max_port; i++)
{
if (strlen (p_if_list->netif[i].name) == 0)
{
printf ("Error: Zero length network interface name (%d).\n", i);
ret = -1;
}
}
}

return ret;
}

int app_pnet_cfg_init_netifs (
pyhys marked this conversation as resolved.
Show resolved Hide resolved
const char * netif_list_str,
pnet_cfg_t * p_cfg,
int verbosity)
{
int ret = 0;
int i = 0;
pnal_ipaddr_t ip;
pnal_ipaddr_t netmask;
pnal_ipaddr_t gateway;
app_netif_namelist_t if_list;

ret = app_get_netif_namelist (netif_list_str, &if_list, PNET_MAX_PORT);
if (ret != 0)
{
return ret;
}

if (verbosity > 0)
{
printf ("Management port: ");
}
if (
app_port_cfg_init (
if_list.netif[0].name,
&p_cfg->if_cfg.main_port,
verbosity) != 0)
{
return -1;
}

for (i = 1; i <= PNET_MAX_PORT; i++)
{
if (verbosity > 0)
{
printf ("Physical port [%u]: ", i);
}

if (
app_port_cfg_init (
if_list.netif[i].name,
&p_cfg->if_cfg.ports[i - 1].phy_port,
verbosity) != 0)
{
return -1;
}
}

/* Read IP, netmask, gateway from operating system */
ip = pnal_get_ip_address (if_list.netif[0].name);
netmask = pnal_get_netmask (if_list.netif[0].name);
gateway = pnal_get_gateway (if_list.netif[0].name);

if (gateway == IP_INVALID)
{
printf (
"Error: Invalid gateway IP address for Ethernet interface: %s\n",
if_list.netif[0].name);
return -1;
}

if (verbosity > 0)
{
app_print_network_details (ip, netmask, gateway);
}

app_copy_ip_to_struct (&p_cfg->if_cfg.ip_cfg.ip_addr, ip);
app_copy_ip_to_struct (&p_cfg->if_cfg.ip_cfg.ip_gateway, gateway);
app_copy_ip_to_struct (&p_cfg->if_cfg.ip_cfg.ip_mask, netmask);

return ret;
}

/*************************** Helper functions ********************************/

void app_copy_ip_to_struct (
Expand Down
Loading