dev: new device driver infra

Type: feature
Change-Id: I20c56e0d3103624407f18365c2bc1273dea5c199
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vnet/dev/cli.c b/src/vnet/dev/cli.c
new file mode 100644
index 0000000..d478f1d
--- /dev/null
+++ b/src/vnet/dev/cli.c
@@ -0,0 +1,315 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) 2023 Cisco Systems, Inc.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/dev/dev.h>
+#include <vnet/dev/counters.h>
+#include <vnet/dev/api.h>
+
+static clib_error_t *
+device_attach_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+		      vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_attach_args_t a = {};
+  vnet_dev_rv_t rv;
+
+  if (!unformat_user (input, unformat_c_string_array, a.device_id,
+		      sizeof (a.device_id)))
+    return clib_error_return (0, "please specify valid device id");
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (!a.driver_name[0] &&
+	  unformat (input, "driver %U", unformat_c_string_array, a.driver_name,
+		    sizeof (a.driver_name)))
+	;
+      else if (!a.flags.n &&
+	       unformat (input, "flags %U", unformat_vnet_dev_flags, &a.flags))
+	;
+      else if (!a.args && unformat (input, "args %v", &a.args))
+	;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  rv = vnet_dev_api_attach (vm, &a);
+
+  vec_free (a.args);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to attach '%s': %U", a.device_id,
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_attach_cmd, static) = {
+  .path = "device attach",
+  .short_help = "device attach <device-id> [driver <name>] "
+		"[args <dev-args>]",
+  .function = device_attach_cmd_fn,
+};
+
+static clib_error_t *
+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;
+
+  if (!unformat_user (input, unformat_c_string_array, a.device_id,
+		      sizeof (a.device_id)))
+    return clib_error_return (0, "please specify valid device id");
+
+  rv = vnet_dev_api_detach (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to detach '%s': %U", a.device_id,
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_detach_cmd, static) = {
+  .path = "device detach",
+  .short_help = "device detach <device-id>",
+  .function = device_detach_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+device_reset_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+		     vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_reset_args_t a = {};
+  vnet_dev_rv_t rv;
+
+  if (!unformat_user (input, unformat_c_string_array, a.device_id,
+		      sizeof (a.device_id)))
+    return clib_error_return (0, "please specify valid device id");
+
+  rv = vnet_dev_api_reset (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to reset '%s': %U", a.device_id,
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_reset_cmd, static) = {
+  .path = "device reset",
+  .short_help = "device reset <device-id>",
+  .function = device_reset_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+device_create_if_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+			 vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_create_port_if_args_t a = {};
+  vnet_dev_rv_t rv;
+  u32 n;
+
+  if (!unformat_user (input, unformat_c_string_array, a.device_id,
+		      sizeof (a.device_id)))
+    return clib_error_return (0, "please specify valid device id");
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (!a.intf_name[0] &&
+	  unformat (input, "if-name %U", unformat_c_string_array, a.intf_name,
+		    sizeof (a.intf_name)))
+	;
+      else if (!a.port_id && unformat (input, "port %u", &n))
+	a.port_id = n;
+      else if (!a.flags.n && unformat (input, "flags %U",
+				       unformat_vnet_dev_port_flags, &a.flags))
+	;
+      else if (!a.num_rx_queues && unformat (input, "num-rx-queues %u", &n))
+	a.num_rx_queues = n;
+      else if (!a.num_tx_queues && unformat (input, "num-tx-queues %u", &n))
+	a.num_tx_queues = n;
+      else if (!a.rx_queue_size && unformat (input, "rx-queues-size %u", &n))
+	a.rx_queue_size = n;
+      else if (!a.tx_queue_size && unformat (input, "tx-queues-size %u", &n))
+	a.tx_queue_size = n;
+      else if (!a.intf_name[0] &&
+	       unformat (input, "name %U", unformat_c_string_array,
+			 &a.intf_name, sizeof (a.intf_name)))
+	;
+      else if (!a.args && unformat (input, "args %v", &a.args))
+	;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  rv = vnet_dev_api_create_port_if (vm, &a);
+
+  vec_free (a.args);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to create_if '%s': %U", a.device_id,
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_create_if_cmd, static) = {
+  .path = "device create-interface",
+  .short_help = "device create-interface <device-id> [port <port-id>] "
+		"[args <iface-args>]",
+  .function = device_create_if_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+device_remove_if_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+			 vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_remove_port_if_args_t a = { .sw_if_index = ~0 };
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_rv_t rv;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
+		    &a.sw_if_index))
+	;
+      else if (unformat (input, "sw-if-index %u", &a.sw_if_index))
+	;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  if (a.sw_if_index == ~0)
+    return clib_error_return (0, "please specify existing interface name");
+
+  rv = vnet_dev_api_remove_port_if (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to remove interface: %U",
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_remove_if_cmd, static) = {
+  .path = "device remove-interface",
+  .short_help = "device remove-interface [<interface-name> | sw-if-index <n>]",
+  .function = device_remove_if_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+show_devices_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+		     vlib_cli_command_t *cmd)
+{
+  vnet_dev_main_t *dm = &vnet_dev_main;
+  vnet_dev_format_args_t fa = {}, *a = &fa;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "counters"))
+	fa.counters = 1;
+      else if (unformat (input, "all"))
+	fa.show_zero_counters = 1;
+      else if (unformat (input, "debug"))
+	fa.debug = 1;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  pool_foreach_pointer (dev, dm->devices)
+    {
+      vlib_cli_output (vm, "device '%s':", dev->device_id);
+      vlib_cli_output (vm, "  %U", format_vnet_dev_info, a, dev);
+      foreach_vnet_dev_port (p, dev)
+	{
+	  vlib_cli_output (vm, "  Port %u:", p->port_id);
+	  vlib_cli_output (vm, "    %U", format_vnet_dev_port_info, a, p);
+	  if (fa.counters)
+	    vlib_cli_output (vm, "    %U", format_vnet_dev_counters, a,
+			     p->counter_main);
+
+	  foreach_vnet_dev_port_rx_queue (q, p)
+	    {
+	      vlib_cli_output (vm, "    RX queue %u:", q->queue_id);
+	      vlib_cli_output (vm, "      %U", format_vnet_dev_rx_queue_info,
+			       a, q);
+	    }
+
+	  foreach_vnet_dev_port_tx_queue (q, p)
+	    {
+	      vlib_cli_output (vm, "    TX queue %u:", q->queue_id);
+	      vlib_cli_output (vm, "      %U", format_vnet_dev_tx_queue_info,
+			       a, q);
+	    }
+	}
+    }
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_devices_cmd, static) = {
+  .path = "show device",
+  .short_help = "show device [counters]",
+  .function = show_devices_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+show_device_counters_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+			     vlib_cli_command_t *cmd)
+{
+  vnet_dev_main_t *dm = &vnet_dev_main;
+  vnet_dev_format_args_t fa = { .counters = 1 };
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "all"))
+	fa.show_zero_counters = 1;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  pool_foreach_pointer (dev, dm->devices)
+    {
+      vlib_cli_output (vm, "device '%s':", dev->device_id);
+      foreach_vnet_dev_port (p, dev)
+	{
+	  vlib_cli_output (vm, "    %U", format_vnet_dev_counters, &fa,
+			   p->counter_main);
+
+	  foreach_vnet_dev_port_rx_queue (q, p)
+	    if (q->counter_main)
+	      {
+		vlib_cli_output (vm, "  RX queue %u:", q->queue_id);
+		vlib_cli_output (vm, "    %U", format_vnet_dev_counters, &fa,
+				 q->counter_main);
+	      }
+
+	  foreach_vnet_dev_port_tx_queue (q, p)
+	    if (q->counter_main)
+	      {
+		vlib_cli_output (vm, "  TX queue %u:", q->queue_id);
+		vlib_cli_output (vm, "    %U", format_vnet_dev_counters, &fa,
+				 q->counter_main);
+	      }
+	}
+    }
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_device_counters_cmd, static) = {
+  .path = "show device counters",
+  .short_help = "show device counters [all]",
+  .function = show_device_counters_cmd_fn,
+  .is_mp_safe = 1,
+};