blob: 56a1f9c762c5c2eaaaddc2b23359a6f92417dcd6 [file] [log] [blame]
/* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2024 Cisco Systems, Inc.
*/
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
#include <vnet/dev/bus/platform.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
.class_name = "dev",
.subclass_name = "platform",
};
#define log_debug(dev, f, ...) \
vlib_log (VLIB_LOG_LEVEL_DEBUG, dev_log.class, "%U" f, format_vnet_dev_log, \
dev, \
clib_string_skip_prefix (__func__, "vnet_dev_bus_platform_dt_"), \
##__VA_ARGS__)
#define log_err(dev, f, ...) \
vlib_log (VLIB_LOG_LEVEL_ERR, dev_log.class, "%U" f, format_vnet_dev_log, \
dev, 0, ##__VA_ARGS__)
#define PLATFORM_DEV_PATH "/sys/bus/platform/devices"
clib_dt_main_t vnet_dev_bus_platform_dt_main;
vnet_dev_rv_t
vnet_dev_bus_platform_dt_node_from_device_id (clib_dt_node_t **nodep,
char *device_id)
{
clib_dt_main_t *dm = &vnet_dev_bus_platform_dt_main;
clib_dt_node_t *n;
char *name = device_id + sizeof (PLATFORM_BUS_NAME);
char path[PATH_MAX];
int r;
u8 *link;
if (dm->root == 0)
{
clib_error_t *err;
err = clib_dt_read_from_sysfs (&vnet_dev_bus_platform_dt_main);
if (err)
{
log_err (0, "cannot read devicetree: %U", format_clib_error, err);
clib_error_free (err);
return VNET_DEV_ERR_NOT_FOUND;
}
}
link = format (0, PLATFORM_DEV_PATH "/%s/of_node%c", name, 0);
r = readlink ((char *) link, path, sizeof (path) - 1);
if (r < 1)
{
log_err (0, "of_node doesn't exist for '%s'", name);
vec_free (link);
return VNET_DEV_ERR_NOT_FOUND;
}
path[r] = 0;
vec_reset_length (link);
link = format (link, PLATFORM_DEV_PATH "/%s/%s%c", name, path, 0);
if (!realpath ((char *) link, path))
{
log_err (0, "cannot find realpath for '%s'", link);
vec_free (link);
return VNET_DEV_ERR_NOT_FOUND;
}
vec_free (link);
if (strncmp (CLIB_DT_LINUX_PREFIX, path,
sizeof (CLIB_DT_LINUX_PREFIX) - 1) != 0)
return VNET_DEV_ERR_BUG;
n = clib_dt_get_node_with_path (dm, "%s",
path + sizeof (CLIB_DT_LINUX_PREFIX) - 1);
if (n)
{
*nodep = n;
return VNET_DEV_OK;
}
return VNET_DEV_ERR_NOT_FOUND;
}
static void *
vnet_dev_bus_platform_get_device_info (vlib_main_t *vm, char *device_id)
{
clib_dt_node_t *n = 0;
vnet_dev_bus_platform_device_info_t *di;
vnet_dev_bus_platform_dt_node_from_device_id (&n, device_id);
if (n)
{
clib_dt_property_t *compatible;
compatible = clib_dt_get_node_property_by_name (n, "compatible");
log_debug (0, "node found, is compatible %U",
format_clib_dt_property_data, compatible);
di = clib_mem_alloc (sizeof (*di));
di->node = n;
return di;
}
return 0;
}
static void
vnet_dev_bus_platform_free_device_info (vlib_main_t *vm, void *p)
{
clib_mem_free (p);
}
static void
vnet_dev_bus_platform_close (vlib_main_t *vm, vnet_dev_t *dev)
{
log_debug (dev, "");
}
static vnet_dev_rv_t
vnet_dev_bus_platform_open (vlib_main_t *vm, vnet_dev_t *dev)
{
clib_dt_node_t *n = 0;
vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev);
vnet_dev_rv_t rv;
log_debug (dev, "");
rv = vnet_dev_bus_platform_dt_node_from_device_id (&n, dev->device_id);
if (rv != VNET_DEV_OK)
return rv;
dd->node = n;
return VNET_DEV_OK;
}
static u8 *
format_dev_bus_platform_device_info (u8 *s, va_list *args)
{
vnet_dev_format_args_t __clib_unused *a =
va_arg (*args, vnet_dev_format_args_t *);
vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev);
return format (s, "device-tree path is '%v'", dd->node->path);
}
static u8 *
format_dev_bus_platform_device_addr (u8 *s, va_list *args)
{
vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
return format (s, "%s", dev->device_id + sizeof (PLATFORM_BUS_NAME));
}
VNET_DEV_REGISTER_BUS (pp2) = {
.name = PLATFORM_BUS_NAME,
.device_data_size = sizeof (vnet_dev_bus_platform_device_info_t),
.ops = {
.get_device_info = vnet_dev_bus_platform_get_device_info,
.free_device_info = vnet_dev_bus_platform_free_device_info,
.device_open = vnet_dev_bus_platform_open,
.device_close = vnet_dev_bus_platform_close,
.format_device_info = format_dev_bus_platform_device_info,
.format_device_addr = format_dev_bus_platform_device_addr,
},
};