summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtest/udev-test.pl9
-rw-r--r--udev_rules.c77
2 files changed, 81 insertions, 5 deletions
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 10d87ed54a..53499d8871 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1561,6 +1561,15 @@ KERNEL="sda1", NAME=="no"
KERNEL=="sda1", NAME="yes"
EOF
},
+ {
+ desc => "magic subsys/kernel lookup",
+ subsys => "block",
+ devpath => "/block/sda",
+ exp_name => "00:e0:00:fb:04:e1",
+ rules => <<EOF
+KERNEL=="sda", NAME="\$attr{[net/eth0]address}"
+EOF
+ },
);
# set env
diff --git a/udev_rules.c b/udev_rules.c
index 5ded26d049..598c1ba739 100644
--- a/udev_rules.c
+++ b/udev_rules.c
@@ -267,10 +267,48 @@ static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
info("wait for '%s' for %i mseconds", filepath, 1000 / WAIT_LOOP_PER_SECOND);
usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
}
- err("waiting for '%s' failed", filepath);
+ info("waiting for '%s' failed", filepath);
return -1;
}
+/* handle "[$SUBSYSTEM/$KERNEL]<attribute>" lookup */
+static int attr_get_by_subsys_id(const char *attrstr, char *devpath, size_t len, char **attr)
+{
+ char subsys[NAME_SIZE];
+ char *attrib;
+ char *id;
+ int found = 0;
+
+ if (attrstr[0] != '[')
+ goto out;
+
+ strlcpy(subsys, &attrstr[1], sizeof(subsys));
+
+ attrib = strchr(subsys, ']');
+ if (attrib == NULL)
+ goto out;
+ attrib[0] = '\0';
+ attrib = &attrib[1];
+
+ id = strchr(subsys, '/');
+ if (id == NULL)
+ goto out;
+ id[0] = '\0';
+ id = &id[1];
+
+ if (sysfs_lookup_devpath_by_subsys_id(devpath, len, subsys, id)) {
+ if (attr != NULL) {
+ if (attrib[0] != '\0')
+ *attr = attrib;
+ else
+ *attr = NULL;
+ }
+ found = 1;
+ }
+out:
+ return found;
+}
+
void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
{
char temp[PATH_SIZE];
@@ -443,11 +481,20 @@ found:
if (attr == NULL)
err("missing file parameter for attr");
else {
+ char devpath[PATH_SIZE];
+ char *attrib;
const char *value = NULL;
size_t size;
- /* first try the current device, other matches may have selected */
- if (udev->dev_parent != NULL && udev->dev_parent != udev->dev)
+ if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) {
+ if (attrib != NULL)
+ value = sysfs_attr_get_value(devpath, attrib);
+ else
+ break;
+ }
+
+ /* try the current device, other matches may have selected */
+ if (value == NULL && udev->dev_parent != NULL && udev->dev_parent != udev->dev)
value = sysfs_attr_get_value(udev->dev_parent->devpath, attr);
/* look at all devices along the chain of parents */
@@ -643,12 +690,23 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
if (rule->test.operation != KEY_OP_UNSET) {
char filename[PATH_SIZE];
+ char devpath[PATH_SIZE];
+ char *attr;
struct stat statbuf;
int match;
strlcpy(filename, key_val(rule, &rule->test), sizeof(filename));
udev_rules_apply_format(udev, filename, sizeof(filename));
+ if (attr_get_by_subsys_id(filename, devpath, sizeof(devpath), &attr)) {
+ strlcpy(filename, sysfs_path, sizeof(filename));
+ strlcat(filename, devpath, sizeof(filename));
+ if (attr != NULL) {
+ strlcat(filename, "/", sizeof(filename));
+ strlcat(filename, attr, sizeof(filename));
+ }
+ }
+
match = (stat(filename, &statbuf) == 0);
info("'%s' %s", filename, match ? "exists" : "does not exist");
if (match && rule->test_mode_mask > 0) {
@@ -680,11 +738,20 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
pair->key.operation == KEY_OP_NOMATCH) {
const char *key_name = key_pair_name(rule, pair);
const char *key_value = key_val(rule, &pair->key);
- const char *value;
+ char devpath[PATH_SIZE];
+ char *attrib;
+ const char *value = NULL;
char val[VALUE_SIZE];
size_t len;
- value = sysfs_attr_get_value(udev->dev->devpath, key_name);
+ if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
+ if (attrib != NULL)
+ value = sysfs_attr_get_value(devpath, attrib);
+ else
+ goto nomatch;
+ }
+ if (value == NULL)
+ value = sysfs_attr_get_value(udev->dev->devpath, key_name);
if (value == NULL)
goto nomatch;
strlcpy(val, value, sizeof(val));