From: Juergen Borleis <j...@pengutronix.de>

Add of_find_path() support which mimics the of_find_path() from barebox.
The purpose of this function is to get the device path specified by a
device-tree property like:

   - proptery = &mmc2, partname:0;
   - property = &of_partition;

Signed-off-by: Juergen Borleis <j...@pengutronix.de>
[m.fel...@pengutronix.de: adapt commit message]
[m.fel...@pengutronix.de: fix of-partition use-case]
[m.fel...@pengutronix.de: use the correct udev_list_entry variable]
[m.fel...@pengutronix.de: of_find_path function behaviour with barebox]
[m.fel...@pengutronix.de: drop partuuid suport since barbeox don't support ot 
either]
Signed-off-by: Marco Felsch <m.fel...@pengutronix.de>
---
 src/dt/dt.h         |   2 +
 src/libdt-utils.sym |   1 +
 src/libdt.c         | 224 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+)

diff --git a/src/dt/dt.h b/src/dt/dt.h
index 97dd855..7c27259 100644
--- a/src/dt/dt.h
+++ b/src/dt/dt.h
@@ -229,6 +229,8 @@ void of_add_memory_bank(struct device_node *node, bool 
dump, int r,
 
 int of_get_devicepath(struct device_node *partition_node, char **devnode, 
off_t *offset,
                size_t *size);
+int of_find_path(struct device_node *node, const char *propname, char 
**devpath,
+                off_t *offset, size_t *size);
 
 #define for_each_node_by_name(dn, name) \
        for (dn = of_find_node_by_name(NULL, name); dn; \
diff --git a/src/libdt-utils.sym b/src/libdt-utils.sym
index a749317..441c8ef 100644
--- a/src/libdt-utils.sym
+++ b/src/libdt-utils.sym
@@ -34,6 +34,7 @@ global:
        of_get_child_by_name;
        of_get_child_count;
        of_get_devicepath;
+       of_find_path;
        of_get_next_available_child;
        of_get_parent;
        of_get_property;
diff --git a/src/libdt.c b/src/libdt.c
index f18ea90..89ef501 100644
--- a/src/libdt.c
+++ b/src/libdt.c
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <libudev.h>
+#include <limits.h>
 #include <dt.h>
 
 static int pr_level = 5;
@@ -2234,6 +2235,137 @@ out:
        return ret;
 }
 
+/**
+ * libdt_udev_scan_restrict - Restrict a udev scan to a specific device
+ * @e:         The scan enumerater to restrict to a specific device
+ * @specifier: Describes the device in question in more detail
+ *
+ * Since property matches are or'ed, we can only add exactly one match based on
+ * a specific property value.
+ *
+ * Note: this function isn't thread save
+ */
+static int libdt_udev_scan_restrict(struct udev_enumerate *e, const char 
*specifier)
+{
+       static char part_number_match[16];
+       unsigned long int part_no;
+       char *eptr;
+
+       /*
+        * The variables: 'PARTN' (for <number>) or 'PARTNAME' (for <name>) can
+        * be queried by:
+        *   udevadm info -q all /dev/<partition device node>
+        */
+
+       /* "partname:<name>" use case */
+       if (!isdigit(*specifier))
+               return udev_enumerate_add_match_property(e, "PARTNAME", 
specifier);
+
+       /*
+        * "partname:<number>" use case
+        * It's a little bit more complicated, since the bootloader
+        * counts beginning with '0', while userland counts
+        * beginning with '1'. Thus, we need to increase the partition
+        * number by one here - and we need it as a string.
+        */
+       part_no = strtoul(specifier, &eptr, 10);
+       if (part_no == 0 && specifier == eptr) {
+               fprintf(stderr, "Numerical partition count value expected - but 
found invalid value: '%s'\n", specifier);
+               return -EINVAL;
+       }
+       if (part_no == ULONG_MAX && errno == ERANGE) {
+               fprintf(stderr, "Numerical partition count expected - but found 
invalid number: '%s'\n", specifier);
+               return -ERANGE;
+       }
+
+       snprintf(part_number_match, sizeof(part_number_match), "%ld", part_no + 
1);
+       udev_enumerate_add_match_property(e, "PARTN", part_number_match);
+       return 0;
+}
+
+/**
+ * device_find_blockdev_partition - Retrieve the device node path to a block 
device
+ *                                 (which can be a of-partition as well)
+ * @parent:            The base block device or of-partition
+ * @part_specifier:    Specifier to define a unique partition on @parent
+ * @devpath:           Return the corresponding path to the device node
+ *                     (e.g. /dev/<something>)
+ *
+ * Returns 0 on success (e.g. @devpath is valid) or a negative errno else
+ *
+ * @part_specifier is a string and can be one of:
+ * - "partname:<number>": restrict the scan to a partition with index <number> 
+ 1
+ *   - can be used in conjuntion with MBR or GPT partitions
+ * - "partname:<name>": restrict the scan to a partition with name <name>
+ *   - can be used in conjuntion with GPT partitions
+ */
+static int device_find_blockdev_partition(struct udev_device *parent, const 
char *part_specifier, char **devpath)
+{
+       struct udev_list_entry *device_list, *dev_entry;
+       struct udev_enumerate *enumerate;
+       struct udev_device *part = NULL;
+       const char *devnode_path;
+       const char *sys_path;
+       struct udev *udev;
+       int ret;
+
+       udev = udev_new();
+       if (!udev) {
+                 fprintf(stderr, "Failed to create udev context when 
retrieving block device nodes\n");
+                 return -ENODEV;
+       }
+       /*
+        * Create an enumation context to match exactly one device
+        * Note: the following matches are and'ed (one per group)
+        */
+       enumerate = udev_enumerate_new(udev);
+
+       /* We are interested only in devices related to the given parent device 
*/
+       udev_enumerate_add_match_parent(enumerate, parent);
+       /* We are interested only in subsystem 'block' devices */
+       udev_enumerate_add_match_subsystem(enumerate, "block");
+       /* We are interested in a unique ID */
+       ret = libdt_udev_scan_restrict(enumerate, part_specifier);
+       if (ret)
+               goto leave;
+
+       udev_enumerate_scan_devices(enumerate);
+
+       /* Note: this list should contain none or max *one* device */
+       device_list = udev_enumerate_get_list_entry(enumerate);
+       if (device_list == NULL) {
+               fprintf(stderr, "No device matches the partition specifier: 
'%s'\n", part_specifier);
+               ret = -ENODEV;
+               goto leave;
+       }
+
+       sys_path = udev_list_entry_get_name(device_list);
+       part = udev_device_new_from_syspath(udev, sys_path);
+       devnode_path = udev_device_get_devnode(part);
+
+       /* Ensure only one match is in the list */
+       dev_entry = udev_list_entry_get_next(device_list);
+       if (dev_entry == NULL) {
+               *devpath = strdup(devnode_path);
+               ret = 0;
+       } else {
+               fprintf(stderr, "Unexpected behaviour of udev matches: a single 
partition device node expected, but got more than one.\n");
+               fprintf(stderr, " One match to '%s'", devnode_path);
+               udev_device_unref(part);
+               sys_path = udev_list_entry_get_name(dev_entry);
+               part = udev_device_new_from_syspath(udev, sys_path);
+               fprintf(stderr, "  and (at least) one more to '%s'\n", 
udev_device_get_devnode(part));
+               ret = -ENODEV;
+       }
+
+       udev_device_unref(part);
+leave:
+       udev_enumerate_unref(enumerate);
+       udev_unref(udev);
+
+       return ret;
+}
+
 /*
  * of_parse_partition - extract offset and size from a partition device_node
  *
@@ -2532,3 +2664,95 @@ int of_get_devicepath(struct device_node 
*partition_node, char **devpath, off_t
 
        return -EINVAL;
 }
+
+static int libdt_partition_size_get(const char *dev_node, size_t *size)
+{
+       struct stat fstat;
+       int rc;
+
+       rc = stat(dev_node, &fstat);
+       if (rc < 0) {
+               rc = -errno;
+               fprintf(stderr, "Failed to get the size of state's partition 
based backend memory via '%s': %m\n", dev_node);
+               return rc;
+       }
+
+       if (fstat.st_size > SIZE_MAX) {
+               fprintf(stderr, "Partition size of '%s' too big to fit into a 
size_t type. Cannot continue.\n", dev_node);
+               return -ERANGE;
+       }
+
+       *size = fstat.st_size;
+       return 0;
+}
+
+/**
+ * of_find_path - Translates a path description in the devicetree to a Linux
+ *                device path
+ *
+ * @node: the node containing the property with the path description
+ * @propname: the property name of the path description
+ * @devpath: if this function returns 0 devpath will contain the path belonging
+ *           to the input path description.
+ * @offset: Returns the offset to be used inside the partition device (always 
0)
+ * @size: Returns the size of the partition device
+ *
+ * paths in the devicetree have the form of a multistring property. The first
+ * string contains the full path to the physical device containing the path or
+ * a full path to a partition described by the OF partition binding.
+ * The remaining strings have the form "<type>:<options>". Currently supported
+ * for <type> are:
+ *
+ * partname:<partname> - find a partition by its partition name. For mtd
+ *                       partitions this is the label. For DOS partitions
+ *                       this is the number beginning with 0.
+ *
+ * examples:
+ *
+ * device-path = &mmc0, "partname:0";
+ * device-path = &norflash, "partname:barebox-environment";
+ * device-path = &environment_nor;
+ */
+
+int of_find_path(struct device_node *node, const char *propname, char 
**devpath,
+                off_t *offset, size_t *size)
+{
+       const char partnamestr[] = "partname:";
+       struct device_node *rnode;
+       struct udev_device *udev;
+       const char *part = NULL;
+       const char *path;
+       int rc;
+
+       path = of_get_property(node, propname, NULL);
+       if (!path)
+               return -EINVAL;
+
+       rnode = of_find_node_by_path(path);
+       if (!rnode)
+               return -ENODEV;
+
+       of_property_read_string_index(node, propname, 1, &part);
+       if (part) {
+               if (!strncmp(part, partnamestr, sizeof(partnamestr) - 1)) {
+                       part += sizeof(partnamestr) - 1;
+               } else {
+                       pr_err("Invalid device-path: %s\n", part);
+                       return -EINVAL;
+               }
+       } else {
+               /* of-partition use-case */
+               return of_get_devicepath(rnode, devpath, offset, size);
+       }
+
+       udev = of_find_device_by_node_path(rnode->full_name);
+       if (udev) {
+               *offset = 0;
+               rc = device_find_blockdev_partition(udev, part, devpath);
+               if (rc < 0)
+                       return rc;
+               return libdt_partition_size_get(*devpath, size);
+       }
+
+       return -EINVAL;
+}
-- 
2.30.2


Reply via email to