| /* 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, |
| }, |
| }; |