dev: initial set of APIs

Type: improvement
Change-Id: I9ecbf705d460a1744f36c7005b08097dc58d9522
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index 1e4bb15..3225540 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -32,6 +32,7 @@
   dev/config.c
   dev/counters.c
   dev/dev.c
+  dev/dev_api.c
   dev/error.c
   dev/format.c
   dev/handlers.c
@@ -98,6 +99,7 @@
 )
 
 list(APPEND VNET_API_FILES
+  dev/dev.api
   interface.api
   interface_types.api
   ip/ip_types.api
diff --git a/src/vnet/dev/api.c b/src/vnet/dev/api.c
index 72811f5..114b63d 100644
--- a/src/vnet/dev/api.c
+++ b/src/vnet/dev/api.c
@@ -115,6 +115,8 @@
 
   if (rv != VNET_DEV_OK && dev)
     vnet_dev_process_call_op_no_rv (vm, dev, vnet_dev_free);
+  else if (dev)
+    args->dev_index = dev->index;
 
   return rv;
 }
@@ -122,7 +124,7 @@
 vnet_dev_rv_t
 vnet_dev_api_detach (vlib_main_t *vm, vnet_dev_api_detach_args_t *args)
 {
-  vnet_dev_t *dev = vnet_dev_by_id (args->device_id);
+  vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
 
   log_debug (dev, "detach");
 
@@ -152,16 +154,16 @@
 vnet_dev_api_create_port_if (vlib_main_t *vm,
 			     vnet_dev_api_create_port_if_args_t *args)
 {
-  vnet_dev_t *dev = vnet_dev_by_id (args->device_id);
+  vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
   vnet_dev_port_t *port = 0;
   u16 n_threads = vlib_get_n_threads ();
   int default_is_intr_mode;
   vnet_dev_rv_t rv;
 
   log_debug (dev,
-	     "create_port_if: device '%s' port %u intf_name '%s' num_rx_q %u "
+	     "create_port_if: dev_index %u port %u intf_name '%s' num_rx_q %u "
 	     "num_tx_q %u rx_q_sz %u tx_q_sz %u, flags '%U' args '%v'",
-	     args->device_id, args->port_id, args->intf_name,
+	     args->dev_index, args->port_id, args->intf_name,
 	     args->num_rx_queues, args->num_tx_queues, args->rx_queue_size,
 	     args->tx_queue_size, format_vnet_dev_port_flags, &args->flags,
 	     args->args);
@@ -237,7 +239,10 @@
   clib_memcpy (port->intf.name, args->intf_name, sizeof (port->intf.name));
   port->intf.default_is_intr_mode = default_is_intr_mode;
 
-  return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_create);
+  rv = vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_create);
+  args->sw_if_index = (rv == VNET_DEV_OK) ? port->intf.sw_if_index : ~0;
+
+  return rv;
 }
 
 vnet_dev_rv_t
diff --git a/src/vnet/dev/api.h b/src/vnet/dev/api.h
index 69a8462..1b7bf27 100644
--- a/src/vnet/dev/api.h
+++ b/src/vnet/dev/api.h
@@ -15,6 +15,9 @@
   vnet_dev_driver_name_t driver_name;
   vnet_dev_flags_t flags;
   u8 *args;
