Browse Source

Add support for shared device

Having support for shared device is at its core the same as having
support for more than one AR. This commit adds such support.

The main changes are:
- Added handling more than one concurrent AR.
- Better separation of real and expected identifications.
- Added submodule ownership handling.
- Change the PNET_MAX_AR configuration setting to be the actual number
  of application relations.

Change-Id: Ie8792cf66317a834391430a7a785e5702f69fe22
Mattias Nilsson 3 năm trước cách đây
mục cha
commit
a55fce3f08

+ 2 - 2
CMakeLists.txt

@@ -69,8 +69,8 @@ option (PNET_OPTION_DRIVER_ENABLE "Enable drivers. Specific driver must be enabl
 # TODO: this should be handled in cc.h
 option (PNET_USE_ATOMICS "Enable use of atomic operations (stdatomic.h)" OFF)
 
-set(PNET_MAX_AR                 2
-  CACHE STRING "Number of connections. Must be > 0. 'Automated RT Tester' uses 2, but only 1 connection AR is supported.")
+set(PNET_MAX_AR                 1
+  CACHE STRING "Number of connections. Must be > 0. If > 1, support shared device")
 set(PNET_MAX_API                1
   CACHE STRING "Number of Application Processes. Must be > 0")
 set(PNET_MAX_CR                 2

+ 2 - 1
README.md

@@ -70,6 +70,7 @@ git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
 - Porting layer provided
 - Supports I&M0 - I&M4. The I&M data is supported for the device, but not for
   individual modules.
+- Shared device (connection to multiple controllers)
 
 ## Limitations
 
@@ -81,7 +82,7 @@ git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
 - No support for DHCP
 - No fast start-up
 - No MC multicast device-to-device
-- No support of shared device (connection to multiple controllers)
+- No support for shared inputs
 - Supports only full connections, not the limited "DeviceAccess" connection type.
 - No iPar (parameter server) support
 - No support for time synchronization

+ 2 - 0
doc/api_documentation.rst

@@ -37,6 +37,7 @@ Plug and pull modules/submodules
 .. doxygenfunction:: pnet_plug_submodule
 .. doxygenfunction:: pnet_pull_module
 .. doxygenfunction:: pnet_pull_submodule
+.. doxygenfunction:: pnet_sm_released_cnf
 
 
 Periodic data
@@ -103,6 +104,7 @@ to perform specific functionality.
 .. doxygentypedef:: pnet_alarm_ind
 .. doxygentypedef:: pnet_alarm_cnf
 .. doxygentypedef:: pnet_alarm_ack_cnf
+.. doxygentypedef:: pnet_sm_released_ind
 
 
 Selected enums

+ 1 - 0
doc/index.rst

@@ -31,6 +31,7 @@ documentation.
 
    capturing_packets.rst
    multiple_ports.rst
+   shared_device.rst
    creating_gsdml_files.rst
    applications_and_porting.rst
    linuxtiming.rst

+ 51 - 0
doc/shared_device.rst

@@ -0,0 +1,51 @@
+Shared device
+=============
+A device can potentially be shared between several controllers, in the sense
+that the submodules of the device need not necessarily all be owned by the same
+controller. On connection to a device, a controller lists the submodules it
+wants to control, and if the device supports the shared device feature, the
+controller is given ownership of all submodules that are not already taken by
+another controller. If two controllers want to own the same submodule, the
+first one to claim it will get the ownership.
+
+If two controllers ("C1" and "C2") are connected to the device, and C1 owns a
+submodule that C2 also has listed in its connect call, then if C1 disconnects,
+the submodule will be released, and the ownership of it will be transferred to
+C2. If there had been a third controller ("C3") which also wanted to control
+the submodule, then on disconnect of C1, the ownership of the submodule would
+be given to the first one of C2 and C3 to connect. In other words, submodule
+ownership is handled on a first come, first served basis.
+
+Configuration
+-------------
+The p-net stack supports the shared device feature if the build configuration
+variable ``PNET_MAX_AR`` is set to a value greater than 1. In this case, the
+application will be required to handle the ``pnet_sm_released_ind`` callback.
+
+The p-net stack does not yet support the closely related shared input feature.
+
+GSDML updates
+-------------
+In addition to increase the number of supported ARs in the build configuration
+a few updates in the the GSDML file is required to support shared device.
+The required settings are available but commented out in the gsdml for the
+sample application. The following attributes shall be set:
+
+* SharedDeviceSupported="true"
+  (in the DeviceAccessPointItem)
+* NumberOfAR="``PNET_MAX_AR``"
+  (in the InterfaceSubmoduleItem/ApplicationRelations)
+
+Controller side
+---------------
+PLCs using a shared device do not need to be aware of each other. If they
+attempt to use the same submodules there will be a conflict as described above.
+
+Codesys
+^^^^^^^
+To use the shared device feature in a Codesys project, simply check the
+"Shared Device" checkbox when the device is added to the engineering project.
+
+If you are using the the free Raspberry PI runtime you will not be able to have
+several controllers in the same project. Create several Codesys projects to
+configure several PLCs to test the shared device feature.

+ 51 - 0
include/pnet_api.h

@@ -635,6 +635,43 @@ typedef int (*pnet_dcontrol_ind) (
    pnet_control_command_t control_command,
    pnet_result_t * p_result);
 
+/**
+ * Indication to the application that a submodule has been released, and is
+ * now owned by a new AR.
+ *
+ * After receiving this, the application is required to set new values for the
+ * following, as applicable for the subslot in question:
+ *
+ * - input data
+ * - input IOPS
+ * - output IOCS
+ *
+ * After setting the values, the application must call
+ * \a pnet_sm_released_cnf(), for the stack to function properly.
+ *
+ * This callback is mandatory to implement if p-net has been configured to
+ * accept more than one AR (PNET_MAX_AR > 1), in which case shared device is
+ * supported. If only one AR is allowed, this callback is never called.
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param arg              InOut: User-defined data (not used by p-net)
+ * @param arep             In:    The AREP.
+ * @param api              In:    The API containing the submodule.
+ * @param slot_number      In:    The slot containing the submodule.
+ * @param subslot_number   In:    The subslot containing the submodule.
+ * @param p_result         Out:   Detailed error information if return != 0.
+ * @return  0  on success.
+ *          -1 if an error occurred.
+ */
+typedef int (*pnet_sm_released_ind) (
+   pnet_t * net,
+   void * arg,
+   uint32_t arep,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   pnet_result_t * p_result);
+
 /**
  * Indication to the application that a CControl confirmation was received from
  * the controller. Typically this means that the controller has received our
@@ -1297,6 +1334,7 @@ typedef struct pnet_cfg
    pnet_alarm_ack_cnf alarm_ack_cnf_cb;
    pnet_reset_ind reset_cb;
    pnet_signal_led_ind signal_led_cb;
+   pnet_sm_released_ind sm_released_cb;
 
    /** User data passed to callbacks, not used by stack */
    void * cb_arg;
@@ -1413,6 +1451,19 @@ PNET_EXPORT void pnet_handle_periodic (pnet_t * net);
  */
 PNET_EXPORT int pnet_application_ready (pnet_t * net, uint32_t arep);
 
+/**
+ * Application signals that it is ready to exchange data for a submodule that
+ * has changed owner.
+ *
+ * This should only be called after the application has set subslot data
+ * following a \a pnet_sm_released_ind() callback. It must not be called in any
+ * other situation.
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param arep             In:    The AREP
+ */
+PNET_EXPORT void pnet_sm_released_cnf (pnet_t * net, uint32_t arep);
+
 /**
  * Plug a module into a slot.
  *

+ 12 - 0
samples/pn_dev/GSDML-V2.4-RT-Labs-P-Net-Sample-App-20220324.xml

@@ -24,6 +24,12 @@
       <ApplicationProcess>
          <DeviceAccessPointList>
             <DeviceAccessPointItem ID="IDD_1" PNIO_Version="V2.4" PhysicalSlots="0..4" ModuleIdentNumber="0x00000001" MinDeviceInterval="32" DNS_CompatibleName="rt-labs-dev" FixedInSlots="0" ObjectUUID_LocalIndex="1" DeviceAccessSupported="false" MultipleWriteSupported="true" CheckDeviceID_Allowed="true" NameOfStationNotTransferable="false" LLDP_NoD_Supported="true" ResetToFactoryModes="1..2">
+            <!--
+Replace the tag above with the one below to support shared device.
+-->
+            <!--
+            <DeviceAccessPointItem ID="IDD_1" PNIO_Version="V2.4" PhysicalSlots="0..4" ModuleIdentNumber="0x00000001" MinDeviceInterval="32" DNS_CompatibleName="rt-labs-dev" FixedInSlots="0" ObjectUUID_LocalIndex="1" DeviceAccessSupported="false" MultipleWriteSupported="true" SharedDeviceSupported="true" CheckDeviceID_Allowed="true" NameOfStationNotTransferable="false" LLDP_NoD_Supported="true" ResetToFactoryModes="1..2">
+-->
                <ModuleInfo>
                   <Name TextId="IDT_MODULE_NAME_DAP1"/>
                   <InfoText TextId="IDT_INFO_DAP1"/>
@@ -51,6 +57,12 @@
                </VirtualSubmoduleList>
                <SystemDefinedSubmoduleList>
                   <InterfaceSubmoduleItem ID="IDS_I" SubmoduleIdentNumber="0x00008000" SubslotNumber="32768" TextId="IDT_NAME_IS" SupportedRT_Classes="RT_CLASS_1" SupportedProtocols="SNMP;LLDP" NetworkComponentDiagnosisSupported="false" PTP_BoundarySupported="true" DCP_BoundarySupported="true">
+                     <!--
+If shared device is supported, add a "NumberOfAR" attribute to the following ApplicationRelations element.
+The number should match the p-net configuration variable PNET_MAX_AR.
+Example, for PNET_MAX_AR = 3:
+                     <ApplicationRelations StartupMode="Advanced" NumberOfAR="3">
+-->
                      <ApplicationRelations StartupMode="Advanced">
                         <TimingProperties SendClock="32" ReductionRatio="1 2 4 8 16 32 64 128 256 512"/>
                      </ApplicationRelations>

+ 121 - 0
samples/pn_dev/app_utils.c

@@ -32,6 +32,127 @@
 #define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
 #define GET_LOW_BYTE(id)  (id & 0xFF)
 
+int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar)
+{
+   uint16_t i;
+
+   CC_ASSERT (api != NULL);
+   CC_ASSERT (ar != NULL);
+   for (i = 0; i < PNET_MAX_AR; ++i)
+   {
+      if (api->ar[i].arep == UINT32_MAX)
+      {
+         api->ar[i].arep = arep;
+         api->ar[i].events = 0;
+         *ar = api->ar + i;
+         return 1;
+      }
+   }
+   return 0;
+}
+
+uint32_t app_ar_arep (app_ar_t * ar)
+{
+   CC_ASSERT (ar != NULL);
+   return ar->arep;
+}
+
+void app_ar_event_set (app_ar_t * ar, uint32_t event)
+{
+   CC_ASSERT (ar != NULL);
+   ar->events |= event;
+}
+
+int app_ar_event_clr (app_ar_t * ar, uint32_t event)
+{
+   CC_ASSERT (ar != NULL);
+   if (ar->events & event)
+   {
+      ar->events &= ~event;
+      return 1;
+   }
+   return 0;
+}
+
+void app_ar_iterator_init (
+   app_ar_iterator_t * iterator,
+   app_api_t * api)
+{
+   CC_ASSERT (iterator != NULL);
+   CC_ASSERT (api != NULL);
+   iterator->ar = api->ar;
+   iterator->index = -1;
+   iterator->modified = false;
+}
+
+int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar)
+{
+#if PNET_MAX_AR > 1
+   uint16_t i;
+   uint16_t j;
+#endif
+
+   CC_ASSERT (iterator != NULL);
+   CC_ASSERT (ar != NULL);
+   ++iterator->index;
+   if (
+      (iterator->index < PNET_MAX_AR) &&
+      (iterator->ar[iterator->index].arep != UINT32_MAX))
+   {
+      *ar = iterator->ar + iterator->index;
+      return 1;
+   }
+#if PNET_MAX_AR > 1
+   else if (iterator->modified)
+   {
+      for (i = 0; i < PNET_MAX_AR; ++i)
+      {
+         if (iterator->ar[i].arep == UINT32_MAX)
+         {
+            for (j = i + 1; j < PNET_MAX_AR; ++j)
+            {
+               if (iterator->ar[j].arep != UINT32_MAX)
+               {
+                  iterator->ar[i] = iterator->ar[j];
+                  iterator->ar[j].arep = UINT32_MAX;
+                  break;
+               }
+            }
+            if (j >= PNET_MAX_AR - 1)
+            {
+               break;
+            }
+         }
+      }
+   }
+#endif
+   return 0;
+}
+
+int app_ar_iterator_done (app_ar_iterator_t * iterator)
+{
+   CC_ASSERT (iterator != NULL);
+   if (
+      (iterator->index < 0) ||
+      ((iterator->index < PNET_MAX_AR) &&
+       (iterator->ar[iterator->index].arep != UINT32_MAX)))
+   {
+      return 0;
+   }
+   return 1;
+}
+
+void app_ar_iterator_delete_current (app_ar_iterator_t * iterator)
+{
+   CC_ASSERT (iterator != NULL);
+   CC_ASSERT (iterator->index >= 0);
+   if (iterator->index < PNET_MAX_AR)
+   {
+      iterator->ar[iterator->index].arep = UINT32_MAX;
+      iterator->modified = true;
+   }
+}
+
 void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring)
 {
    snprintf (

+ 92 - 1
samples/pn_dev/app_utils.h

@@ -111,6 +111,25 @@ typedef struct app_slot
    app_subslot_t subslots[PNET_MAX_SUBSLOTS];
 } app_slot_t;
 
+/**
+ * Information relating to an application relation.
+ */
+typedef struct app_ar
+{
+   uint32_t arep;
+   uint32_t events;
+} app_ar_t;
+
+/**
+ * AR list iterator state.
+ */
+typedef struct app_ar_iterator
+{
+   app_ar_t *ar;
+   int16_t index;
+   bool modified;
+} app_ar_iterator_t;
+
 /**
  * Profinet API state for application
  *
@@ -119,12 +138,84 @@ typedef struct app_slot
 typedef struct app_api_t
 {
    uint32_t api_id;
-   uint32_t arep;
+
+   /**
+    * Active AR:s.
+    * A list which is terminated by an entry with arep == UINT32_MAX.
+    */
+   app_ar_t ar[PNET_MAX_AR];
 
    /** Slots. Use slot number as index */
    app_slot_t slots[PNET_MAX_SLOTS];
 } app_api_t;
 
+/**
+ * Add an arep to the AR list of an API.
+ * @param api              InOut: The \a app_api_t instance.
+ * @param arep             In:    The arep to add.
+ * @param ar               Out:   The AR entry, if successful.
+ *
+ * @return 0 if the arep could not be added.
+ *         1 if the arep was added.
+ */
+int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar);
+
+/**
+ * Get the arep of an AR.
+ * @param ar               In: The AR.
+ * @return the arep of the AR.
+ */
+uint32_t app_ar_arep (app_ar_t * ar);
+
+/**
+ * Clear an event from the events value of the AR.
+ * @param ar               InOut: The AR to modify.
+ * @param event            In:    The event(s) (bitmask) that should be cleared.
+ * @return 0 if none of the set bits in \a event were set for the AR.
+ *         1 if at least one of the set bits in \a event were set for the AR.
+ */
+int app_ar_event_clr (app_ar_t * ar, uint32_t event);
+
+/**
+ * Set an event from the events value of the AR.
+ * @param ar               InOut: The AR to modify.
+ * @param event            In:    The event(s) (bitmask) that should be set.
+ */
+void app_ar_event_set (app_ar_t * ar, uint32_t event);
+
+/**
+ * Initialize a list iterator for the AR list of an API.
+ * @param iterator         InOut: The iterator to be initialized.
+ * @param api              In:    The \a app_api_t instance.
+ */
+void app_ar_iterator_init (
+   app_ar_iterator_t * iterator,
+   app_api_t * api);
+
+/**
+ * Get the next AR from a list iterator.
+ * @param iterator         InOut: The iterator to use.
+ * @param ar               Out:   If the return value is 1, the next AR from
+ *                                the list.
+ * @return 0 if there were no more items in the list.
+ *         1 if there was another item in the list.
+ */
+int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar);
+
+/**
+ * Check whether the iterator has run to the end of entries.
+ * @param iterator         InOut: The iterator.
+ * @return 0 if the iterator is finished.
+ *         1 if the iterator is finished.
+ */
+int app_ar_iterator_done (app_ar_iterator_t * iterator);
+
+/**
+ * Delete the current AR entry of the list iterator.
+ * @param ar               InOut: The AR.
+ */
+void app_ar_iterator_delete_current (app_ar_iterator_t * iterator);
+
 /**
  * Convert IP address to string
  * @param ip               In:    IP address

+ 248 - 111
samples/pn_dev/sampleapp_common.c

@@ -31,6 +31,7 @@
 #define APP_EVENT_READY_FOR_DATA BIT (0)
 #define APP_EVENT_TIMER          BIT (1)
 #define APP_EVENT_ALARM          BIT (2)
+#define APP_EVENT_SM_RELEASED    BIT (3)
 #define APP_EVENT_ABORT          BIT (15)
 
 /* Defines used for alarm demo functionality */
@@ -87,8 +88,6 @@ typedef struct app_data_t
    app_demo_state_t alarm_demo_state;
    uint8_t alarm_payload[APP_GSDML_ALARM_PAYLOAD_SIZE];
 