+
+  /* return */
+  u32 dev_index;
 } vnet_dev_api_attach_args_t;
 
 vnet_dev_rv_t vnet_dev_api_attach (vlib_main_t *,
@@ -22,7 +25,7 @@
 
 typedef struct
 {
-  vnet_dev_device_id_t device_id;
+  u32 dev_index;
 } vnet_dev_api_detach_args_t;
 vnet_dev_rv_t vnet_dev_api_detach (vlib_main_t *,
 				   vnet_dev_api_detach_args_t *);
@@ -35,7 +38,7 @@
 
 typedef struct
 {
-  vnet_dev_device_id_t device_id;
+  u32 dev_index;
   vnet_dev_if_name_t intf_name;
   u16 num_rx_queues;
   u16 num_tx_queues;
@@ -44,6 +47,9 @@
   vnet_dev_port_id_t port_id;
   vnet_dev_port_flags_t flags;
   u8 *args;
+
+  /* return */
+  u32 sw_if_index;
 } vnet_dev_api_create_port_if_args_t;
 
 vnet_dev_rv_t
diff --git a/src/vnet/dev/cli.c b/src/vnet/dev/cli.c
index d478f1d..53be448 100644
--- a/src/vnet/dev/cli.c
+++ b/src/vnet/dev/cli.c
@@ -56,17 +56,26 @@
 device_detach_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
 		      vlib_cli_command_t *cmd)
 {
-  vnet_dev_api_detach_args_t a = {};
   vnet_dev_rv_t rv;
+  vnet_dev_device_id_t device_id = {};
+  vnet_dev_t *dev;
 
-  if (!unformat_user (input, unformat_c_string_array, a.device_id,
-		      sizeof (a.device_id)))
+  if (!unformat_user (input, unformat_c_string_array, device_id,
+		      sizeof (device_id)))
     return clib_error_return (0, "please specify valid device id");
 
-  rv = vnet_dev_api_detach (vm, &a);
+  dev = vnet_dev_by_id (device_id);
+
+  if (dev)
+    {
+      vnet_dev_api_detach_args_t a = { .dev_index = dev->index };
+      rv = vnet_dev_api_detach (vm, &a);
+    }
+  else
+    rv = VNET_DEV_ERR_UNKNOWN_DEVICE;
 
   if (rv != VNET_DEV_OK)
-    return clib_error_return (0, "unable to detach '%s': %U", a.device_id,
+    return clib_error_return (0, "unable to detach '%s': %U", device_id,
 			      format_vnet_dev_rv, rv);
 
   return 0;
@@ -112,12 +121,19 @@
 {
   vnet_dev_api_create_port_if_args_t a = {};
   vnet_dev_rv_t rv;
+  vnet_dev_device_id_t device_id = {};
+  vnet_dev_t *dev = 0;
   u32 n;
 
-  if (!unformat_user (input, unformat_c_string_array, a.device_id,
-		      sizeof (a.device_id)))
+  if (unformat_user (input, unformat_c_string_array, device_id,
+		     sizeof (device_id)))
+    dev = vnet_dev_by_id (device_id);
+
+  if (!dev)
     return clib_error_return (0, "please specify valid device id");
 
+  a.dev_index = dev->index;
+
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
       if (!a.intf_name[0] &&
@@ -153,7 +169,7 @@
   vec_free (a.args);
 
   if (rv != VNET_DEV_OK)
-    return clib_error_return (0, "unable to create_if '%s': %U", a.device_id,
+    return clib_error_return (0, "unable to create_if '%s': %U", device_id,
 			      format_vnet_dev_rv, rv);
 
   return 0;
diff --git a/src/vnet/dev/config.c b/src/vnet/dev/config.c
index 6523871..8883e72 100644
--- a/src/vnet/dev/config.c
+++ b/src/vnet/dev/config.c
@@ -107,8 +107,7 @@
 	{
 	  vec_foreach (if_args, if_args_vec)
 	    {
-	      clib_memcpy (if_args->device_id, device_id,
-			   sizeof (if_args->device_id));
+	      if_args->dev_index = args.dev_index;
 	      rv = vnet_dev_api_create_port_if (vm, if_args);
 	      if (rv != VNET_DEV_OK)
 		break;
diff --git a/src/vnet/dev/dev.api b/src/vnet/dev/dev.api
new file mode 100644
index 0000000..552b778
--- /dev/null
+++ b/src/vnet/dev/dev.api
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2022 Cisco Systems, Inc.
+ */
+
+option version = "0.0.1";
+
+enumflag dev_flags : u32
+{
+  VL_API_DEV_FLAG_NO_STATS = 0x1,
+};
+
+enumflag dev_port_flags : u32
+{
+  VL_API_DEV_PORT_FLAG_INTERRUPT_MODE = 0x1,
+};
+
+autoendian define dev_attach
+{
+  u32 client_index;
+  u32 context;
+  string device_id[48];
+  string driver_name[16];
+  vl_api_dev_flags_t flags;
+  string args[];
+};
+
+autoendian define dev_attach_reply
+{
+  u32 context;
+  u32 dev_index;
+  i32 retval;
+  string error_string[];
+};
+
+autoendian define dev_detach
+{
+  u32 client_index;
+  u32 context;
+  u32 dev_index;
+};
+
+autoendian define dev_detach_reply
+{
+  u32 context;
+  i32 retval;
+  string error_string[];
+};
+
+autoendian define dev_create_port_if
+{
+  u32 client_index;
+  u32 context;
+  u32 dev_index;
+  string intf_name[32];
+  u16 num_rx_queues;
+  u16 num_tx_queues;
+  u16 rx_queue_size;
+  u16 tx_queue_size;
+  u16 port_id;
+  vl_api_dev_port_flags_t flags;
+  string args[];
+};
+
+autoendian define dev_create_port_if_reply
+{
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+  i32 retval;
+  string error_string[];
+};
+
+autoendian define dev_remove_port_if
+{
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+};
+
+autoendian define dev_remove_port_if_reply
+{
+  u32 context;
+  i32 retval;
+  string error_string[];
+};
+
diff --git a/src/vnet/dev/dev.h b/src/vnet/dev/dev.h
index c18d29a..59e1003 100644
--- a/src/vnet/dev/dev.h
+++ b/src/vnet/dev/dev.h
@@ -18,8 +18,6 @@
   _ (0, UNKNOWN)                                                              \
   _ (1, ETHERNET)
 
-typedef char vnet_dev_device_id_t[32];
-
 typedef enum
 {
 #define _(b, n) VNET_DEV_PORT_TYPE_##n = (1U << (b)),
diff --git a/src/vnet/dev/dev_api.c b/src/vnet/dev/dev_api.c
new file mode 100644
index 0000000..4161c32
--- /dev/null
+++ b/src/vnet/dev/dev_api.c
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/api.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <dev/dev.api_enum.h>
+#include <dev/dev.api_types.h>
+
+static u16 vnet_dev_api_msg_id_base;
+
+#define REPLY_MSG_ID_BASE (vnet_dev_api_msg_id_base)
+#include <vlibapi/api_helper_macros.h>
+
+#define _(b, n, d)                                                            \
+  STATIC_ASSERT ((int) VL_API_DEV_FLAG_##n == (int) VNET_DEV_F_##n, "");
+foreach_vnet_dev_flag;
+#undef _
+
+#define _(b, n, d)                                                            \
+  STATIC_ASSERT ((int) VL_API_DEV_PORT_FLAG_##n == (int) VNET_DEV_PORT_F_##n, \
+		 "");
+foreach_vnet_dev_port_flag;
+#undef _
+
+static void
+vl_api_dev_attach_t_handler (vl_api_dev_attach_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_dev_attach_reply_t *rmp;
+  vnet_dev_api_attach_args_t a = {};
+  vnet_dev_rv_t rv;
+  u8 *error_string = 0;
+
+  STATIC_ASSERT (sizeof (mp->device_id) == sizeof (a.device_id), "");
+  STATIC_ASSERT (sizeof (mp->driver_name) == sizeof (a.driver_name), "");
+  STATIC_ASSERT (sizeof (mp->flags) == sizeof (a.flags), "");
+
+  a.flags.n = mp->flags;
+  strncpy (a.device_id, (char *) mp->device_id, sizeof (a.device_id));
+  strncpy (a.driver_name, (char *) mp->driver_name, sizeof (a.driver_name));
+  vec_add (a.args, mp->args.buf, mp->args.length);
+
+  rv = vnet_dev_api_attach (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    error_string = format (0, "%U", format_vnet_dev_rv, rv);
+
+  vec_free (a.args);
+
+  REPLY_MACRO3 (VL_API_DEV_ATTACH_REPLY, vec_len (error_string), ({
+		  rmp->retval = rv;
+		  if (error_string)
+		    {
+		      rmp->dev_index = ~0;
+		      vl_api_vec_to_api_string (error_string,
+						&rmp->error_string);
+		    }
+		  else
+		    rmp->dev_index = a.dev_index;
+		}));
+
+  vec_free (a.args);
+  vec_free (error_string);
+}
+
+static void
+vl_api_dev_detach_t_handler (vl_api_dev_detach_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_dev_detach_reply_t *rmp;
+  vnet_dev_api_detach_args_t a = {};
+  vnet_dev_rv_t rv;
+  u8 *error_string = 0;
+
+  a.dev_index = mp->dev_index;
+
+  rv = vnet_dev_api_detach (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    error_string = format (0, "%U", format_vnet_dev_rv, rv);
+
+  REPLY_MACRO3 (VL_API_DEV_DETACH_REPLY, vec_len (error_string), ({
+		  rmp->retval = rv;
+		  if (error_string)
+		    vl_api_vec_to_api_string (error_string,
+					      &rmp->error_string);
+		}));
+
+  vec_free (error_string);
+}
+
+static void
+vl_api_dev_create_port_if_t_handler (vl_api_dev_create_port_if_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_dev_create_port_if_reply_t *rmp;
+  vnet_dev_api_create_port_if_args_t a = {};
+  vnet_dev_rv_t rv;
+  u8 *error_string = 0;
+
+  STATIC_ASSERT (sizeof (mp->intf_name) == sizeof (a.intf_name), "");
+  STATIC_ASSERT (sizeof (mp->flags) == sizeof (a.flags), "");
+
+  a.flags.n = mp->flags;
+#define _(n) a.n = mp->n;
+  _ (dev_index)
+  _ (port_id)
+  _ (num_rx_queues)
+  _ (num_tx_queues)
+  _ (rx_queue_size)
+  _ (tx_queue_size)
+#undef _
+
+  strncpy (a.intf_name, (char *) mp->intf_name, sizeof (a.intf_name));
+  vec_add (a.args, mp->args.buf, mp->args.length);
+
+  rv = vnet_dev_api_create_port_if (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    error_string = format (0, "%U", format_vnet_dev_rv, rv);
+
+  vec_free (a.args);
+
+  REPLY_MACRO3 (VL_API_DEV_CREATE_PORT_IF_REPLY, vec_len (error_string), ({
+		  rmp->retval = rv;
+		  if (error_string)
+		    {
+		      rmp->sw_if_index = ~0;
+		      vl_api_vec_to_api_string (error_string,
+						&rmp->error_string);
+		    }
+		  else
+		    rmp->sw_if_index = a.sw_if_index;
+		}));
+
+  vec_free (a.args);
+  vec_free (error_string);
+}
+
+static void
+vl_api_dev_remove_port_if_t_handler (vl_api_dev_remove_port_if_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_dev_remove_port_if_reply_t *rmp;
+  vnet_dev_api_remove_port_if_args_t a = {};
+  vnet_dev_rv_t rv;
+  u8 *error_string = 0;
+
+  a.sw_if_index = mp->sw_if_index;
+
+  rv = vnet_dev_api_remove_port_if (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    error_string = format (0, "%U", format_vnet_dev_rv, rv);
+
+  REPLY_MACRO3 (VL_API_DEV_REMOVE_PORT_IF_REPLY, vec_len (error_string), ({
+		  rmp->retval = rv;
+		  if (error_string)
+		    vl_api_vec_to_api_string (error_string,
+					      &rmp->error_string);
+		}));
+
+  vec_free (error_string);
+}
+
+/* set tup the API message handling tables */
+
+#include <dev/dev.api.c>
+
+static clib_error_t *
+vnet_dev_api_hookup (vlib_main_t *vm)
+{
+  api_main_t *am = vlibapi_get_main ();
+
+  /* ask for a correctly-sized block of API message decode slots */
+  vnet_dev_api_msg_id_base = setup_message_id_table ();
+
+  vl_api_set_msg_thread_safe (am, vnet_dev_api_msg_id_base + VL_API_DEV_ATTACH,
+			      1);
+
+  return 0;
+}
+
+VLIB_API_INIT_FUNCTION (vnet_dev_api_hookup);
diff --git a/src/vnet/dev/dev_funcs.h b/src/vnet/dev/dev_funcs.h
index a74d339..521157a 100644
--- a/src/vnet/dev/dev_funcs.h
+++ b/src/vnet/dev/dev_funcs.h
@@ -75,6 +75,16 @@
 }
 
 static_always_inline vnet_dev_t *
+vnet_dev_by_index (u32 index)
+{
+  vnet_dev_main_t *dm = &vnet_dev_main;
+  if (pool_is_free_index (dm->devices, index))
+    return 0;
+
+  return *pool_elt_at_index (dm->devices, index);
+}
+
+static_always_inline vnet_dev_t *
 vnet_dev_by_id (char *id)
 {
   vnet_dev_main_t *dm = &vnet_dev_main;
diff --git a/src/vnet/dev/errors.h b/src/vnet/dev/errors.h
index 1f45ce2..47e7295 100644
--- a/src/vnet/dev/errors.h
+++ b/src/vnet/dev/errors.h
@@ -35,6 +35,7 @@
   _ (PROCESS_REPLY, "dev process reply error")                                \
   _ (RESOURCE_NOT_AVAILABLE, "resource not available")                        \
   _ (TIMEOUT, "timeout")                                                      \
+  _ (UNKNOWN_DEVICE, "unknown device")                                        \
   _ (UNKNOWN_INTERFACE, "unknown interface")                                  \
   _ (UNSUPPORTED_CONFIG, "unsupported config")                                \
   _ (UNSUPPORTED_DEVICE, "unsupported device")                                \
diff --git a/src/vnet/dev/types.h b/src/vnet/dev/types.h
index 1a82c97..006d18e 100644
--- a/src/vnet/dev/types.h
+++ b/src/vnet/dev/types.h
@@ -8,7 +8,7 @@
 #include <vppinfra/types.h>
 #include <vnet/dev/errors.h>
 
-typedef char vnet_dev_device_id_t[32];
+typedef char vnet_dev_device_id_t[48];
 typedef char vnet_dev_if_name_t[32];
 typedef char vnet_dev_driver_name_t[16];
 typedef char vnet_dev_bus_name_t[8];
@@ -35,7 +35,7 @@
 } vnet_dev_rv_t;
 
 /* do not change bit assignments - API dependency */
-#define foreach_vnet_dev_flag _ (3, NO_STATS, "don't poll device stats")
+#define foreach_vnet_dev_flag _ (0, NO_STATS, "don't poll device stats")
 
 typedef union
 {
@@ -45,12 +45,12 @@
     foreach_vnet_dev_flag
 #undef _
   } e;
-  u64 n;
+  u32 n;
 } vnet_dev_flags_t;
 
 /* do not change bit assignments - API dependency */
 #define foreach_vnet_dev_port_flag                                            \
-  _ (3, INTERRUPT_MODE, "enable interrupt mode")
+  _ (0, INTERRUPT_MODE, "enable interrupt mode")
 
 typedef union
 {
@@ -60,7 +60,7 @@
     foreach_vnet_dev_port_flag
 #undef _
   } e;
-  u64 n;
+  u32 n;
 } vnet_dev_port_flags_t;
 
 #endif /* _VNET_DEV_TYPES_H_ */