-   uint32_t arep_for_appl_ready;
-
    bool button1_pressed;
    bool button2_pressed;
    bool button2_pressed_previous;
@@ -125,17 +124,24 @@ pnet_t * app_get_pnet_instance (app_data_t * app)
  */
 static bool app_is_connected_to_controller (app_data_t * app)
 {
-   return app->main_api.arep != UINT32_MAX;
+   return app->main_api.ar[0].arep != UINT32_MAX;
 }
 
 app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args)
 {
+   app_data_t * app;
+   uint16_t i;
+
    APP_LOG_INFO ("Init P-Net stack and sample application\n");
 
-   app_data_t * app = &app_state;
+   app = &app_state;
 
    app->alarm_allowed = true;
-   app->main_api.arep = UINT32_MAX;
+   for (i = 0; i < PNET_MAX_AR; ++i)
+   {
+      app->main_api.ar[i].arep = UINT32_MAX;
+      app->main_api.ar[i].events = 0;
+   }
    app->pnet_cfg = pnet_cfg;
 
    app->net = pnet_init (app->pnet_cfg);
@@ -214,6 +220,40 @@ static void app_set_outputs_default_value (void)
    app_data_set_default_outputs();
 }
 
+/**
+ * Set event flag(s) for one arep.
+ */
+static void app_event_set (
+   app_data_t * app,
+   uint32_t arep,
+   uint32_t event,
+   bool add_arep)
+{
+   app_ar_iterator_t iter;
+   app_ar_t * ar;
+
+   app_ar_iterator_init (&iter, &app->main_api);
+   while (app_ar_iterator_next (&iter, &ar))
+   {
+      if (app_ar_arep (ar) == arep)
+      {
+         app_ar_event_set (ar, event);
+         break;
+      }
+   }
+   if (add_arep)
+   {
+      if (app_ar_iterator_done (&iter))
+      {
+         if (app_ar_add_arep (&app->main_api, arep, &ar))
+         {
+            app_ar_event_set (ar, event);
+         }
+      }
+   }
+   os_event_set (app->main_events, event);
+}
+
 /*********************************** Callbacks ********************************/
 
 static int app_connect_ind (
@@ -261,6 +301,30 @@ static int app_dcontrol_ind (
    return 0;
 }
 
+static int app_sm_released_ind (
+   pnet_t * net,
+   void * arg,
+   uint32_t arep,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   pnet_result_t * p_result)
+{
+   app_data_t * app = (app_data_t *)arg;
+
+   APP_LOG_DEBUG (
+      "SM released indication.\n"
+      "  AREP: %u API: %u Slot: 0x%x Subslot: 0x%x\n",
+      arep,
+      api,
+      slot_number,
+      subslot_number);
+
+   app_event_set (app, arep, APP_EVENT_SM_RELEASED, false);
+
+   return 0;
+}
+
 static int app_ccontrol_cnf (
    pnet_t * net,
    void * arg,
@@ -466,16 +530,10 @@ static int app_state_ind (
       /* Set output values */
       app_set_outputs_default_value();
 
-      /* Only abort AR with correct session key */
-      os_event_set (app->main_events, APP_EVENT_ABORT);
+      app_event_set (app, arep, APP_EVENT_ABORT, false);
    }
    else if (event == PNET_EVENT_PRMEND)
    {
-      if (app_is_connected_to_controller (app))
-      {
-         APP_LOG_WARNING ("Warning - AREP out of sync\n");
-      }
-      app->main_api.arep = arep;
       app_set_initial_data_and_ioxs (app);
 
       (void)pnet_set_provider_state (net, true);
@@ -483,8 +541,7 @@ static int app_state_ind (
       /* Send application ready at next tick
          Do not call pnet_application_ready() here as it will affect
          the internal stack states */
-      app->arep_for_appl_ready = arep;
-      os_event_set (app->main_events, APP_EVENT_READY_FOR_DATA);
+      app_event_set (app, arep, APP_EVENT_READY_FOR_DATA, true);
    }
    else if (event == PNET_EVENT_DATA)
    {
@@ -785,7 +842,8 @@ static int app_alarm_ind (
       data_usi);
 
    app->alarm_arg = *p_alarm_arg;
-   os_event_set (app->main_events, APP_EVENT_ALARM);
+
+   app_event_set (app, arep, APP_EVENT_ALARM, false);
 
    return 0;
 }
@@ -920,58 +978,6 @@ static void app_plug_dap (app_data_t * app, uint16_t number_of_ports)
    APP_LOG_DEBUG ("Done plugging DAP\n\n");
 }
 
-/**
- * Send application ready to the PLC
- * @param net              InOut: p-net stack instance
- * @param arep             In:    Arep
- */
-static void app_handle_send_application_ready (pnet_t * net, uint32_t arep)
-{
-   int ret = -1;
-
-   APP_LOG_DEBUG (
-      "Application will signal that it is ready for data, for "
-      "AREP %u.\n",
-      arep);
-
-   ret = pnet_application_ready (net, arep);
-   if (ret != 0)
-   {
-      APP_LOG_ERROR (
-         "Error returned when application telling that it is ready for "
-         "data. Have you set IOCS or IOPS for all subslots?\n");
-   }
-
-   /* When the PLC sends a confirmation to this message, the
-      pnet_ccontrol_cnf() callback will be triggered.  */
-}
-
-/**
- * Send alarm ACK to the PLC
- *
- * @param net              InOut: p-net stack instance
- * @param arep             In:    Arep
- * @param p_alarm_arg      In:    Alarm argument (slot, subslot etc)
- */
-static void app_handle_send_alarm_ack (
-   pnet_t * net,
-   uint32_t arep,
-   const pnet_alarm_argument_t * p_alarm_arg)
-{
-   pnet_pnio_status_t pnio_status = {0, 0, 0, 0};
-   int ret;
-
-   ret = pnet_alarm_send_ack (net, arep, p_alarm_arg, &pnio_status);
-   if (ret != 0)
-   {
-      APP_LOG_DEBUG ("Error when sending alarm ACK. Error: %d\n", ret);
-   }
-   else
-   {
-      APP_LOG_DEBUG ("Alarm ACK sent\n");
-   }
-}
-
 /**
  * Handle cyclic input- and output data for a subslot.
  *
@@ -1295,7 +1301,7 @@ static void app_handle_demo_pnet_api (app_data_t * app)
             app->alarm_payload[0]);
          pnet_alarm_send_process_alarm (
             app->net,
-            app->main_api.arep,
+            app->main_api.ar[0].arep,
             APP_GSDML_API,
             slot,
             p_subslot->subslot_nbr,
@@ -1446,7 +1452,7 @@ static void app_handle_demo_pnet_api (app_data_t * app)
          pnio_status.error_code_2 = APP_GSDML_LOGBOOK_ERROR_CODE_2;
          pnet_create_log_book_entry (
             app->net,
-            app->main_api.arep,
+            app->main_api.ar[0].arep,
             &pnio_status,
             APP_GSDML_LOGBOOK_ENTRY_DETAIL);
       }
@@ -1463,8 +1469,8 @@ static void app_handle_demo_pnet_api (app_data_t * app)
          APP_LOG_INFO (
             "Sample app will disconnect and reconnect. Executing "
             "pnet_ar_abort()  AREP: %u\n",
-            app->main_api.arep);
-         (void)pnet_ar_abort (app->net, app->main_api.arep);
+            app->main_api.ar[0].arep);
+         (void)pnet_ar_abort (app->net, app->main_api.ar[0].arep);
       }
       else
       {
@@ -1533,6 +1539,7 @@ void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg)
    pnet_cfg->alarm_ack_cnf_cb = app_alarm_ack_cnf;
    pnet_cfg->reset_cb = app_reset_ind;
    pnet_cfg->signal_led_cb = app_signal_led_ind;
+   pnet_cfg->sm_released_cb = app_sm_released_ind;
 
    pnet_cfg->cb_arg = (void *)&app_state;
 }
@@ -1555,15 +1562,164 @@ static void update_button_states (app_data_t * app)
    }
 }
 
+/* Event handlers for the main loop. */
+
+static void app_handle_event_timer (app_data_t * app)
+{
+   os_event_clr (app->main_events, APP_EVENT_TIMER);
+
+   update_button_states (app);
+   if (app_is_connected_to_controller (app))
+   {
+      app_handle_cyclic_data (app);
+   }
+
+   /* Run alarm demo function if button2 is pressed */
+   if ((app->button2_pressed == true) && (app->button2_pressed_previous == false))
+   {
+      app_handle_demo_pnet_api (app);
+   }
+   app->button2_pressed_previous = app->button2_pressed;
+
+   /* Run p-net stack */
+   pnet_handle_periodic (app->net);
+}
+
+/**
+ * Handle AR specific events.
+ *
+ * Calls the \a handler for each AR that has a pending \a event.
+ *
+ * @param app              InOut: Application handle
+ * @param event            In:    Event
+ * @param handler          In:    Event handling function
+ */
+static void app_handle_event_ar (
+   app_data_t * app,
+   uint32_t event,
+   app_ar_event_handler_t handler)
+{
+   app_ar_iterator_t iter;
+   app_ar_t * ar;
+
+   os_event_clr (app->main_events, event);
+   app_ar_iterator_init (&iter, &app->main_api);
+   while (app_ar_iterator_next (&iter, &ar))
+   {
+      if (app_ar_event_clr (ar, event))
+      {
+         if (handler (app, app_ar_arep (ar)))
+         {
+            app_ar_iterator_delete_current (&iter);
+         }
+      }
+   }
+}
+
+/* AR specific event handlers */
+
+/**
+ * Handle an AR connection PrmEnd.
+ *
+ * @param app              InOut: Application handle
+ * @param arep             In:    Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_ready_for_data_handler (app_data_t * app, uint32_t arep)
+{
+   int err;
+
+   APP_LOG_DEBUG (
+      "Application will signal that it is ready for data, for AREP %u.\n",
+      arep);
+   /* When the PLC sends a confirmation to this message, the
+      pnet_ccontrol_cnf() callback will be triggered.  */
+   err = pnet_application_ready (app->net, arep);
+   if (err)
+   {
+      APP_LOG_ERROR (
+         "Error returned when application telling that it is ready for "
+         "data. Have you set IOCS or IOPS for all subslots?\n");
+   }
+   return 0;
+}
+
+/**
+ * Handle an AR alarm.
+ *
+ * @param app              InOut: Application handle
+ * @param arep             In:    Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_alarm_handler (app_data_t * app, uint32_t arep)
+{
+   pnet_pnio_status_t pnio_status = {0, 0, 0, 0};
+   int err;
+
+   err = pnet_alarm_send_ack (app->net, arep, &app->alarm_arg, &pnio_status);
+   if (err)
+   {
+      APP_LOG_DEBUG ("Error when sending alarm ACK. Error: %d\n", err);
+   }
+   else
+   {
+      APP_LOG_DEBUG ("Alarm ACK sent\n");
+   }
+   return 0;
+}
+
+/**
+ * Handle a released submodule.
+ *
+ * @param app              InOut: Application handle
+ * @param arep             In:    Arep
+ *
+ * @return 0 to indicate that the arep should not be removed.
+ */
+static int app_ar_sm_released_handler (app_data_t * app, uint32_t arep)
+{
+   /* Ideally, we should only set data for the indicated submodule,
+      but setting everything works. */
+   app_set_initial_data_and_ioxs (app);
+   pnet_sm_released_cnf (app->net, arep);
+   return 0;
+}
+
+/**
+ * Handle an AR abort.
+ *
+ * @param app              InOut: Application handle
+ * @param arep             In:    Arep
+ *
+ * @return 1 to indicate that the arep should be removed.
+ */
+static int app_ar_abort_handler (app_data_t * app, uint32_t arep)
+{
+   APP_LOG_DEBUG ("Connection (AREP %u) closed\n", arep);
+   return 1;
+}
+
+/**
+ * Wait for events generated elsewhere in this program.
+ *
+ * @param app              InOut: Application handle
+ * @param mask             In:    Bitmask of events to wait for
+ * @param flags            Out:   Bitmask of pending events
+ */
+static void app_event_wait (app_data_t * app, uint32_t mask, uint32_t * flags)
+{
+   os_event_wait (app->main_events, mask, flags, OS_WAIT_FOREVER);
+}
+
 void app_loop_forever (void * arg)
 {
    app_data_t * app = (app_data_t *)arg;
    uint32_t mask = APP_EVENT_READY_FOR_DATA | APP_EVENT_TIMER |
-                   APP_EVENT_ALARM | APP_EVENT_ABORT;
+                   APP_EVENT_ALARM | APP_EVENT_SM_RELEASED | APP_EVENT_ABORT;
    uint32_t flags = 0;
 
-   app->main_api.arep = UINT32_MAX;
-
    app_set_led (APP_DATA_LED_ID, false);
    app_plug_dap (app, app->pnet_cfg->num_physical_ports);
    APP_LOG_INFO ("Waiting for PLC connect request\n\n");
@@ -1571,52 +1727,33 @@ void app_loop_forever (void * arg)
    /* Main event loop */
    for (;;)
    {
-      os_event_wait (app->main_events, mask, &flags, OS_WAIT_FOREVER);
+      app_event_wait (app, mask, &flags);
       if (flags & APP_EVENT_READY_FOR_DATA)
       {
-         os_event_clr (app->main_events, APP_EVENT_READY_FOR_DATA);
-
-         app_handle_send_application_ready (app->net, app->arep_for_appl_ready);
+         app_handle_event_ar (
+            app,
+            APP_EVENT_READY_FOR_DATA,
+            app_ar_ready_for_data_handler);
       }
-      else if (flags & APP_EVENT_ALARM)
+      if (flags & APP_EVENT_ALARM)
       {
-         os_event_clr (app->main_events, APP_EVENT_ALARM);
-
-         app_handle_send_alarm_ack (
-            app->net,
-            app->main_api.arep,
-            &app->alarm_arg);
+         app_handle_event_ar (app, APP_EVENT_ALARM, app_ar_alarm_handler);
       }
-      else if (flags & APP_EVENT_TIMER)
+      if (flags & APP_EVENT_TIMER)
       {
-         os_event_clr (app->main_events, APP_EVENT_TIMER);
-
-         update_button_states (app);
-         if (app_is_connected_to_controller (app))
-         {
-            app_handle_cyclic_data (app);
-         }
-
-         /* Run alarm demo function if button2 is pressed */
-         if (
-            (app->button2_pressed == true) &&
-            (app->button2_pressed_previous == false))
-         {
-            app_handle_demo_pnet_api (app);
-         }
-         app->button2_pressed_previous = app->button2_pressed;
-
-         /* Run p-net stack */
-         pnet_handle_periodic (app->net);
+         app_handle_event_timer (app);
       }
-      else if (flags & APP_EVENT_ABORT)
+      if (flags & APP_EVENT_SM_RELEASED)
       {
-         os_event_clr (app->main_events, APP_EVENT_ABORT);
-
-         app->main_api.arep = UINT32_MAX;
+         app_handle_event_ar (
+            app,
+            APP_EVENT_SM_RELEASED,
+            app_ar_sm_released_handler);
+      }
+      if (flags & APP_EVENT_ABORT)
+      {
+         app_handle_event_ar (app, APP_EVENT_ABORT, app_ar_abort_handler);
          app->alarm_allowed = true;
-         APP_LOG_DEBUG ("Connection closed\n");
-         APP_LOG_DEBUG ("Waiting for PLC connect request\n\n");
       }
    }
 }

+ 13 - 0
samples/pn_dev/sampleapp_common.h

@@ -72,6 +72,19 @@ typedef enum
 
 typedef struct app_data_t app_data_t;
 
+/**
+ * AR specific event handler type.
+ *
+ * Handles an AR specific event.
+ *
+ * @param app          InOut: Application handle
+ * @param arep         In:    Arep of the AR.
+ *
+ * @return 0 to indicate that the arep should be kept
+ *         1 to indicate that the arep should be forgotten
+ */
+typedef int (*app_ar_event_handler_t) (app_data_t * app, uint32_t arep);
+
 /** Partially initialise config values, and use proper callbacks
  *
  * @param pnet_cfg     Out:   Configuration to be updated

+ 2 - 0
src/CMakeLists.txt

@@ -40,6 +40,7 @@ target_sources (profinet PRIVATE
   device/pf_cmwrr.c
   device/pf_pdport.c
   device/pf_port.c
+  device/pf_plugsm.c
   device/pf_block_reader.h
   device/pf_block_writer.h
   device/pf_fspm.h
@@ -59,6 +60,7 @@ target_sources (profinet PRIVATE
   device/pf_cmwrr.h
   device/pf_pdport.h
   device/pf_port.h
+  device/pf_plugsm.h
   common/pf_alarm.c
   common/pf_bg_worker.c
   common/pf_cpm.c

+ 153 - 22
src/common/pf_alarm.c

@@ -681,6 +681,37 @@ static int pf_alarm_alpmr_apmr_a_data_ind (
    return ret;
 }
 
+uint16_t pf_alarm_allocate_endpoint (pnet_t * net, pf_ar_t * ar)
+{
+   uint16_t i;
+
+   for (i = 0; i < PNET_MAX_AR; ++i)
+   {
+      if (net->alarm_endpoint[i].ar == NULL)
+      {
+         net->alarm_endpoint[i].active = false;
+         net->alarm_endpoint[i].ar = ar;
+         return (i + 1);
+      }
+   }
+   return 0;
+}
+
+void pf_alarm_free_endpoint (pnet_t * net, pf_ar_t * ar)
+{
+   uint16_t i;
+
+   for (i = 0; i < PNET_MAX_AR; ++i)
+   {
+      if (net->alarm_endpoint[i].ar == ar)
+      {
+         net->alarm_endpoint[i].active = false;
+         net->alarm_endpoint[i].ar = NULL;
+         return;
+      }
+   }
+}
+
 /*********************** Frame handler callback ******************************/
 
 /**
@@ -702,7 +733,7 @@ static int pf_alarm_alpmr_apmr_a_data_ind (
  * @param p_buf            In:    The Ethernet frame buffer.
  * @param frame_id_pos     In:    Position in the buffer of the frame id.
  *                                Depends on whether VLAN tagging is used.
- * @param p_arg            In:    The APMX instance. Should be pf_apmx_t
+ * @param p_arg            In:    Not used, should be NULL.
  * @return  0 if the frame was NOT handled by this function.
  *          1 if the frame was handled and the buffer was freed.
  */
@@ -713,23 +744,52 @@ static int pf_alarm_apmr_frame_handler (
    uint16_t frame_id_pos,
    void * p_arg)
 {
-   pf_apmx_t * p_apmx = (pf_apmx_t *)p_arg;
+   int priority;
    pf_apmr_msg_t tempmsg;
-   int ret = 0; /* Failed to handle frame. The frame handler needs to free
+   pf_get_info_t get_info;
+   pf_ar_t * ar;
+   uint16_t alarm_endpoint;
+   uint16_t pos;
+   int ret;
+
+   ret = 0; /* Failed to handle frame. The frame handler needs to free
                    the buffer. */
+   priority = (frame_id == PF_FRAME_ID_ALARM_HIGH) ? 1 : 0;
+   ar = NULL;
 
    if (p_buf != NULL)
    {
       tempmsg.p_buf = p_buf;
       tempmsg.frame_id_pos = frame_id_pos;
 
-      if (pf_alarm_receive_queue_post (&p_apmx->alarm_receive_q, &tempmsg) == 0)
+      /* Parse the destination endpoint of the alarm,
+         to know where it should be queued. */
+
+      get_info.result = PF_PARSE_OK;
+      get_info.is_big_endian = true;
+      get_info.p_buf = (uint8_t *)p_buf->payload;
+      get_info.len = p_buf->len;
+
+      pos = frame_id_pos + sizeof (uint16_t); /* Skip frame_id. */
+      alarm_endpoint = pf_get_uint16(&get_info, &pos);
+
+      if (
+         (alarm_endpoint > 0) && (alarm_endpoint <= PNET_MAX_AR) &&
+         net->alarm_endpoint[alarm_endpoint - 1].active)
+      {
+         ar = net->alarm_endpoint[alarm_endpoint - 1].ar;
+      }
+
+      if (
+         (ar != NULL) && (pf_alarm_receive_queue_post (
+                             &ar->apmx[priority].alarm_receive_q,
+                             &tempmsg) == 0))
       {
          LOG_INFO (
             PF_ALARM_LOG,
             "Alarm(%d): Received %s prio alarm frame %p. Put in queue. \n",
             __LINE__,
-            p_apmx->high_priority ? "high" : "low",
+            priority ? "high" : "low",
             p_buf);
 
          ret = 1; /* Means that calling function should not free buffer,
@@ -739,10 +799,11 @@ static int pf_alarm_apmr_frame_handler (
       {
          LOG_ERROR (
             PF_ALARM_LOG,
-            "Alarm(%d): Failed to put incoming %s prio alarm frame in queue. "
+            "Alarm(%d): Failed to put incoming %s prio alarm frame in "
+            "queue. "
             "Framehandler will free %p\n",
             __LINE__,
-            p_apmx->high_priority ? "high" : "low",
+            priority ? "high" : "low",
             p_buf);
       }
    }
@@ -754,7 +815,7 @@ static int pf_alarm_apmr_frame_handler (
          "Alarm(%d): Did not put incoming %s prio alarm frame in queue, "
          "as p_buf is NULL.\n",
          __LINE__,
-         p_apmx->high_priority ? "high" : "low");
+         priority ? "high" : "low");
       ret = 1;
    }
 
@@ -944,7 +1005,7 @@ static int pf_alarm_apmx_activate (pnet_t * net, pf_ar_t * p_ar)
             &p_ar->ar_param.cm_initiator_mac_add,
             sizeof (p_ar->apmx[ix].da));
 
-         p_ar->apmx[ix].src_ref = p_ar->alarm_cr_result.remote_alarm_reference;
+         p_ar->apmx[ix].src_ref = p_ar->alarm_cr_result.local_alarm_reference;
          p_ar->apmx[ix].dst_ref = p_ar->alarm_cr_request.local_alarm_reference;
 
          /* APMS counters */
@@ -995,16 +1056,33 @@ static int pf_alarm_apmx_activate (pnet_t * net, pf_ar_t * p_ar)
       }
    }
 
-   pf_eth_frame_id_map_add (
-      net,
-      p_ar->apmx[0].frame_id,
-      pf_alarm_apmr_frame_handler,
-      &p_ar->apmx[0]);
-   pf_eth_frame_id_map_add (
-      net,
-      p_ar->apmx[1].frame_id,
-      pf_alarm_apmr_frame_handler,
-      &p_ar->apmx[1]);
+   /* Add the frame handler, if no alarm endpoint is active at this point. */
+   for (ix = 0; (ix < PNET_MAX_AR) && (net->alarm_endpoint[ix].active == false);
+        ++ix)
+      ;
+   if (ix >= PNET_MAX_AR)
+   {
+      pf_eth_frame_id_map_add (
+            net,
+            PF_FRAME_ID_ALARM_LOW,
+            pf_alarm_apmr_frame_handler,
+            NULL);
+      pf_eth_frame_id_map_add (
+            net,
+            PF_FRAME_ID_ALARM_HIGH,
+            pf_alarm_apmr_frame_handler,
+            NULL);
+   }
+
+   /* Mark the alarm endpoint as active. */
+   for (ix = 0; ix < PNET_MAX_AR; ++ix)
+   {
+      if (net->alarm_endpoint[ix].ar == p_ar)
+      {
+         net->alarm_endpoint[ix].active = true;
+         break;
+      }
+   }
 
    return ret;
 }
@@ -1539,8 +1617,24 @@ static int pf_alarm_apmx_close (pnet_t * net, pf_ar_t * p_ar, uint8_t err_code)
       }
    }
 
-   pf_eth_frame_id_map_remove (net, PF_FRAME_ID_ALARM_HIGH);
-   pf_eth_frame_id_map_remove (net, PF_FRAME_ID_ALARM_LOW);
+   /* Mark the alarm endpoint as inactive. */
+   for (ix = 0; ix < PNET_MAX_AR; ++ix)
+   {
+      if (net->alarm_endpoint[ix].ar == p_ar)
+      {
+         net->alarm_endpoint[ix].active = false;
+         break;
+      }
+   }
+   /* Remove the frame handler if no alarm endpoint is active at this point. */
+   for (ix = 0; (ix < PNET_MAX_AR) && (net->alarm_endpoint[ix].active == false);
+        ++ix)
+      ;
+   if (ix >= PNET_MAX_AR)
+   {
+      pf_eth_frame_id_map_remove (net, PF_FRAME_ID_ALARM_HIGH);
+      pf_eth_frame_id_map_remove (net, PF_FRAME_ID_ALARM_LOW);
+   }
 
    for (ix = 0; ix < NELEMENTS (p_ar->apmx); ix++)
    {
@@ -2768,7 +2862,10 @@ void pf_alarm_add_diag_item_to_summary (
    uint32_t severity_qualifier = 0;
 
    /* Is the diagnosis on the same AR? */
-   if (p_subslot->p_ar == p_ar)
+   if (
+      ((p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+       (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS)) &&
+      (p_subslot->owner == p_ar))
    {
       is_same_ar = true;
    }
@@ -3483,3 +3580,37 @@ int pf_alarm_send_plug_wrong (
 
    return 0;
 }
+
+int pf_alarm_send_released (
+   pnet_t * net,
+   pf_ar_t * p_ar,
+   uint32_t api_id,
+   uint16_t slot_nbr,
+   uint16_t subslot_nbr,
+   uint32_t module_ident,
+   uint32_t submodule_ident)
+{
+   LOG_INFO (
+      PF_ALARM_LOG,
+      "Alarm(%d): Sending released alarm. Slot: %u  Subslot: 0x%04X\n",
+      __LINE__,
+      slot_nbr,
+      subslot_nbr);
+
+   (void)pf_alarm_send_alarm (
+      net,
+      p_ar,
+      PF_ALARM_TYPE_RELEASED,
+      false, /* Low prio */
+      api_id,
+      slot_nbr,
+      subslot_nbr,
+      NULL, /* No p_diag_item */
+      module_ident,
+      submodule_ident,
+      0,     /* No payload */
+      0,     /* No payload */
+      NULL); /* No payload */
+
+   return 0;
+}

+ 42 - 0
src/common/pf_alarm.h

@@ -27,6 +27,22 @@ extern "C" {
  */
 void pf_alarm_init (pnet_t * net);
 
+/**
+ * Allocate an alarm endpoint for an AR.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               In:    The AR.
+ */
+uint16_t pf_alarm_allocate_endpoint (pnet_t * net, pf_ar_t * ar);
+
+/**
+ * Free the alarm endpoint that has been allocated to an AR.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               In:    The AR.
+ */
+void pf_alarm_free_endpoint (pnet_t * net, pf_ar_t * ar);
+
 /**
  * Create and activate an alarm instance for the specified AR.
  *
@@ -164,6 +180,32 @@ int pf_alarm_send_plug_wrong (
    uint32_t module_ident,
    uint32_t submodule_ident);
 
+/**
+ * Send a RELEASED alarm.
+ *
+ * Uses no alarm payload (not attached to diagnosis ASE.
+ * Related to whole submodule, not related to channels).
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param p_ar             InOut: The AR instance.
+ * @param api_id           In:    The API identifier.
+ * @param slot_nbr         In:    The slot number.
+ * @param subslot_nbr      In:    The sub-slot number.
+ * @param module_ident     In:    The module ident number.
+ * @param submodule_ident  In:    The sub-module ident number.
+ * @return  0  if operation succeeded.
+ *          -1 if an error occurred (or waiting for ACK from controller: re-try
+ *                                  later).
+ */
+int pf_alarm_send_released (
+   pnet_t * net,
+   pf_ar_t * p_ar,
+   uint32_t api_id,
+   uint16_t slot_nbr,
+   uint16_t subslot_nbr,
+   uint32_t module_ident,
+   uint32_t submodule_ident);
+
 /**
  * Send process alarm.
  *

+ 6 - 1
src/common/pf_cpm.c

@@ -342,7 +342,12 @@ static int pf_cpm_get_ar_iocr_desc (
       pf_cmdev_get_subslot_full (net, api_id, slot_nbr, subslot_nbr, &p_subslot) ==
       0)
    {
-      p_ar = p_subslot->p_ar;
+      if (
+         (p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+         (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS))
+      {
+         p_ar = p_subslot->owner;
+      }
    }
 
    if (p_ar == NULL)

+ 6 - 1
src/common/pf_ppm.c

@@ -446,7 +446,12 @@ int pf_ppm_get_ar_iocr_desc (
       pf_cmdev_get_subslot_full (net, api_id, slot_nbr, subslot_nbr, &p_subslot) ==
       0)
    {
-      p_ar = p_subslot->p_ar;
+      if (
+         (p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+         (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS))
+      {
+         p_ar = p_subslot->owner;
+      }
    }
 
    if (p_ar == NULL)

+ 55 - 50
src/device/pf_block_reader.c

@@ -277,40 +277,39 @@ static void pf_get_iocr_api_entry (
 static void pf_get_exp_submodule (
    pf_get_info_t * p_info,
    uint16_t * p_pos,
-   pf_exp_submodule_t * p_sub)
+   pf_exp_submodule_t * submodule)
 {
    uint16_t temp_u16;
 
-   p_sub->subslot_number = pf_get_uint16 (p_info, p_pos);
-   p_sub->submodule_ident_number = pf_get_uint32 (p_info, p_pos);
+   submodule->subslot_number = pf_get_uint16 (p_info, p_pos);
+   submodule->ident_number = pf_get_uint32 (p_info, p_pos);
    /* subslot_properties */
    temp_u16 = pf_get_uint16 (p_info, p_pos);
-   p_sub->submodule_properties.type = pf_get_bits (temp_u16, 0, 2);
-   p_sub->submodule_properties.sharedInput =
-      (pf_get_bits (temp_u16, 2, 1) != 0);
-   p_sub->submodule_properties.reduce_input_submodule_data_length =
+   submodule->properties.type = pf_get_bits (temp_u16, 0, 2);
+   submodule->properties.sharedInput = (pf_get_bits (temp_u16, 2, 1) != 0);
+   submodule->properties.reduce_input_submodule_data_length =
       (pf_get_bits (temp_u16, 3, 1) != 0);
-   p_sub->submodule_properties.reduce_output_submodule_data_length =
+   submodule->properties.reduce_output_submodule_data_length =
       (pf_get_bits (temp_u16, 4, 1) != 0);
-   p_sub->submodule_properties.discard_ioxs =
-      (pf_get_bits (temp_u16, 5, 1) != 0);
+   submodule->properties.discard_ioxs = (pf_get_bits (temp_u16, 5, 1) != 0);
 
    /* At least one submodule data descriptor */
-   p_sub->data_descriptor[0].data_direction = pf_get_uint16 (p_info, p_pos);
-   p_sub->data_descriptor[0].submodule_data_length =
+   submodule->data_descriptor[0].data_direction = pf_get_uint16 (p_info, p_pos);
+   submodule->data_descriptor[0].submodule_data_length =
       pf_get_uint16 (p_info, p_pos);
-   p_sub->data_descriptor[0].length_iocs = pf_get_byte (p_info, p_pos);
-   p_sub->data_descriptor[0].length_iops = pf_get_byte (p_info, p_pos);
-   p_sub->nbr_data_descriptors = 1;
+   submodule->data_descriptor[0].length_iocs = pf_get_byte (p_info, p_pos);
+   submodule->data_descriptor[0].length_iops = pf_get_byte (p_info, p_pos);
+   submodule->nbr_data_descriptors = 1;
    /* May have one more */
-   if (p_sub->submodule_properties.type == PNET_DIR_IO)
+   if (submodule->properties.type == PNET_DIR_IO)
    {
-      p_sub->data_descriptor[1].data_direction = pf_get_uint16 (p_info, p_pos);
-      p_sub->data_descriptor[1].submodule_data_length =
+      submodule->data_descriptor[1].data_direction =
          pf_get_uint16 (p_info, p_pos);
-      p_sub->data_descriptor[1].length_iocs = pf_get_byte (p_info, p_pos);
-      p_sub->data_descriptor[1].length_iops = pf_get_byte (p_info, p_pos);
-      p_sub->nbr_data_descriptors = 2;
+      submodule->data_descriptor[1].submodule_data_length =
+         pf_get_uint16 (p_info, p_pos);
+      submodule->data_descriptor[1].length_iocs = pf_get_byte (p_info, p_pos);
+      submodule->data_descriptor[1].length_iops = pf_get_byte (p_info, p_pos);
+      submodule->nbr_data_descriptors = 2;
    }
 
    LOG_DEBUG (
@@ -318,9 +317,9 @@ static void pf_get_exp_submodule (
       "BR(%d):   Subslot 0x%04x. Expected submodule 0x%" PRIx32
       " with direction %s\n",
       __LINE__,
-      p_sub->subslot_number,
-      p_sub->submodule_ident_number,
-      pf_cmdev_submod_dir_to_string (p_sub->submodule_properties.type));
+      submodule->subslot_number,
+      submodule->ident_number,
+      pf_cmdev_submod_dir_to_string (submodule->properties.type));
 }
 
 /* ======================== Public functions ======================== */
@@ -444,39 +443,45 @@ void pf_get_exp_api_module (
 {
    uint16_t ix;
    uint16_t iy;
-   uint32_t api;
+   uint32_t exp_api;
    uint16_t slot_number;
-   pf_exp_api_t * p_api = NULL;
-   pf_exp_module_t * p_mod = NULL;
+   pf_exp_api_t * api;
+   pf_exp_api_t * this_api;
+   pf_exp_module_t * module;
    uint16_t nbr_exp_api;
 
+   api = NULL;
+   module = NULL;
    nbr_exp_api = pf_get_uint16 (p_info, p_pos); /* In this block */
+
    for (ix = 0; ix < nbr_exp_api; ix++)
    {
       /* Get one module description */
-      api = pf_get_uint32 (p_info, p_pos);
+      exp_api = pf_get_uint32 (p_info, p_pos);
 
       /* Find the API if we are augmenting it */
-      for (iy = 0; iy < p_ar->nbr_exp_apis; iy++)
+      for (iy = 0; iy < p_ar->exp_ident.nbr_apis; iy++)
       {
-         if ((p_ar->exp_apis[iy].valid == true) && (p_ar->exp_apis[iy].api == api))
+         this_api = &p_ar->exp_ident.api[iy];
+         if ((this_api->valid == true) && (this_api->api == exp_api))
          {
-            p_api = &p_ar->exp_apis[iy];
+            api = this_api;
          }
       }
 
       /* If this is the first mention of this API */
-      if ((p_api == NULL) && (p_ar->nbr_exp_apis < PNET_MAX_API))
+      if ((api == NULL) && (p_ar->exp_ident.nbr_apis < PNET_MAX_API))
       {
          /* Allocate a new API */
-         p_api = &p_ar->exp_apis[p_ar->nbr_exp_apis];
-         p_ar->nbr_exp_apis++;
+         api = &p_ar->exp_ident.api[p_ar->exp_ident.nbr_apis];
+         p_ar->exp_ident.nbr_apis++;
 
-         p_api->api = api;
-         p_api->nbr_modules = 0;
+         api->api = exp_api;
+         api->nbr_modules = 0;
+         api->nbr_diff_modules = 0;
       }
 
-      if (p_api == NULL)
+      if (api == NULL)
       {
          /* This error condition is reported by caller. */
          p_info->result = PF_PARSE_OUT_OF_API_RESOURCES;
@@ -490,30 +495,30 @@ void pf_get_exp_api_module (
          slot_number = pf_get_uint16 (p_info, p_pos);
 
          /* Get a new module. */
-         if (p_api->nbr_modules < PNET_MAX_SLOTS)
+         if (api->nbr_modules < PNET_MAX_SLOTS)
          {
-            p_mod = &p_api->modules[p_api->nbr_modules];
-            p_api->nbr_modules++;
+            module = &api->module[api->nbr_modules];
+            api->nbr_modules++;
 
-            p_mod->slot_number = slot_number;
-            p_mod->module_ident_number = pf_get_uint32 (p_info, p_pos);
-            p_mod->module_properties = pf_get_uint16 (p_info, p_pos);
-            p_mod->nbr_submodules = pf_get_uint16 (p_info, p_pos);
+            module->slot_number = slot_number;
+            module->ident_number = pf_get_uint32 (p_info, p_pos);
+            module->properties = pf_get_uint16 (p_info, p_pos);
+            module->nbr_submodules = pf_get_uint16 (p_info, p_pos);
 
             LOG_DEBUG (
                PNET_LOG,
                "BR(%d): Slot %u. Expected module 0x%" PRIx32 " with %u "
                "submodules.\n",
                __LINE__,
-               p_mod->slot_number,
-               p_mod->module_ident_number,
-               p_mod->nbr_submodules);
+               module->slot_number,
+               module->ident_number,
+               module->nbr_submodules);
 
-            for (iy = 0; iy < p_mod->nbr_submodules; iy++)
+            for (iy = 0; iy < module->nbr_submodules; iy++)
             {
-               pf_get_exp_submodule (p_info, p_pos, &p_mod->submodules[iy]);
+               pf_get_exp_submodule (p_info, p_pos, &module->submodule[iy]);
             }
-            p_api->valid = true;
+            api->valid = true;
          }
          else
          {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 583 - 442
src/device/pf_block_writer.c


+ 184 - 78
src/device/pf_block_writer.h

@@ -387,56 +387,64 @@ void pf_put_im_3 (
    uint16_t * p_pos);
 
 /**
- * Insert filtered real or expected ident data into a buffer.
- *
- * filter_level specifies what is being filtered:
- *   If p_ar is != NULL then only select items that belong to this AR.
- *
- *   PF_DEV_FILTER_LEVEL_SUBSLOT means "Only include sub-slot"
- *      specified by api_id, slot_nbr and subslot_nbr.
- *   PF_DEV_FILTER_LEVEL_SLOT means "Include all sub-slots of slot"
- *      specified by api_id and slot_nbr.
- *   PF_DEV_FILTER_LEVEL_API means "Include all slots and sub-slots of API id"
- *      specified by api_id.
- *   PF_DEV_FILTER_LEVEL_DEVICE essentially means "No filtering" on API id,
- *      slot_nbr or subslot_nbr.
- *
- * stop_level specifies how much data is being included:
- *    PF_DEV_FILTER_LEVEL_SUBSLOT means "Include all levels".
- *    PF_DEV_FILTER_LEVEL_SLOT means "Do not include sub-slots".
- *    PF_DEV_FILTER_LEVEL_API means "Do not include slots or sub-slots".
- *    PF_DEV_FILTER_LEVEL_DEVICE means "Only include API count".
+ * Insert real identification data into a buffer.
  *
  * @param net               InOut: The p-net stack instance
- * @param is_big_endian     In:    Endianness of the destination buffer.
+ * @param big_endian        In:    Endianness of the destination buffer.
  * @param block_version_low In:    The minor version number of the block to
  *                                   insert.
- * @param block_type        In:    Specifies REAL or EXP ident number to insert.
- * @param filter_level      In:    The filter level.
- * @param stop_level        In:    The amount of detail to include
- *                                 (ending level).
- * @param p_ar              In:    If != NULL then filter by AR.
- * @param api_id            In:    API id to filter by.
- * @param slot_nbr          In:    Slot number to filter by.
- * @param subslot_nbr       In:    Sub-slot number to filter by.
+ * @param scope             In:    The scope (see Profinet 2.4 services,
+ *                                   sections 7.3.2.2.5.4-8)
+ * @param ar                In:    AR (used for AR scope).
+ * @param api               In:    API (used for device, API, slot or subslot
+ *                                   scope).
+ * @param slot_number       In:    Slot (used for slot or subslot scope).
+ * @param subslot_number    In:    Subslot (used for subslot scope).
  * @param res_len           In:    Size of destination buffer.
- * @param p_bytes           Out:   Destination buffer.
- * @param p_pos             InOut: Position in destination buffer.
+ * @param bytes             Out:   Destination buffer.
+ * @param pos               InOut: Position in destination buffer.
  */
-void pf_put_ident_data (
+void pf_put_real_ident_data (
    pnet_t * net,
-   bool is_big_endian,
+   bool big_endian,
    uint8_t block_version_low,
-   pf_block_type_values_t block_type,
-   pf_dev_filter_level_t filter_level,
-   pf_dev_filter_level_t stop_level,
-   const pf_ar_t * p_ar,
-   uint32_t api_id,
-   uint16_t slot_nbr,
-   uint16_t subslot_nbr,
+   pf_record_data_scope_t scope,
+   pf_ar_t const * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
    uint16_t res_len,
-   uint8_t * p_bytes,
-   uint16_t * p_pos);
+   uint8_t * bytes,
+   uint16_t * pos);
+
+/**
+ * Insert expected identification data into a buffer.
+ *
+ * @param ar                In:    The AR containing the expected
+ *                                   identification.
+ * @param big_endian        In:    Endianness of the destination buffer.
+ * @param block_version_low In:    The minor version number of the block to
+ *                                   insert.
+ * @param scope             In:    The scope (see Profinet 2.4 services,
+ *                                   section 7.3.1.5.5)
+ * @param api               In:    API (used for slot or subslot scope).
+ * @param slot_number       In:    Slot (used for slot or subslot scope).
+ * @param subslot_number    In:    Subslot (used for subslot scope).
+ * @param res_len           In:    Size of destination buffer.
+ * @param bytes             Out:   Destination buffer.
+ * @param pos               InOut: Position in destination buffer.
+ */
+void pf_put_exp_ident_data (
+   pf_ar_t const * ar,
+   bool big_endian,
+   uint8_t block_version_low,
+   pf_record_data_scope_t scope,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   uint16_t res_len,
+   uint8_t * bytes,
+   uint16_t * pos);
 
 /**
  * Insert a pnet status into a buffer
@@ -650,61 +658,159 @@ void pf_put_input_data (
    uint16_t * p_pos);
 
 /**
- * Insert a DiagnosisData block into a buffer.
+ * Insert a DiagnosisData block for a subslot into a buffer.
  *
  * Insert filtered Diagnosis, Maintenance, Qualifiers and Status for one
- * sub-slot/slot/ar/api.
- *
- * filter_level specifies what is being filtered:
- *    If p_ar is != NULL then only select items that belong to this AR.
- *    PF_DEV_FILTER_LEVEL_SUBSLOT:  Only include sub-slot specified by api_id,
- *                                  slot_nbr and subslot_nbr.
- *    PF_DEV_FILTER_LEVEL_SLOT:     Include all sub-slots of slot specified by
- *                                  api_id and slot_nbr.
- *    PF_DEV_FILTER_LEVEL_API:      Include all slots and sub-slots of API id
- *                                  specified by api_id.
- *    PF_DEV_FILTER_LEVEL_DEVICE:   No filtering on API id, slot_nbr or
- *                                  subslot_nbr (i.e. all diags)
- *
- * diag_filter is an orthogonal filter that selects only specific diag types:
+ * subslot.
+ *
+ * diag_filter is a filter that selects only specific diag types:
  *    PF_DIAG_FILTER_FAULT_STD:  Only STD, severity FAULT.
  *    PF_DIAG_FILTER_FAULT_ALL:  Both USI and STD, severity FAULT.
  *    PF_DIAG_FILTER_ALL:        All types of diag, both USI and STD.
  *    PF_DIAG_FILTER_M_REQ:      Only Maintenance required.
  *    PF_DIAG_FILTER_M_DEM:      Only Maintenance demanded.
  *
- * Implemented using:
- *    pf_put_diag_device()
- *       pf_put_diag_api()              for all APIs
- *          pf_put_diag_slot()          for all slots
- *             pf_put_diag_subslot()    for all subslots
- *               pf_put_diag_list()     Header insertion for a USI value
- *                  pf_put_diag_item()  Insertion of diag item
+ * @param net              InOut: The p-net stack instance
+ * @param big_endian       In:    Endianness of the destination buffer.
+ * @param diag_filter      In:    The diag type filter.
+ * @param ar               In:    The AR. If NULL, use real identification.
+ * @param api              In:    The API.
+ * @param slot_number      In:    The slot.
+ * @param subslot_number   In:    The subslot.
+ * @param res_len          In:    Size of destination buffer.
+ * @param p_bytes          Out:   Destination buffer.
+ * @param p_pos            InOut: Position in destination buffer.
+ */
+void pf_put_diagnosis_subslot (
+   pnet_t * net,
+   bool big_endian,
+   pf_diag_filter_level_t diag_filter,
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   uint16_t res_len,
+   uint8_t * bytes,
+   uint16_t * pos);
+
+/**
+ * Insert a DiagnosisData block for a slot into a buffer.
+ *
+ * Insert filtered Diagnosis, Maintenance, Qualifiers and Status for one slot.
+ *
+ * diag_filter is a filter that selects only specific diag types:
+ *    PF_DIAG_FILTER_FAULT_STD:  Only STD, severity FAULT.
+ *    PF_DIAG_FILTER_FAULT_ALL:  Both USI and STD, severity FAULT.
+ *    PF_DIAG_FILTER_ALL:        All types of diag, both USI and STD.
+ *    PF_DIAG_FILTER_M_REQ:      Only Maintenance required.
+ *    PF_DIAG_FILTER_M_DEM:      Only Maintenance demanded.
  *
  * @param net              InOut: The p-net stack instance
- * @param is_big_endian    In:    Endianness of the destination buffer.
- * @param filter_level     In:    The filter level.
+ * @param big_endian       In:    Endianness of the destination buffer.
  * @param diag_filter      In:    The diag type filter.
- * @param p_ar             In:    If != NULL then filter by AR.
- * @param api_id           In:    The API id to filter by.
- * @param slot_nbr         In:    The slot number to filter by.
- * @param subslot_nbr      In:    The sub-slot number to filter by.
+ * @param ar               In:    The AR. If NULL, use real identification.
+ * @param api              In:    The API.
+ * @param slot_number      In:    The slot.
  * @param res_len          In:    Size of destination buffer.
  * @param p_bytes          Out:   Destination buffer.
  * @param p_pos            InOut: Position in destination buffer.
  */
-void pf_put_diag_data (
+void pf_put_diagnosis_slot (
    pnet_t * net,
-   bool is_big_endian,
-   pf_dev_filter_level_t filter_level,
+   bool big_endian,
    pf_diag_filter_level_t diag_filter,
-   const pf_ar_t * p_ar, /* If != NULL only include those belonging to p_ar */
-   uint32_t api_id,
-   uint16_t slot_nbr,
-   uint16_t subslot_nbr,
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
    uint16_t res_len,
-   uint8_t * p_bytes,
-   uint16_t * p_pos);
+   uint8_t * bytes,
+   uint16_t * pos);
+
+/**
+ * Insert a DiagnosisData block for an AR into a buffer.
+ *
+ * Insert filtered Diagnosis, Maintenance, Qualifiers and Status for one AR.
+ *
+ * diag_filter is a filter that selects only specific diag types:
+ *    PF_DIAG_FILTER_FAULT_STD:  Only STD, severity FAULT.
+ *    PF_DIAG_FILTER_FAULT_ALL:  Both USI and STD, severity FAULT.
+ *    PF_DIAG_FILTER_ALL:        All types of diag, both USI and STD.
+ *    PF_DIAG_FILTER_M_REQ:      Only Maintenance required.
+ *    PF_DIAG_FILTER_M_DEM:      Only Maintenance demanded.
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param big_endian       In:    Endianness of the destination buffer.
+ * @param diag_filter      In:    The diag type filter.
+ * @param ar               In:    The AR.
+ * @param res_len          In:    Size of destination buffer.
+ * @param p_bytes          Out:   Destination buffer.
+ * @param p_pos            InOut: Position in destination buffer.
+ */
+void pf_put_diagnosis_ar (
+   pnet_t * net,
+   bool big_endian,
+   pf_diag_filter_level_t diag_filter,
+   pf_ar_t * ar,
+   uint16_t res_len,
+   uint8_t * bytes,
+   uint16_t * pos);
+
+/**
+ * Insert a DiagnosisData block for an API into a buffer.
+ *
+ * Insert filtered Diagnosis, Maintenance, Qualifiers and Status for one API.
+ *
+ * diag_filter is a filter that selects only specific diag types:
+ *    PF_DIAG_FILTER_FAULT_STD:  Only STD, severity FAULT.
+ *    PF_DIAG_FILTER_FAULT_ALL:  Both USI and STD, severity FAULT.
+ *    PF_DIAG_FILTER_ALL:        All types of diag, both USI and STD.
+ *    PF_DIAG_FILTER_M_REQ:      Only Maintenance required.
+ *    PF_DIAG_FILTER_M_DEM:      Only Maintenance demanded.
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param big_endian       In:    Endianness of the destination buffer.
+ * @param diag_filter      In:    The diag type filter.
+ * @param api              In:    The API.
+ * @param res_len          In:    Size of destination buffer.
+ * @param p_bytes          Out:   Destination buffer.
+ * @param p_pos            InOut: Position in destination buffer.
+ */
+void pf_put_diagnosis_api (
+   pnet_t * net,
+   bool big_endian,
+   pf_diag_filter_level_t diag_filter,
+   uint32_t api,
+   uint16_t res_len,
+   uint8_t * bytes,
+   uint16_t * pos);
+
+/**
+ * Insert a DiagnosisData block for a device into a buffer.
+ *
+ * Insert filtered Diagnosis, Maintenance, Qualifiers and Status for one
+ * device.
+ *
+ * diag_filter is a filter that selects only specific diag types:
+ *    PF_DIAG_FILTER_FAULT_STD:  Only STD, severity FAULT.
+ *    PF_DIAG_FILTER_FAULT_ALL:  Both USI and STD, severity FAULT.
+ *    PF_DIAG_FILTER_ALL:        All types of diag, both USI and STD.
+ *    PF_DIAG_FILTER_M_REQ:      Only Maintenance required.
+ *    PF_DIAG_FILTER_M_DEM:      Only Maintenance demanded.
+ *
+ * @param net              InOut: The p-net stack instance
+ * @param big_endian       In:    Endianness of the destination buffer.
+ * @param diag_filter      In:    The diag type filter.
+ * @param res_len          In:    Size of destination buffer.
+ * @param p_bytes          Out:   Destination buffer.
+ * @param p_pos            InOut: Position in destination buffer.
+ */
+void pf_put_diagnosis_device (
+   pnet_t * net,
+   bool big_endian,
+   pf_diag_filter_level_t diag_filter,
+   uint16_t res_len,
+   uint8_t * bytes,
+   uint16_t * pos);
 
 /**
  * Insert PDport data check block into a buffer.

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 271 - 367
src/device/pf_cmdev.c


+ 85 - 7
src/device/pf_cmdev.h

@@ -179,6 +179,32 @@ int pf_cmdev_get_device (pnet_t * net, pf_device_t ** pp_device);
  */
 int pf_cmdev_get_api (pnet_t * net, uint32_t api_id, pf_api_t ** pp_api);
 
+/**
+ * Get a slot instance of an API instance by slot number.
+ * @param p_api            In:  The API instance.
+ * @param slot_nbr         In:  The slot number.
+ * @param pp_slot          Out: The slot instance.
+ * @return  0 if operation succeeded.
+ *         -1 if an error occurred.
+ */
+int pf_cmdev_get_slot (
+   pf_api_t const * p_api,
+   uint16_t slot_nbr,
+   pf_slot_t ** pp_slot);
+
+/**
+ * Get a subslot instance of a slot instance by subslot number.
+ * @param p_slot           In:  The slot instance.
+ * @param subslot_nbr      In:  The subslot number.
+ * @param pp_subslot       Out: The subslot instance.
+ * @return  0 if operation succeeded.
+ *         -1 if an error occurred.
+ */
+int pf_cmdev_get_subslot (
+   pf_slot_t const * p_slot,
+   uint16_t subslot_nbr,
+   pf_subslot_t ** pp_subslot);
+
 /**
  * Get a sub-slot instance from the api ID, the slot number and the sub-slot
  * number.
@@ -192,7 +218,7 @@ int pf_cmdev_get_api (pnet_t * net, uint32_t api_id, pf_api_t ** pp_api);
  */
 int pf_cmdev_get_subslot_full (
    pnet_t * net,
-   uint16_t api_id,
+   uint32_t api_id,
    uint16_t slot_nbr,
    uint16_t subslot_nbr,
    pf_subslot_t ** pp_subslot);
@@ -209,7 +235,7 @@ int pf_cmdev_get_subslot_full (
  */
 int pf_cmdev_get_slot_full (
    pnet_t * net,
-   uint16_t api_id,
+   uint32_t api_id,
    uint16_t slot_nbr,
    pf_slot_t ** pp_slot);
 
@@ -224,7 +250,7 @@ int pf_cmdev_get_slot_full (
  */
 int pf_cmdev_get_module_ident (
    pnet_t * net,
-   uint16_t api_id,
+   uint32_t api_id,
    uint16_t slot_nbr,
    uint32_t * p_module_ident);
 
@@ -241,7 +267,7 @@ int pf_cmdev_get_module_ident (
  */
 int pf_cmdev_get_submodule_ident (
    pnet_t * net,
-   uint16_t api_id,
+   uint32_t api_id,
    uint16_t slot_nbr,
    uint16_t subslot_nbr,
    uint32_t * p_submodule_ident);
@@ -296,13 +322,65 @@ int pf_cmdev_get_next_diagnosis_usi (
    uint16_t * p_next_usi);
 
 /**
- * Generate module diffs, when needed, for the specified AR.
+ * Find the expected module of an AR, addressed by the API and the
+ * slot number.
+ * @param ar               In:    The AR instance.
+ * @param api              In:    The API.
+ * @param slot_number      In:    The slot number.
+ * @param exp_module       Out:   The expected module instance.
+ * @return  0  if a module instance was found.
+ *          -1 if the module instance was not found.
+ */
+int pf_cmdev_get_exp_mod (
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   pf_exp_module_t ** exp_module);
+
+/**
+ * Find the expected submodule of an AR, addressed by the API and the
+ * slot and subslot numbers.
+ * @param ar               In:    The AR instance.
+ * @param api              In:    The API.
+ * @param slot_number      In:    The slot number.
+ * @param subslot_number   In:    The subslot number.
+ * @param exp_submodule    Out:   The expected submodule instance.
+ * @return  0  if a submodule instance was found.
+ *          -1 if the submodule instance was not found.
+ */
+int pf_cmdev_get_exp_sub (
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   pf_exp_submodule_t ** exp_submodule);
+
+/**
+ * Generate module diff, when needed, for the specified AR.
  * @param net              InOut: The p-net stack instance
- * @param p_ar             InOut: The AR instance.
+ * @param ar               InOut: The AR instance.
+ * @return  0  if operation succeeded.
+ *          -1 if an error occurred.
+ */
+int pf_cmdev_generate_module_diff (pnet_t * net, pf_ar_t * ar);
+
+/**
+ * Generate module diff focused on a specific submodule, when needed, for the
+ * specified AR.
+ * @param net              InOut: The p-net stack instance
+ * @param ar               InOut: The AR instance.
+ * @param api              In:    The API containing the submodule.
+ * @param slot_number      In:    The slot containing the submodule.
+ * @param subslot_number   In:    The subslot containing the submodule.
  * @return  0  if operation succeeded.
  *          -1 if an error occurred.
  */
-int pf_cmdev_generate_submodule_diff (pnet_t * net, pf_ar_t * p_ar);
+int pf_cmdev_generate_submodule_diff (
+   pnet_t * net,
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number);
 
 /**
  * Return a string representation of the specified CMDEV state.

+ 1 - 1
src/device/pf_cmpbe.c

@@ -282,7 +282,7 @@ int pf_cmpbe_cm_ccontrol_req (pnet_t * net, pf_ar_t * p_ar)
       /* Control block is always APPLRDY here */
       if (pf_alarm_pending (p_ar) == false)
       {
-         ret = pf_cmrpc_rm_ccontrol_req (net, p_ar);
+         ret = pf_cmrpc_rm_ccontrol_req (net, p_ar, PF_BT_APPRDY_REQ);
          pf_cmpbe_set_state (p_ar, PF_CMPBE_STATE_WFCNF);
       }
    }

+ 52 - 131
src/device/pf_cmrdr.c

@@ -226,14 +226,11 @@ int pf_cmrdr_rm_read_ind (
       /* Block-writer knows where to fetch and how to build the answer to ID
        * data requests. */
       case PF_IDX_SUB_EXP_ID_DATA:
-         pf_put_ident_data (
-            net,
+         pf_put_exp_ident_data (
+            p_ar,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_EXPECTED_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
-            p_ar,
+            PF_RECORD_DATA_SCOPE_SUBSLOT,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -243,13 +240,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_SUB_REAL_ID_DATA:
-         pf_put_ident_data (
+         pf_put_real_ident_data(
             net,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_REAL_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
+            PF_RECORD_DATA_SCOPE_SUBSLOT,
             NULL,
             p_read_request->api,
             p_read_request->slot_number,
@@ -260,14 +255,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_SLOT_EXP_ID_DATA:
-         pf_put_ident_data (
-            net,
+         pf_put_exp_ident_data (
+            p_ar,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_EXPECTED_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_SLOT,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
-            p_ar,
+            PF_RECORD_DATA_SCOPE_SLOT,
             p_read_request->api,
             p_read_request->slot_number,
             0,
@@ -277,13 +269,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_SLOT_REAL_ID_DATA:
-         pf_put_ident_data (
+         pf_put_real_ident_data(
             net,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_REAL_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_SLOT,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
+            PF_RECORD_DATA_SCOPE_SLOT,
             NULL,
             p_read_request->api,
             p_read_request->slot_number,
@@ -294,14 +284,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_AR_EXP_ID_DATA:
-         pf_put_ident_data (
-            net,
+         pf_put_exp_ident_data (
+            p_ar,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_EXPECTED_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_DEVICE,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
-            p_ar,
+            PF_RECORD_DATA_SCOPE_AR,
             0,
             0,
             0,
@@ -311,13 +298,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_AR_REAL_ID_DATA:
-         pf_put_ident_data (
+         pf_put_real_ident_data(
             net,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_REAL_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_DEVICE,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
+            PF_RECORD_DATA_SCOPE_AR,
             p_ar,
             0,
             0,
@@ -328,13 +313,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_API_REAL_ID_DATA:
-         pf_put_ident_data (
+         pf_put_real_ident_data(
             net,
             true,
             PNET_BLOCK_VERSION_LOW_1,
-            PF_BT_REAL_IDENTIFICATION_DATA,
-            PF_DEV_FILTER_LEVEL_API,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
+            PF_RECORD_DATA_SCOPE_API,
             NULL,
             p_read_request->api,
             0,
@@ -346,13 +329,11 @@ int pf_cmrdr_rm_read_ind (
          break;
 
       case PF_IDX_DEV_API_DATA:
-         pf_put_ident_data (
+         pf_put_real_ident_data(
             net,
             true,
             PNET_BLOCK_VERSION_LOW,
-            PF_BT_API_DATA,
-            PF_DEV_FILTER_LEVEL_DEVICE,
-            PF_DEV_FILTER_LEVEL_API_ID,
+            PF_RECORD_DATA_SCOPE_DEVICE,
             NULL,
             0,
             0,
@@ -482,12 +463,11 @@ int pf_cmrdr_rm_read_ind (
       /* Block-writer knows where to fetch and how to build the answer to diag
        * data requests. */
       case PF_IDX_SUB_DIAGNOSIS_CH:
-         pf_put_diag_data (
+         pf_put_diagnosis_subslot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
             PF_DIAG_FILTER_FAULT_STD,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -497,12 +477,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_SUB_DIAGNOSIS_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_subslot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
             PF_DIAG_FILTER_FAULT_ALL,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -512,12 +491,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_SUB_DIAGNOSIS_DMQS:
-         pf_put_diag_data (
+         pf_put_diagnosis_subslot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
             PF_DIAG_FILTER_ALL,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -528,12 +506,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_SUB_DIAG_MAINT_REQ_CH:
       case PF_IDX_SUB_DIAG_MAINT_REQ_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_subslot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
             PF_DIAG_FILTER_M_REQ,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -544,12 +521,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_SUB_DIAG_MAINT_DEM_CH:
       case PF_IDX_SUB_DIAG_MAINT_DEM_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_subslot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SUBSLOT,
             PF_DIAG_FILTER_M_DEM,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
             p_read_request->subslot_number,
@@ -560,45 +536,39 @@ int pf_cmrdr_rm_read_ind (
          break;
 
       case PF_IDX_SLOT_DIAGNOSIS_CH:
-         pf_put_diag_data (
+         pf_put_diagnosis_slot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SLOT,
             PF_DIAG_FILTER_FAULT_STD,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_SLOT_DIAGNOSIS_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_slot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SLOT,
             PF_DIAG_FILTER_FAULT_ALL,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_SLOT_DIAGNOSIS_DMQS:
-         pf_put_diag_data (
+         pf_put_diagnosis_slot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SLOT,
             PF_DIAG_FILTER_ALL,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -606,15 +576,13 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_SLOT_DIAG_MAINT_REQ_CH:
       case PF_IDX_SLOT_DIAG_MAINT_REQ_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_slot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SLOT,
             PF_DIAG_FILTER_M_REQ,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -622,15 +590,13 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_SLOT_DIAG_MAINT_DEM_CH:
       case PF_IDX_SLOT_DIAG_MAINT_DEM_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_slot(
             net,
             true,
-            PF_DEV_FILTER_LEVEL_SLOT,
             PF_DIAG_FILTER_M_DEM,
-            NULL,
+            p_ar,
             p_read_request->api,
             p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -638,45 +604,33 @@ int pf_cmrdr_rm_read_ind (
          break;
 
       case PF_IDX_API_DIAGNOSIS_CH:
-         pf_put_diag_data (
+         pf_put_diagnosis_api (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_API,
             PF_DIAG_FILTER_FAULT_STD,
-            NULL,
             p_read_request->api,
-            p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_API_DIAGNOSIS_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_api (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_API,
             PF_DIAG_FILTER_FAULT_ALL,
-            NULL,
             p_read_request->api,
-            p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_API_DIAGNOSIS_DMQS:
-         pf_put_diag_data (
+         pf_put_diagnosis_api (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_API,
             PF_DIAG_FILTER_ALL,
-            NULL,
             p_read_request->api,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -684,15 +638,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_API_DIAG_MAINT_REQ_CH:
       case PF_IDX_API_DIAG_MAINT_REQ_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_api (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_API,
             PF_DIAG_FILTER_M_REQ,
-            NULL,
             p_read_request->api,
-            p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -700,15 +650,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_API_DIAG_MAINT_DEM_CH:
       case PF_IDX_API_DIAG_MAINT_DEM_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_api (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_API,
             PF_DIAG_FILTER_M_DEM,
-            NULL,
             p_read_request->api,
-            p_read_request->slot_number,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -716,45 +662,33 @@ int pf_cmrdr_rm_read_ind (
          break;
 
       case PF_IDX_AR_DIAGNOSIS_CH:
-         pf_put_diag_data (
+         pf_put_diagnosis_ar (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_FAULT_STD,
             p_ar,
-            0,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_AR_DIAGNOSIS_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_ar (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_FAULT_ALL,
             p_ar,
-            0,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
          ret = 0;
          break;
       case PF_IDX_AR_DIAGNOSIS_DMQS:
-         pf_put_diag_data (
+         pf_put_diagnosis_ar (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_ALL,
             p_ar,
-            0,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -762,15 +696,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_AR_DIAG_MAINT_REQ_CH:
       case PF_IDX_AR_DIAG_MAINT_REQ_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_ar (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_M_REQ,
             p_ar,
-            0,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -778,15 +708,11 @@ int pf_cmrdr_rm_read_ind (
          break;
       case PF_IDX_AR_DIAG_MAINT_DEM_CH:
       case PF_IDX_AR_DIAG_MAINT_DEM_ALL:
-         pf_put_diag_data (
+         pf_put_diagnosis_ar (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_M_DEM,
             p_ar,
-            0,
-            0,
-            0,
             res_size,
             p_res,
             p_pos);
@@ -794,15 +720,10 @@ int pf_cmrdr_rm_read_ind (
          break;
 
       case PF_IDX_DEV_DIAGNOSIS_DMQS:
-         pf_put_diag_data (
+         pf_put_diagnosis_device (
             net,
             true,
-            PF_DEV_FILTER_LEVEL_DEVICE,
             PF_DIAG_FILTER_ALL,
-            NULL, /* Do not filter by AR */
-            0,    /* API */
-            0,    /* Slot */
-            0,    /* Subslot */
             res_size,
             p_res,
             p_pos);
@@ -830,11 +751,11 @@ int pf_cmrdr_rm_read_ind (
          ret = 0;
          break;
       case PF_IDX_AR_MOD_DIFF:
-         /* On implicit with p_ar == NULL pf_cmdev_generate_submodule_diff
+         /* On implicit with p_ar == NULL pf_cmdev_generate_module_diff
           * will return -1
           * In this case pf_put_ar_diff will not give an empty response
           */
-         (void)pf_cmdev_generate_submodule_diff (net, p_ar);
+         (void)pf_cmdev_generate_module_diff (net, p_ar);
          pf_put_ar_diff (true, p_ar, res_size, p_res, p_pos);
          ret = 0;
          break;

+ 133 - 70
src/device/pf_cmrpc.c

@@ -658,6 +658,11 @@ static int pf_session_locate_by_ar (
 /**
  * @internal
  * Allocate and clear a new AR.
+ * Only PNET_MAX_AR AR:s are supported, but since the AR data structure is
+ * needed for unpacking request data, an additional instance can be allocated
+ * in order to generate the correct error response in certain cases. This
+ * additional AR data structure will have arep set to 0, and it should not be
+ * used for anything, except request error checking.
  * @param net              InOut: The p-net stack instance
  * @param pp_ar            Out:  The new AR instance.
  * @return  0  if operation succeeded.
@@ -665,25 +670,35 @@ static int pf_session_locate_by_ar (
  */
 static int pf_ar_allocate (pnet_t * net, pf_ar_t ** pp_ar)
 {
-   int ret = -1;
-   uint16_t ix = 0;
+   int ret;
+   uint16_t ix;
+   pf_ar_t * ar;
+
+   ret = -1;
+
    os_mutex_lock (net->p_cmrpc_rpc_mutex);
+   ix = 0;
    while ((ix < NELEMENTS (net->cmrpc_ar)) &&
           (net->cmrpc_ar[ix].in_use == true))
    {
       ix++;
    }
-
    if (ix < NELEMENTS (net->cmrpc_ar))
    {
-      memset (&net->cmrpc_ar[ix], 0, sizeof (net->cmrpc_ar[ix]));
-      net->cmrpc_ar[ix].in_use = true;
-      pf_cmsu_init (net, &net->cmrpc_ar[ix]);
-      pf_cmwrr_init (net, &net->cmrpc_ar[ix]);
-      pf_scheduler_init_handle (&net->cmrpc_ar[ix].cmio_timeout, "cmio");
-      net->cmrpc_ar[ix].cmio_timer_should_reschedule = false;
+      ar = &net->cmrpc_ar[ix];
 
-      *pp_ar = &net->cmrpc_ar[ix];
+      memset (ar, 0, sizeof (*ar));
+      ar->in_use = true;
+      if (ix < PNET_MAX_AR)
+      {
+         ar->arep = ix + 1; /* Avoid AREP == 0 */
+         pf_cmsu_init (net, ar);
+         pf_cmwrr_init (net, ar);
+         pf_scheduler_init_handle (&ar->cmio_timeout, "cmio");
+         ar->cmio_timer_should_reschedule = false;
+         net->cmrpc_ar_order[net->cmrpc_nbr_ars++] = ix;
+      }
+      *pp_ar = ar;
 
       ret = 0;
    }
@@ -691,7 +706,6 @@ static int pf_ar_allocate (pnet_t * net, pf_ar_t ** pp_ar)
 
    if (ix < PNET_MAX_AR)
    {
-      net->cmrpc_ar[ix].arep = ix + 1; /* Avoid AREP == 0 */
       LOG_DEBUG (
          PF_RPC_LOG,
          "CMRPC(%d): Allocate AR %u (AREP %u)\n",
@@ -708,19 +722,40 @@ static int pf_ar_allocate (pnet_t * net, pf_ar_t ** pp_ar)
  * Free the AR.
  * @param p_ar             InOut: The AR instance.
  */
-static void pf_ar_release (pf_ar_t * p_ar)
+static void pf_ar_release (pnet_t * net, pf_ar_t * p_ar)
 {
+   uint16_t i;
+
    if (p_ar != NULL)
    {
       if (p_ar->in_use == true)
       {
-         LOG_DEBUG (
-            PF_RPC_LOG,
-            "CMRPC(%d): Free AR %u (AREP %u)\n",
-            __LINE__,
-            p_ar->arep - 1,
-            p_ar->arep);
+         if (p_ar->arep > 0)
+         {
+            /* Search for the arep in the ordered vector. */
+            for (i = 0; i < net->cmrpc_nbr_ars; ++i)
+            {
+               if (net->cmrpc_ar_order[i] == (p_ar->arep - 1))
+               {
+                  /* When found, decrement the length of the vector. */
+                  --(net->cmrpc_nbr_ars);
+                  break;
+               }
+            }
+            /* Move all the following areps one step up in the order. */
+            for (; i < net->cmrpc_nbr_ars; ++i)
+            {
+               *(net->cmrpc_ar_order + i) = *(net->cmrpc_ar_order + i + 1);
+            }
+            LOG_DEBUG (
+               PF_RPC_LOG,
+               "CMRPC(%d): Free AR %u (AREP %u)\n",
+               __LINE__,
+               p_ar->arep - 1,
+               p_ar->arep);
+         }
          memset (p_ar, 0, sizeof (*p_ar));
+         p_ar->in_use = false;
       }
       else
       {
@@ -1774,7 +1809,7 @@ static void pf_cmrpc_rm_connect_rsp (
          p_res,
          p_res_pos);
 
-      if (p_ar->nbr_api_diffs > 0)
+      if (p_ar->exp_ident.nbr_diff_apis > 0)
       {
          pf_put_ar_diff (
             p_sess->get_info.is_big_endian,
@@ -1912,29 +1947,42 @@ static int pf_cmrpc_rm_connect_ind (
    /* CheckResource */
    else if (pf_ar_allocate (net, &p_ar) == 0)
    {
-      /* Cross-reference */
-      p_ar->p_sess = p_sess;
-      p_sess->p_ar = p_ar;
-
       /* Parse the Connect request - No support for ArSet (yet) */
       if (pf_cmrpc_rm_connect_interpret_ind (p_sess, &req_pos, p_ar) == 0)
       {
-         if (pf_ar_find_by_uuid (net, &p_ar->ar_param.ar_uuid, &p_ar_2) != 0)
+         if (pf_ar_find_by_uuid (net, &p_ar->ar_param.ar_uuid, &p_ar_2) == 0)
+         {
+            if (p_ar_2->p_sess != p_sess)
+            {
+               p_sess->kill_session = true;
+            }
+            pf_ar_release (net, p_ar);
+            p_ar = p_ar_2;
+
+            /* The ARUUID is already in use by an established AR. */
+            LOG_ERROR (
+               PF_RPC_LOG,
+               "CMRPC(%d): Duplicate Connect request received\n",
+               __LINE__);
+            pf_set_error (
+               &p_sess->rpc_result,
+               PNET_ERROR_CODE_CONNECT,
+               PNET_ERROR_DECODE_PNIO,
+               PNET_ERROR_CODE_1_CMDEV,
+               PNET_ERROR_CODE_2_CMDEV_STATE_CONFLICT);
+         }
+         else
          {
-            /* Valid, unknown AR, NoArSet */
-            if (p_ar->arep > 1)
+            if (p_ar->arep == 0)
             {
-               /* We only support one AR.
-                  However in order to respond with the correct error code, we
-                  need to know whether a new AR has the same ARUUID as the
-                  previous AR. Thus we need to parse the incoming connect
-                  message, and for that to be possible PNET_MAX_AR must be at
-                  least 2. This is checked by ART Tester. */
+               /* An AREP of zero indicates that we already have PNET_MAX_AR
+                  application relations in use. We allow it up to this point,
+                  to be able to catch a duplicate ARUUID above, but here it
+                  becomes an error. */
                LOG_ERROR (
                   PF_RPC_LOG,
-                  "CMRPC(%d): Only one connection (AR) supported! AREP %u\n",
-                  __LINE__,
-                  p_ar->arep);
+                  "CMRPC(%d): Out of AR resources\n",
+                  __LINE__);
                pf_set_error (
                   &p_sess->rpc_result,
                   PNET_ERROR_CODE_CONNECT,
@@ -1944,27 +1992,16 @@ static int pf_cmrpc_rm_connect_ind (
             }
             else
             {
+               /* Cross-reference */
+               p_ar->p_sess = p_sess;
+               p_sess->p_ar = p_ar;
+
                p_ar->ar_state = PF_AR_STATE_PRIMARY;
                p_ar->sync_state = PF_SYNC_STATE_NOT_AVAILABLE;
 
                ret = pf_cmdev_rm_connect_ind (net, p_ar, &p_sess->rpc_result);
             }
          }
-         else
-         {
-            /* Valid, explicit AR - This could be a re-connect */
-            LOG_ERROR (
-               PF_RPC_LOG,
-               "CMRPC(%d): Duplicate Connect request received\n",
-               __LINE__);
-            pf_set_error (
-               &p_sess->rpc_result,
-               PNET_ERROR_CODE_CONNECT,
-               PNET_ERROR_DECODE_PNIO,
-               PNET_ERROR_CODE_1_CMDEV,
-               PNET_ERROR_CODE_2_CMDEV_STATE_CONFLICT);
-            (void)pf_cmdev_cm_abort (net, p_ar_2);
-         }
       }
       else
       {
@@ -1978,7 +2015,7 @@ static int pf_cmrpc_rm_connect_ind (
    else
    {
       /* unavailable */
-      LOG_ERROR (PF_RPC_LOG, "CMRPC(%d): Out of AR resources (AR)\n", __LINE__);
+      LOG_ERROR (PF_RPC_LOG, "CMRPC(%d): Out of AR resources\n", __LINE__);
       pf_set_error (
          &p_sess->rpc_result,
          PNET_ERROR_CODE_CONNECT,
@@ -2000,13 +2037,17 @@ static int pf_cmrpc_rm_connect_ind (
       p_sess->rpc_result.pnio_status.error_code_2);
    pf_cmrpc_rm_connect_rsp (p_sess, ret, p_ar, res_size, p_res, p_res_pos);
 
-   if (
+   if (p_ar_2 != NULL)
+   {
+      (void)pf_cmdev_cm_abort (net, p_ar_2);
+   }
+   else if (
       (ret != 0) ||
       (p_sess->rpc_result.pnio_status.error_code != PNET_ERROR_CODE_NOERROR))
    {
       /* Connect failed: Cleanup and signal to terminate the session. */
       LOG_INFO (PF_RPC_LOG, "CMRPC(%d): Connect failed - Free AR!\n", __LINE__);
-      pf_ar_release (p_ar);
+      pf_ar_release (net, p_ar);
       p_sess->p_ar = NULL;
       p_sess->kill_session = true;
    }
@@ -2392,6 +2433,8 @@ static int pf_cmrpc_rm_dcontrol_interpret_req (
          {
          case PF_BT_PRMBEGIN_REQ:
          case PF_BT_PRMEND_REQ:
+         case PF_BT_PRMEND_PLUG_ALARM_REQ:
+            p_control_io->block_type = block_header.block_type;
             pf_get_control (&p_sess->get_info, &req_pos, p_control_io);
             break;
          default:
@@ -2543,7 +2586,9 @@ static void pf_cmrpc_rm_dcontrol_rsp (
       p_control_io->control_command = BIT (PF_CONTROL_COMMAND_BIT_DONE);
       pf_put_control (
          p_sess->get_info.is_big_endian,
-         PF_BT_PRMEND_RES,
+         (p_control_io->block_type == PF_BT_PRMEND_PLUG_ALARM_REQ)
+            ? PF_BT_PRMEND_PLUG_ALARM_RES
+            : PF_BT_PRMEND_RES,
          p_control_io,
          res_size,
          p_res,
@@ -2626,19 +2671,21 @@ static int pf_cmrpc_rm_dcontrol_ind (
       {
          if (pf_ar_find_by_uuid (net, &control_io.ar_uuid, &p_ar) == 0)
          {
-            if (
-               pf_cmdev_rm_dcontrol_ind (
-                  net,
-                  p_ar,
-                  &control_io,
-                  &p_sess->rpc_result,
-                  p_set_state_prmend) == 0)
+            if (control_io.block_type == PF_BT_PRMEND_PLUG_ALARM_REQ)
             {
-               ret = pf_cmpbe_rm_dcontrol_ind (
+               ret = pf_plugsm_prmend_ind (
+                     net,
+                     p_ar,
+                     &p_sess->rpc_result);
+            }
+            else
+            {
+               ret = pf_cmdev_rm_dcontrol_ind (
                   net,
                   p_ar,
                   &control_io,
-                  &p_sess->rpc_result);
+                  &p_sess->rpc_result,
+                  p_set_state_prmend);
             }
          }
          else
@@ -2877,7 +2924,7 @@ static int pf_cmrpc_rm_read_ind (
       else
       {
          /* Note that for "read implicit" we have no AR */
-         if ((p_ar == NULL) && (opnum != PF_RPC_DEV_OPNUM_READ_IMPLICIT))
+         if ((p_ar == NULL) && (opnum == PF_RPC_DEV_OPNUM_READ_IMPLICIT))
          {
             /* In case an AR is needed try to get the target AR */
             pf_ar_find_by_uuid (net, &read_request.target_ar_uuid, &p_ar);
@@ -3639,7 +3686,10 @@ static int pf_cmrpc_rpc_request (
    return ret;
 }
 
-int pf_cmrpc_rm_ccontrol_req (pnet_t * net, pf_ar_t * p_ar)
+int pf_cmrpc_rm_ccontrol_req (
+   pnet_t * net,
+   pf_ar_t * p_ar,
+   pf_block_type_values_t block_type)
 {
    int ret = -1;
    pf_rpc_header_t rpc_req;
@@ -3713,7 +3763,14 @@ int pf_cmrpc_rm_ccontrol_req (pnet_t * net, pf_ar_t * p_ar)
       control_io.ar_uuid = p_ar->ar_param.ar_uuid;
       control_io.session_key = p_ar->ar_param.session_key;
       control_io.control_command = BIT (PF_CONTROL_COMMAND_BIT_APP_RDY);
-      control_io.alarm_sequence_number = 0; /* Reserved */
+      if (block_type == PF_BT_APPRDY_PLUG_ALARM_REQ)
+      {
+         control_io.alarm_sequence_number = p_ar->alpmx[0].prev_sequence_number;
+      }
+      else
+      {
+         control_io.alarm_sequence_number = 0; /* Reserved */
+      }
       control_io.control_block_properties = 0;
 
       memset (p_sess->out_buffer, 0, sizeof (p_sess->out_buffer));
@@ -3771,7 +3828,7 @@ int pf_cmrpc_rm_ccontrol_req (pnet_t * net, pf_ar_t * p_ar)
       control_pos = p_sess->out_buf_len;
       pf_put_control (
          true,
-         PF_BT_APPRDY_REQ,
+         block_type,
          &control_io,
          sizeof (p_sess->out_buffer),
          p_sess->out_buffer,
@@ -3922,6 +3979,8 @@ static int pf_cmrpc_rm_ccontrol_interpret_cnf (
          switch (block_header.block_type)
          {
          case PF_BT_APPRDY_RES:
+         case PF_BT_APPRDY_PLUG_ALARM_RES:
+            p_control_io->block_type = block_header.block_type;
             pf_get_control (&p_sess->get_info, &req_pos, p_control_io);
             break;
          default:
@@ -4055,8 +4114,12 @@ static int pf_cmrpc_rm_ccontrol_cnf (
        */
       if (ccontrol_io.control_command == BIT (PF_CONTROL_COMMAND_BIT_DONE))
       {
-         /* Send the result to CMDEV */
-         if (
+         if (ccontrol_io.block_type == PF_BT_APPRDY_PLUG_ALARM_RES)
+         {
+            ret = pf_plugsm_application_ready_cnf (net, p_ar);
+            p_sess->kill_session = true;
+         }
+         else /* Send the result to CMDEV */ if (
             pf_cmdev_rm_ccontrol_cnf (
                net,
                p_ar,
@@ -5096,7 +5159,7 @@ int pf_cmrpc_cmdev_state_ind (
          }
 
          /* Finally free the AR */
-         pf_ar_release (p_ar);
+         pf_ar_release (net, p_ar);
       }
       break;
    default:

+ 6 - 1
src/device/pf_cmrpc.h

@@ -132,10 +132,15 @@ int pf_cmrpc_cmdev_state_ind (
  *
  * @param net              InOut: The p-net stack instance
  * @param p_ar             InOut: The AR instance.
+ * @param block_type       In:    The block type, depending on
+ *                                the parameterisation sequence.
  * @return  0  if operation succeeded.
  *          -1 if an error occurred.
  */
-int pf_cmrpc_rm_ccontrol_req (pnet_t * net, pf_ar_t * p_ar);
+int pf_cmrpc_rm_ccontrol_req (
+   pnet_t * net,
+   pf_ar_t * p_ar,
+   pf_block_type_values_t block_type);
 
 /**
  * Show AR and session information.

+ 1 - 0
src/device/pf_cmsu.c

@@ -153,6 +153,7 @@ int pf_cmsu_cmdev_state_ind (
          }
 
          pf_alarm_close (net, p_ar);
+         pf_alarm_free_endpoint (net, p_ar);
 
          pf_cmdmc_close_req (p_ar);
 

+ 18 - 1
src/device/pf_cmwrr.c

@@ -179,6 +179,7 @@ static int pf_cmwrr_write (
    uint16_t * p_req_pos,
    pnet_result_t * p_result)
 {
+   pf_subslot_t * subslot = NULL;
    int ret = -1;
 
    if (
@@ -213,7 +214,23 @@ static int pf_cmwrr_write (
          p_ar->arep);
    }
 
-   if (p_write_request->index <= PF_IDX_USER_MAX)
+   if (
+      (pf_cmdev_get_subslot_full (
+          net,
+          p_write_request->api,
+          p_write_request->slot_number,
+          p_write_request->subslot_number,
+          &subslot) == 0) &&
+      (((subslot->ownsm_state != PF_OWNSM_STATE_IOC) &&
+        (subslot->ownsm_state != PF_OWNSM_STATE_IOS)) ||
+       (subslot->owner != p_ar)))
+   {
+      p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
+      p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
+      p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_ACC_ACCESS_DENIED;
+      p_result->pnio_status.error_code_2 = 0;
+   }
+   else if (p_write_request->index <= PF_IDX_USER_MAX)
    {
       /* User defined indexes */
       ret = pf_fspm_cm_write_ind (

+ 62 - 99
src/device/pf_diag.c

@@ -120,11 +120,11 @@ static void pf_diag_update_submodule_state (
    pf_device_t * p_dev = NULL;
    uint16_t ix;
    pf_diag_item_t * p_item;
-   pf_submod_state_t submodule_state;
+   pf_submod_diag_summary_t diag_summary;
    pnet_alarm_spec_t alarm_spec = {0};
    uint32_t maint_status = 0;
 
-   memset (&submodule_state, 0, sizeof (submodule_state));
+   memset (&diag_summary, 0, sizeof (diag_summary));
 
    if (pf_cmdev_get_device (net, &p_dev) == 0)
    {
@@ -143,29 +143,24 @@ static void pf_diag_update_submodule_state (
 
             if (alarm_spec.submodule_diagnosis == true)
             {
-               submodule_state.fault = true;
+               diag_summary.fault = true;
             }
 
             if (maint_status & PF_DIAG_BIT_MAINTENANCE_REQUIRED)
             {
-               submodule_state.maintenance_required = true;
+               diag_summary.maintenance_required = true;
             }
 
             if (maint_status & PF_DIAG_BIT_MAINTENANCE_DEMANDED)
             {
-               submodule_state.maintenance_demanded = true;
+               diag_summary.maintenance_demanded = true;
             }
          }
 
          ix = p_item->next;
          pf_cmdev_get_diag_item (net, ix, &p_item);
       }
-
-      p_subslot->submodule_state.fault = submodule_state.fault;
-      p_subslot->submodule_state.maintenance_required =
-         submodule_state.maintenance_required;
-      p_subslot->submodule_state.maintenance_demanded =
-         submodule_state.maintenance_demanded;
+      p_subslot->diag_summary = diag_summary;
    }
 }
 
@@ -219,57 +214,30 @@ static void pf_diag_find_entry (
          p_diag_source->subslot,
          pp_subslot) == 0)
    {
-      /* Only search valid sub-modules */
-      if (
-         (((*pp_subslot)->submodule_state.ident_info == PF_SUBMOD_PLUG_OK) ||
-          ((*pp_subslot)->submodule_state.ident_info ==
-           PF_SUBMOD_PLUG_SUBSTITUTE)) &&
-         (((*pp_subslot)->submodule_state.ar_info == PF_SUBMOD_AR_INFO_OWN) ||
-          ((*pp_subslot)->submodule_state.ar_info ==
-           PF_SUBMOD_AR_INFO_APPLICATION_READY_PENDING)))
+      prev_ix = PF_DIAG_IX_NULL;
+      item_ix = (*pp_subslot)->diag_list;
+      pf_cmdev_get_diag_item (net, item_ix, &p_item);
+      while ((p_item != NULL) && (*p_diag_ix == PF_DIAG_IX_NULL))
       {
-         prev_ix = PF_DIAG_IX_NULL;
-         item_ix = (*pp_subslot)->diag_list;
-         pf_cmdev_get_diag_item (net, item_ix, &p_item);
-         while ((p_item != NULL) && (*p_diag_ix == PF_DIAG_IX_NULL))
+         /* Found a match if all of:
+          *    format is STD.
+          *    channel number matches.
+          *    individual channel or channel group
+          *    direction matches.
+          *    ch_error_type matches.
+          *    ext_ch_error_type matches.
+          * ToDo: How do we find manuf_data diag entries??
+          */
+         if (p_item->usi >= PF_USI_CHANNEL_DIAGNOSIS)
          {
-            /* Found a match if all of:
-             *    format is STD.
-             *    channel number matches.
-             *    individual channel or channel group
-             *    direction matches.
-             *    ch_error_type matches.
-             *    ext_ch_error_type matches.
-             * ToDo: How do we find manuf_data diag entries??
-             */
-            if (p_item->usi >= PF_USI_CHANNEL_DIAGNOSIS)
-            {
-               if (
-                  (p_item->fmt.std.ch_nbr == p_diag_source->ch) &&
-                  (p_item->fmt.std.ch_error_type == ch_error_type) &&
-                  (p_item->fmt.std.ext_ch_error_type == ext_ch_error_type) &&
-                  (PF_DIAG_CH_PROP_ACC_GET (p_item->fmt.std.ch_properties) ==
-                   p_diag_source->ch_grouping) &&
-                  (PF_DIAG_CH_PROP_DIR_GET (p_item->fmt.std.ch_properties) ==
-                   p_diag_source->ch_direction))
-               {
-                  *p_diag_ix = item_ix;
-
-                  /* Unlink it from the list so it can be updated. */
-                  if (prev_ix != PF_DIAG_IX_NULL)
-                  {
-                     /* Not first in list */
-                     pf_cmdev_get_diag_item (net, prev_ix, &p_prev);
-                     p_prev->next = p_item->next;
-                  }
-                  else
-                  {
-                     /* Unlink the first item in the list */
-                     (*pp_subslot)->diag_list = p_item->next;
-                  }
-               }
-            }
-            else if (p_item->usi == usi)
+            if (
+               (p_item->fmt.std.ch_nbr == p_diag_source->ch) &&
+               (p_item->fmt.std.ch_error_type == ch_error_type) &&
+               (p_item->fmt.std.ext_ch_error_type == ext_ch_error_type) &&
+               (PF_DIAG_CH_PROP_ACC_GET (p_item->fmt.std.ch_properties) ==
+                p_diag_source->ch_grouping) &&
+               (PF_DIAG_CH_PROP_DIR_GET (p_item->fmt.std.ch_properties) ==
+                p_diag_source->ch_direction))
             {
                *p_diag_ix = item_ix;
 
@@ -286,44 +254,30 @@ static void pf_diag_find_entry (
                   (*pp_subslot)->diag_list = p_item->next;
                }
             }
-
-            prev_ix = item_ix;
-            item_ix = p_item->next;
-            pf_cmdev_get_diag_item (net, item_ix, &p_item);
          }
-      }
-      else
-      {
-         /* Signal not found! */
-         *pp_subslot = NULL;
-      }
-   }
-}
+         else if (p_item->usi == usi)
+         {
+            *p_diag_ix = item_ix;
 
-/**
- * @internal
- * Find first used ar.
- *
- * @param net              InOut: The p-net stack instance.
- * @param pp_ar            Out:   Resulting ar
- * @return 0 on success, -1 on error.
- */
-static int pf_diag_find_first_ar (pnet_t * net, pf_ar_t ** pp_ar)
-{
-   int ret = -1;
-   uint16_t ix;
+            /* Unlink it from the list so it can be updated. */
+            if (prev_ix != PF_DIAG_IX_NULL)
+            {
+               /* Not first in list */
+               pf_cmdev_get_diag_item (net, prev_ix, &p_prev);
+               p_prev->next = p_item->next;
+            }
+            else
+            {
+               /* Unlink the first item in the list */
+               (*pp_subslot)->diag_list = p_item->next;
+            }
+         }
 
-   for (ix = 0; ix < NELEMENTS (net->cmrpc_ar); ix++)
-   {
-      if (net->cmrpc_ar[ix].in_use == true)
-      {
-         *pp_ar = &net->cmrpc_ar[ix];
-         ret = 0;
-         break;
+         prev_ix = item_ix;
+         item_ix = p_item->next;
+         pf_cmdev_get_diag_item (net, item_ix, &p_item);
       }
    }
-
-   return ret;
 }
 
 int pf_diag_add (
@@ -549,9 +503,12 @@ int pf_diag_add (
 
             pf_diag_update_submodule_state (net, p_ar, p_subslot);
 
-            /* TODO Use better strategy to find AR */
-            if (pf_diag_find_first_ar (net, &p_ar) == 0)
+            if (
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS))
             {
+               p_ar = p_subslot->owner;
+
                ret = pf_alarm_send_diagnosis (
                   net,
                   p_ar,
@@ -751,9 +708,12 @@ int pf_diag_update (
 
             pf_diag_update_submodule_state (net, p_ar, p_subslot);
 
-            /* TODO Use better strategy to find AR */
-            if (pf_diag_find_first_ar (net, &p_ar) == 0)
+            if (
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS))
             {
+               p_ar = p_subslot->owner;
+
                ret = pf_alarm_send_diagnosis (
                   net,
                   p_ar,
@@ -873,9 +833,12 @@ int pf_diag_remove (
       {
          if (pf_cmdev_get_diag_item (net, item_ix, &p_item) == 0)
          {
-            /* TODO Use better strategy to find AR */
-            if (pf_diag_find_first_ar (net, &p_ar) == 0)
+            if (
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOC) ||
+               (p_subslot->ownsm_state == PF_OWNSM_STATE_IOS))
             {
+               p_ar = p_subslot->owner;
+
                if (p_subslot->diag_list != PF_DIAG_IX_NULL)
                {
                   /*

+ 227 - 0
src/device/pf_plugsm.c

@@ -0,0 +1,227 @@
+/*********************************************************************
+ *        _       _         _
+ *  _ __ | |_  _ | |  __ _ | |__   ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | |   | |_  _ | || (_| || |_) |\__ \
+ * |_|    \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2023 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 Implements the Plug State Machine (PLUGSM)
+ *
+ * This is a partial implementation of PLUGSM, to handle the release of
+ * submodules when more than one AR is allowed, i.e. when shared device is
+ * supported.
+ */
+
+#include "pf_includes.h"
+
+void pf_plugsm_released_alarm_req (
+   pnet_t * net,
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   uint32_t module_ident,
+   uint32_t submodule_ident)
+{
+   pf_ar_plug_t * plug;
+   uint16_t i;
+
+   plug = &ar->plug_info;
+   if (plug->count >= PF_AR_PLUG_MAX_QUEUE)
+   {
+      LOG_ERROR (
+         PNET_LOG,
+         "PLUGSM(%d): Queue is full for AREP %d\n",
+         __LINE__,
+         ar->arep);
+   }
+   else
+   {
+      i = plug->current + plug->count;
+      if (i >= PF_AR_PLUG_MAX_QUEUE)
+      {
+         i -= PF_AR_PLUG_MAX_QUEUE;
+      }
+      plug->queue[i].api = api;
+      plug->queue[i].slot_number = slot_number;
+      plug->queue[i].subslot_number = subslot_number;
+      plug->queue[i].module_ident = module_ident;
+      plug->queue[i].submodule_ident = submodule_ident;
+      ++plug->count;
+
+      LOG_DEBUG (
+         PNET_LOG,
+         "PLUGSM(%d): Queued released alarm.\n"
+         "  AREP: %u, API: %u, Slot: 0x%x, Subslot: 0x%x\n",
+         __LINE__,
+         ar->arep,
+         api,
+         slot_number,
+         subslot_number);
+   }
+   if (plug->state == PF_PLUGSM_STATE_IDLE)
+   {
+      plug->state = PF_PLUGSM_STATE_WFPRMIND;
+
+      LOG_DEBUG (
+         PNET_LOG,
+         "PLUGSM(%d): Sending released alarm to controller.\n"
+         "  AREP: %u, API: %u, Slot: 0x%x, Subslot: 0x%x\n",
+         __LINE__,
+         ar->arep,
+         plug->queue[plug->current].api,
+         plug->queue[plug->current].slot_number,
+         plug->queue[plug->current].subslot_number);
+
+      pf_alarm_send_released (
+         net,
+         ar,
+         plug->queue[plug->current].api,
+         plug->queue[plug->current].slot_number,
+         plug->queue[plug->current].subslot_number,
+         plug->queue[plug->current].module_ident,
+         plug->queue[plug->current].submodule_ident);
+   }
+}
+
+int pf_plugsm_prmend_ind (pnet_t * net, pf_ar_t * ar, pnet_result_t * result)
+{
+   int ret;
+   pf_ar_plug_t * plug;
+
+   ret = 0;
+   plug = &ar->plug_info;
+   if (plug->state == PF_PLUGSM_STATE_WFPRMIND)
+   {
+      if (net->fspm_cfg.sm_released_cb != NULL)
+      {
+         LOG_DEBUG (
+            PNET_LOG,
+            "PLUGSM(%d): Triggering sm_released callback for AREP %u\n",
+            __LINE__,
+            ar->arep);
+
+         ret = net->fspm_cfg.sm_released_cb (
+            net,
+            net->fspm_cfg.cb_arg,
+            ar->arep,
+            plug->queue[plug->current].api,
+            plug->queue[plug->current].slot_number,
+            plug->queue[plug->current].subslot_number,
+            result);
+      }
+      else
+      {
+         LOG_DEBUG (
+            PNET_LOG,
+            "FSPM(%d): No user callback for sm_released implemented.\n",
+            __LINE__);
+      }
+
+      if (ret == 0)
+      {
+         plug->state = PF_PLUGSM_STATE_WFDATAUPDATE;
+      }
+      else
+      {
+         plug->state = PF_PLUGSM_STATE_WFAPLRDYCNF;
+         pf_plugsm_application_ready_cnf (net, ar);
+      }
+   }
+
+   return ret;
+}
+
+void pf_plugsm_application_ready_req (pnet_t * net, pf_ar_t * ar)
+{
+   pf_ar_plug_t * plug;
+
+   plug = &ar->plug_info;
+   if (plug->state == PF_PLUGSM_STATE_WFDATAUPDATE)
+   {
+      plug->state = PF_PLUGSM_STATE_WFAPLRDYCNF;
+
+      LOG_DEBUG (
+         PNET_LOG,
+         "PLUGSM(%d): Sending application ready to controller.\n"
+         "  AREP: %u, API: %u, Slot: 0x%x, Subslot: 0x%x\n",
+         __LINE__,
+         ar->arep,
+         plug->queue[plug->current].api,
+         plug->queue[plug->current].slot_number,
+         plug->queue[plug->current].subslot_number);
+
+      pf_cmdev_generate_submodule_diff (
+         net,
+         ar,
+         plug->queue[plug->current].api,
+         plug->queue[plug->current].slot_number,
+         plug->queue[plug->current].subslot_number);
+
+      pf_cmrpc_rm_ccontrol_req (net, ar, PF_BT_APPRDY_PLUG_ALARM_REQ);
+   }
+}
+
+int pf_plugsm_application_ready_cnf (pnet_t * net, pf_ar_t * ar)
+{
+   pf_ar_plug_t * plug;
+
+   plug = &ar->plug_info;
+   if (plug->state == PF_PLUGSM_STATE_WFAPLRDYCNF)
+   {
+      LOG_DEBUG (
+         PNET_LOG,
+         "PLUGSM(%d): Application ready confirmed by controller.\n"
+         "  AREP: %u, API: %u, Slot: 0x%x, Subslot: 0x%x\n",
+         __LINE__,
+         ar->arep,
+         plug->queue[plug->current].api,
+         plug->queue[plug->current].slot_number,
+         plug->queue[plug->current].subslot_number);
+
+      --plug->count;
+      ++plug->current;
+      if (plug->current >= PF_AR_PLUG_MAX_QUEUE)
+      {
+         plug->current = 0;
+      }
+      if (plug->count > 0)
+      {
+         plug->state = PF_PLUGSM_STATE_WFPRMIND;
+
+         LOG_DEBUG (
+            PNET_LOG,
+            "PLUGSM(%d): Sending released alarm to controller.\n"
+            "  AREP: %u, API: %u, Slot: 0x%x, Subslot: 0x%x\n",
+            __LINE__,
+            ar->arep,
+            plug->queue[plug->current].api,
+            plug->queue[plug->current].slot_number,
+            plug->queue[plug->current].subslot_number);
+
+         pf_alarm_send_released (
+            net,
+            ar,
+            plug->queue[plug->current].api,
+            plug->queue[plug->current].slot_number,
+            plug->queue[plug->current].subslot_number,
+            plug->queue[plug->current].module_ident,
+            plug->queue[plug->current].submodule_ident);
+      }
+      else
+      {
+         plug->state = PF_PLUGSM_STATE_IDLE;
+      }
+   }
+   return 0;
+}

+ 108 - 0
src/device/pf_plugsm.h

@@ -0,0 +1,108 @@
+/*********************************************************************
+ *        _       _         _
+ *  _ __ | |_  _ | |  __ _ | |__   ___
+ * | '__|| __|(_)| | / _` || '_ \ / __|
+ * | |   | |_  _ | || (_| || |_) |\__ \
+ * |_|    \__|(_)|_| \__,_||_.__/ |___/
+ *
+ * www.rt-labs.com
+ * Copyright 2023 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 Partial implementation of the Plug State Machine (PLUGSM)
+ *
+ * These functions implement the subset of the PLUGSM state machine (see
+ * Profinet 2.4 services, section 7.3.2.2.6.2.4) that deals with submodules
+ * being released by an AR, when it is released. A released submodule may get
+ * a new owning AR, the controller side of which is expected to go through a
+ * parameterisation sequence, before the ownership is finalised.
+ */
+
+#ifndef PF_PLUGSM_H
+#define PF_PLUGSM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Request a Released alarm to be sent for a submodule to a controller through
+ * a specific AR.
+ *
+ * The request is queued up for the AR in question. If the queue is empty at
+ * the time of calling this function, the request is acted upon immediately.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               InOut: The AR instance.
+ * @param api              In:    The API containing the submodule.
+ * @param slot_number      In:    The slot containing the submodule.
+ * @param subslot_number   In:    The subslot containing the submodule.
+ * @param module_ident     In:    The identification of the module.
+ * @param submodule_ident  In:    The identification of the submodule.
+ */
+void pf_plugsm_released_alarm_req (
+   pnet_t * net,
+   pf_ar_t * ar,
+   uint32_t api,
+   uint16_t slot_number,
+   uint16_t subslot_number,
+   uint32_t module_ident,
+   uint32_t submodule_ident);
+
+/**
+ * Handle the PrmEnd request sent by a controller as a result of a submodule
+ * being released.
+ *
+ * This will call the \a pnet_sm_released_ind() callback registered by the
+ * application, and act according to its return value: either record that the
+ * application is expected to call \a pnet_sm_released_cnf(), or activate the
+ * next request for a Released alarm from the same AR.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               InOut: The AR instance.
+ * @param result           Out:   Detailed error information if return != 0;
+ * @return 0 on success.
+ *         -1 on failure.
+ */
+int pf_plugsm_prmend_ind (
+   pnet_t * net,
+   pf_ar_t * ar,
+   pnet_result_t * result);
+
+/**
+ * Send an Application Ready request to the controller, after an AR to it has
+ * become the new owner of a released submodule.
+ *
+ * This will be called indirectly by the application, when it calls \a
+ * pnet_sm_released_cnf() to confirm that it has handled a released submodule.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               InOut: The AR instance.
+ */
+void pf_plugsm_application_ready_req (pnet_t * net, pf_ar_t * ar);
+
+/**
+ * Handle the Application Ready confirmation sent by a controller, after an AR
+ * to it has become the new owner of a released submodule.
+ *
+ * This will check the queue of Released alarm requests, and act on the first
+ * request in the queue, if there is one.
+ *
+ * @param net              InOut: The p-net stack instance.
+ * @param ar               InOut: The AR instance.
+ * @return 0 on success.
+ *         -1 on failure.
+ */
+int pf_plugsm_application_ready_cnf (pnet_t * net, pf_ar_t * ar);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PF_PLUGSM_H */

+ 15 - 0
src/device/pnet_api.c

@@ -472,6 +472,21 @@ int pnet_application_ready (pnet_t * net, uint32_t arep)
    return ret;
 }
 
+void pnet_sm_released_cnf (pnet_t * net, uint32_t arep)
+{
+   pf_ar_t * ar = NULL;
+
+   LOG_DEBUG (
+      PNET_LOG,
+      "API(%d): Application confirms released submodule for AREP %" PRIu32 "\n",
+      __LINE__,
+      arep);
+   if (pf_ar_find_by_arep (net, arep, &ar) == 0)
+   {
+      pf_plugsm_application_ready_req (net, ar);
+   }
+}
+
 int pnet_ar_abort (pnet_t * net, uint32_t arep)
 {
    int ret = -1;

+ 1 - 0
src/pf_includes.h

@@ -67,6 +67,7 @@ extern "C" {
 #include "pf_fspm.h"
 #include "pf_pdport.h"
 #include "pf_port.h"
+#include "pf_plugsm.h"
 
 #ifdef __cplusplus
 }

+ 158 - 124
src/pf_types.h

@@ -787,6 +787,7 @@ typedef enum pf_control_command_bit_positions
 /** Used for both RPC control/release requests and responses */
 typedef struct pf_control_block
 {
+   uint16_t block_type;
    pf_uuid_t ar_uuid;
    uint16_t session_key;
    uint16_t control_command;
@@ -1294,6 +1295,48 @@ typedef enum pf_alarm_cr_type_values
    PF_ALARM_CR = 0x0001,
 } pf_alarm_cr_type_values_t;
 
+/* Expected Identification definitions */
+
+/* Module State (see Profinet 2.4 services, Table 214) */
+typedef enum pf_module_state
+{
+   PF_MODULE_STATE_NO_MODULE = 0, /* There is no module. */
+   PF_MODULE_STATE_WRONG_MODULE,  /* [This value should be avoided.] */
+   PF_MODULE_STATE_PROPER_MODULE, /* The module is the expected one. */
+   PF_MODULE_STATE_SUBSTITUTE     /* The module is compatible. */
+} pf_module_state_t;
+
+/* Additional submodule information
+ * (see Profinet 2.4 services, section 7.3.1.5.4) */
+typedef enum pf_add_info
+{
+   PF_ADD_INFO_NONE,
+   PF_ADD_INFO_TAKEOVER_NOT_ALLOWED
+} pf_add_info_t;
+
+/* AR Info (see Profinet 2.4 services, Table 215) */
+typedef enum pf_ar_info
+{
+   PF_AR_INFO_OWN,                       /* Submodule is owned by AR. */
+   PF_AR_INFO_APPLICATION_READY_PENDING, /* Submodule is owned by AR, but
+                                            parameter checking is pending. */
+   PF_AR_INFO_SUPERORDINATED_LOCKED,     /* Submodule is locked by
+                                            superordinated means. */
+   PF_AR_INFO_LOCKED_BY_IO_CONTROLLER,   /* Submodule is owned by another
+                                            IOC AR. */
+   PF_AR_INFO_LOCKED_BY_IO_SUPERVISOR    /* Submodule is owned by another
+                                            IOS AR.  */
+} pf_ar_info_t;
+
+/* Ident Info (see Profinet 2.4 services, Table 216) */
+typedef enum pf_ident_info
+{
+   PF_IDENT_INFO_OK = 0,     /* The real submodule is the expected one. */
+   PF_IDENT_INFO_SUBSTITUTE, /* The real submodule is compatible. */
+   PF_IDENT_INFO_WRONG,      /* The real submodule is not compatible. */
+   PF_IDENT_INFO_NO          /* There is no real submodule. */
+} pf_ident_info_t;
+
 /** According to the spec these are the states used by CMDEV/CMDEV_DA.
  *
  * The states for the AR is stored in cmdev_state, within the AR.
@@ -1441,23 +1484,38 @@ typedef struct pf_submodule_properties
    bool discard_ioxs;
 } pf_submodule_properties_t;
 
+typedef struct pf_submodule_state
+{
+   uint8_t add_info;          /** Bits 0..2: pf_add_info_t */
+   bool advice_avail;         /** Bit 3 */
+   bool maintenance_required; /** Bit 4 */
+   bool maintenance_demanded; /** Bit 5 */
+   bool fault;                /** Bit 6 */
+   uint8_t ar_info;           /** Bits 7..10: pf_ar_info_t */
+   uint8_t ident_info;        /** Bits 11..14: pf_ident_info_t */
+   bool format_indicator;     /** Bit 15: Always 1 (true) */
+} pf_submodule_state_t;
+
 typedef struct pf_exp_submodule
 {
    uint16_t subslot_number; /** Allowed: 0x0000..0x9FFF */
-   uint32_t submodule_ident_number;
-   pf_submodule_properties_t submodule_properties;
+   uint32_t ident_number;
+   pf_submodule_properties_t properties;
    uint16_t nbr_data_descriptors;
    pf_data_descriptor_t data_descriptor[2]; /** 2 used for PNET_DIR_IO */
+   pf_submodule_state_t state;
 } pf_exp_submodule_t;
 
 typedef struct pf_exp_module
 {
-   uint16_t slot_number; /** Allowed 0x0000.0x7FFF */
-   uint32_t module_ident_number;
-   uint16_t module_properties; /** Reserved - currently unused */
-
+   uint16_t slot_number; /* Allowed: 0x0000.0x7FFF */
+   uint32_t ident_number;
+   uint16_t properties; /* Reserved - currently unused */
+   uint16_t state;      /* pf_module_state_t */
    uint16_t nbr_submodules;
-   pf_exp_submodule_t submodules[PNET_MAX_SUBSLOTS];
+   pf_exp_submodule_t submodule[PNET_MAX_SUBSLOTS];
+   uint16_t nbr_diff_submodules;
+   uint16_t diff_submodule[PNET_MAX_SUBSLOTS];
 } pf_exp_module_t;
 
 typedef struct pf_exp_api
@@ -1465,43 +1523,18 @@ typedef struct pf_exp_api
    bool valid;
    uint32_t api;
    uint16_t nbr_modules;
-   pf_exp_module_t modules[PNET_MAX_SLOTS];
+   pf_exp_module_t module[PNET_MAX_SLOTS];
+   uint16_t nbr_diff_modules;
+   uint16_t diff_module[PNET_MAX_SLOTS];
 } pf_exp_api_t;
 
-typedef struct pf_submodule_state
-{
-   uint8_t add_info;          /** Bits 0..2: pf_submod_add_info_t */
-   bool advice_avail;         /** Bit 3 */
-   bool maintenance_required; /** Bit 4 */
-   bool maintenance_demanded; /** Bit 5 */
-   bool fault;                /** Bit 6 */
-   uint8_t ar_info;           /** Bits 7..10: pf_submod_ar_info_t */
-   uint8_t ident_info;        /** Bits 11..14: pf_submod_plug_state_t */
-   bool format_indicator;     /** Bit 15: Always 1 (true) */
-} pf_submodule_state_t;
-
-typedef struct pf_submodule_diff
-{
-   uint16_t subslot_number;
-   uint32_t submodule_ident_number;
-   pf_submodule_state_t submodule_state;
-} pf_submodule_diff_t;
-
-typedef struct pf_module_diff
-{
-   uint16_t slot_number;
-   uint32_t module_ident_number;
-   uint16_t module_state; /** pf_module_state_values_t */
-   uint16_t nbr_submodule_diffs;
-   pf_submodule_diff_t submodule_diffs[PNET_MAX_SUBSLOTS];
-} pf_module_diff_t;
-
-typedef struct pf_api_diff
+typedef struct pf_exp_ident
 {
-   uint32_t api;
-   uint16_t nbr_module_diffs;
-   pf_module_diff_t module_diffs[PNET_MAX_SLOTS];
-} pf_api_diff_t;
+   uint16_t nbr_apis;
+   pf_exp_api_t api[PNET_MAX_API];
+   uint16_t nbr_diff_apis;
+   uint16_t diff_api[PNET_MAX_API];
+} pf_exp_ident_t;
 
 typedef struct pf_parameter_server_properties
 {
@@ -1652,7 +1685,7 @@ typedef struct pf_alarm_cr_request
 typedef struct pf_alarm_cr_result
 {
    uint16_t alarm_cr_type;          /** pf_alarm_cr_type_values_t (0x0001) */
-   uint16_t remote_alarm_reference; /** Our reference */
+   uint16_t local_alarm_reference;  /** Our reference */
    uint16_t max_alarm_data_length;  /** Range: 200..1432 */
 } pf_alarm_cr_result_t;
 
@@ -2030,6 +2063,31 @@ typedef struct pf_session_info
    uint32_t resend_counter;
 } pf_session_info_t;
 
+#define PF_AR_PLUG_MAX_QUEUE (PNET_MAX_API * PNET_MAX_SLOTS * PNET_MAX_SUBSLOTS)
+
+typedef enum pf_plugsm_state
+{
+   PF_PLUGSM_STATE_IDLE,
+   PF_PLUGSM_STATE_WFPRMIND,
+   PF_PLUGSM_STATE_WFDATAUPDATE,
+   PF_PLUGSM_STATE_WFAPLRDYCNF
+} pf_plugsm_state_t;
+
+typedef struct pf_ar_plug
+{
+   uint16_t state;
+   uint16_t current;
+   uint16_t count;
+   struct
+   {
+      uint32_t api;
+      uint16_t slot_number;
+      uint16_t subslot_number;
+      uint32_t module_ident;
+      uint32_t submodule_ident;
+   } queue[PF_AR_PLUG_MAX_QUEUE];
+} pf_ar_plug_t;
+
 typedef struct pf_ar
 {
    bool in_use;
@@ -2072,11 +2130,7 @@ typedef struct pf_ar
    uint16_t nbr_iocrs;           /* From connect.req, typically 2*/
    pf_iocr_t iocrs[PNET_MAX_CR]; /* Each has a CPM and a PPM */
 
-   uint16_t nbr_exp_apis;
-   pf_exp_api_t exp_apis[PNET_MAX_API]; /* From connect.req */
-
-   uint16_t nbr_api_diffs; /* From connect.ind */
-   pf_api_diff_t api_diffs[PNET_MAX_API];
+   pf_exp_ident_t exp_ident;
 
    uint16_t nbr_prm_server;
    uint16_t nbr_rpc_server;
@@ -2112,6 +2166,8 @@ typedef struct pf_ar
    pf_cmpbe_state_values_t cmpbe_state;
    uint16_t cmpbe_stored_command;
 
+   pf_ar_plug_t plug_info;
+
    /* Optional data */
 #if PNET_OPTION_MC_CR
    uint16_t nbr_mcr;
@@ -2146,66 +2202,20 @@ typedef struct pf_ar
 #endif
 } pf_ar_t;
 
+/* Real Identification definitions */
+
+typedef enum pf_ownsm_state_values
+{
+   PF_OWNSM_STATE_FREE, /* Submodule has no owner. */
+   PF_OWNSM_STATE_SOL,  /* Submodule is superordinate locked. */
+   PF_OWNSM_STATE_IOS,  /* Submodule is owned by an IO supervisor. */
+   PF_OWNSM_STATE_IOC   /* Submodule is owned by an IO controller. */
+} pf_ownsm_state_values_t;
+
 /*
  * ============= Pluggable typedefs ==================
  */
 
-typedef enum pf_submod_plug_state
-{
-   PF_SUBMOD_PLUG_OK,
-   PF_SUBMOD_PLUG_SUBSTITUTE, /** module_state == SUBSTITUTE */
-   PF_SUBMOD_PLUG_WRONG,      /** module_state == WRONG_MODULE */
-   PF_SUBMOD_PLUG_NO          /** module_state == WRONG_MODULE */
-} pf_submod_plug_state_t;
-
-typedef enum pf_submod_owner
-{
-   PF_SUBMOD_OWNER_FREE,
-   PF_SUBMOD_OWNER_SOL, /* Superordinator locked */
-   PF_SUBMOD_OWNER_IOS, /* Owned by IO supervisor */
-   PF_SUBMOD_OWNER_IOC, /* Owned by IO controller */
-} pf_submod_owner;
-
-typedef enum pf_mod_plug_state
-{
-   PF_MOD_PLUG_NO_MODULE,     /** No module plugged into slot. */
-   PF_MOD_PLUG_WRONG_MODULE,  /** Module/submodule ident number(s) do not match
-                                 cfg. */
-   PF_MOD_PLUG_PROPER_MODULE, /** All OK. Submodule may have diff. */
-   PF_MOD_PLUG_SUBSTITUTE /** There is another module plugged into the slot. */
-} pf_mod_plug_state_t;
-
-typedef enum pf_submod_add_info
-{
-   PF_SUBMOD_ADD_INFO_NONE,
-   PF_SUBMOD_ADD_INFO_TAKEOVER_NOT_ALLOWED
-} pf_submod_add_info_t;
-
-typedef enum pf_submod_ar_info
-{
-   PF_SUBMOD_AR_INFO_OWN,                       /** The AR is owner */
-   PF_SUBMOD_AR_INFO_APPLICATION_READY_PENDING, /** Parameterization in progress
-                                                 */
-   PF_SUBMOD_AR_INFO_SUPERORDINATED_LOCKED, /** Locked by application (ToDo: API
-                                               for that) */
-   PF_SUBMOD_AR_INFO_LOCKED_BY_IO_CONTROLLER, /** Locked by other IO controller
-                                               */
-   PF_SUBMOD_AR_INFO_LOCKED_BY_IO_SUPERVISOR  /** Locked by other IO Supervisor
-                                               */
-} pf_submod_ar_info_t;
-
-typedef struct pf_submod_state
-{
-   pf_submod_add_info_t add_info;     /** Bits 0..2 */
-   bool advice_avail;                 /** Bit 3 */
-   bool maintenance_required;         /** Bit 4 */
-   bool maintenance_demanded;         /** Bit 5 */
-   bool fault;                        /** Bit 6 */
-   pf_submod_ar_info_t ar_info;       /** Bits 7..10 */
-   pf_submod_plug_state_t ident_info; /** Bits 11..14 */
-   bool format_indicator;             /** Bit 15: Always 1 (true) */
-} pf_submod_state_t;
-
 typedef enum pf_usi_values
 {
    /* 0x0000..0x7fff    Manufacturer specific */
@@ -2244,21 +2254,30 @@ typedef enum pf_diag_filter_level
    PF_DIAG_FILTER_M_DEM      /* Manufacturer specific or maintenance demanded */
 } pf_diag_filter_level_t;
 
+/* Real submodule diagnosis summary. */
+typedef struct pf_submod_diag_summary
+{
+   bool maintenance_required;
+   bool maintenance_demanded;
+   bool fault;
+} pf_submod_diag_summary_t;
+
+/* Real identification, subslot level. */
 typedef struct pf_subslot
 {
    bool in_use;
-   uint16_t subslot_nbr;
+   uint16_t subslot_number;
+   uint32_t ident_number;
+   pf_ar_t * owner;
+   pf_ownsm_state_values_t ownsm_state;
 
    /* Submodule plug information */
-   uint32_t exp_submodule_ident_number;
-   uint32_t submodule_ident_number;
    uint16_t length_input;
    uint16_t length_output;
    pnet_submodule_dir_t direction;
 
    /* Run-time information */
-   pf_ar_t * p_ar;
-   pf_submod_state_t submodule_state;
+   pf_submod_diag_summary_t diag_summary;
 
    /* The following members shall be protected by the device.diag_mutex. */
    /*
@@ -2270,30 +2289,29 @@ typedef struct pf_subslot
    uint16_t diag_list;
 } pf_subslot_t;
 
+/* Real identification, slot level. */
 typedef struct pf_slot
 {
    bool in_use;
-   /* Module plug information */
-   uint32_t exp_module_ident_number;
-   uint32_t module_ident_number;
-   pf_mod_plug_state_t plug_state;
+   uint16_t slot_number;
+   uint32_t ident_number;
    pf_subslot_t subslots[PNET_MAX_SUBSLOTS];
-
-   /* Run-time information */
-   pf_ar_t * p_ar;
-   uint16_t slot_nbr;
 } pf_slot_t;
 
+/* Real identification, API level. */
 typedef struct pf_api
 {
    bool in_use;
    uint32_t api_id;
-
    pf_slot_t slots[PNET_MAX_SLOTS];
-
-   pf_ar_t * p_ar;
 } pf_api_t;
 
+/* Real identification. */
+typedef struct pf_real_ident
+{
+   pf_api_t api[PNET_MAX_API];
+} pf_real_ident_t;
+
 /*
  * The device struct contains information about the configured API's.
  * The api member contains a hierarchy which may be traversed using
@@ -2301,10 +2319,7 @@ typedef struct pf_api
  */
 typedef struct pf_device
 {
-   /*
-    * Record things that are needed for the read/write record data service.
-    */
-   pf_api_t apis[PNET_MAX_API];
+   pf_real_ident_t real_ident;
 
    /*
     * This is the pool of diag items.
@@ -2392,6 +2407,15 @@ typedef struct pf_iod_write_result
    uint8_t rw_padding[16];
 } pf_iod_write_result_t;
 
+typedef enum pf_record_data_scope
+{
+   PF_RECORD_DATA_SCOPE_DEVICE,
+   PF_RECORD_DATA_SCOPE_API,
+   PF_RECORD_DATA_SCOPE_SLOT,
+   PF_RECORD_DATA_SCOPE_SUBSLOT,
+   PF_RECORD_DATA_SCOPE_AR
+} pf_record_data_scope_t;
+
 /* ============= LogBook typedefs ================== */
 /* block_version_low == 1 */
 
@@ -2940,7 +2964,9 @@ struct pnet
    uint32_t cmrpc_session_number;
 
    /** ARs */
-   pf_ar_t cmrpc_ar[PNET_MAX_AR];
+   uint16_t cmrpc_nbr_ars;
+   uint16_t cmrpc_ar_order[PNET_MAX_AR];
+   pf_ar_t cmrpc_ar[PNET_MAX_AR + 1];
 
    /** Sessions */
    pf_session_info_t cmrpc_session_info[PF_MAX_SESSION];
@@ -2951,6 +2977,14 @@ struct pnet
    uint8_t cmrpc_dcerpc_input_frame[PF_FRAME_BUFFER_SIZE];
    uint8_t cmrpc_dcerpc_output_frame[PF_FRAME_BUFFER_SIZE];
 
+   /********** ALARM *********/
+
+   struct
+   {
+      bool active;
+      pf_ar_t * ar;
+   } alarm_endpoint[PNET_MAX_AR];
+
    /********** FSPM **********/
 
    /** Default configuration from user. Used at factory reset */

+ 4 - 3
test/test_alarm.cpp

@@ -39,7 +39,8 @@ TEST_F (AlarmUnitTest, AlarmCheckAddDiagItemToSummary)
    /* Prepare default input data */
    memset (&ar, 0, sizeof (ar));
    memset (&subslot, 0, sizeof (subslot));
-   subslot.p_ar = &ar;
+   subslot.ownsm_state = PF_OWNSM_STATE_IOC;
+   subslot.owner = &ar;
 
    memset (&diag_item, 0, sizeof (diag_item));
    diag_item.usi = PF_USI_EXTENDED_CHANNEL_DIAGNOSIS;
@@ -66,7 +67,7 @@ TEST_F (AlarmUnitTest, AlarmCheckAddDiagItemToSummary)
    EXPECT_EQ (maint_status, 0UL);
 
    /* Other AR */
-   subslot.p_ar = NULL;
+   subslot.owner = NULL;
 
    memset (&alarm_specifier, 0, sizeof (alarm_specifier));
    maint_status = 0;
@@ -81,7 +82,7 @@ TEST_F (AlarmUnitTest, AlarmCheckAddDiagItemToSummary)
    EXPECT_TRUE ((bool)alarm_specifier.submodule_diagnosis);
    EXPECT_FALSE ((bool)alarm_specifier.ar_diagnosis);
    EXPECT_EQ (maint_status, 0UL);
-   subslot.p_ar = &ar;
+   subslot.owner = &ar;
 
    /* Manufacturer specific (in USI format) */
    diag_item.usi = TEST_DIAG_USI_CUSTOM;

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác