X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=namedev.c;h=c77b0f8ef43a598d46b4437fef5e0e991ccde7d3;hb=5cac7def788fc6dec19dc8185f9c2c7c7c8d49a2;hp=58986e25c2c712bb07e0b33f2338a603514abbee;hpb=7ecb8d23f371a38cd918334ee7bf8383f1807ddb;p=elogind.git diff --git a/namedev.c b/namedev.c index 58986e25c..c77b0f8ef 100644 --- a/namedev.c +++ b/namedev.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "list.h" #include "udev.h" @@ -101,40 +102,6 @@ static int strcmp_pattern(const char *p, const char *s) if (strlen(b->var)) \ strcpy(a->var, b->var); -int add_config_dev(struct config_device *new_dev) -{ - struct list_head *tmp; - struct config_device *tmp_dev; - - /* update the values if we already have the device */ - list_for_each(tmp, &config_device_list) { - struct config_device *dev = list_entry(tmp, struct config_device, node); - if (strcmp_pattern(new_dev->name, dev->name)) - continue; - if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name))) - continue; - copy_var(dev, new_dev, type); - copy_string(dev, new_dev, bus); - copy_string(dev, new_dev, sysfs_file); - copy_string(dev, new_dev, sysfs_value); - copy_string(dev, new_dev, id); - copy_string(dev, new_dev, place); - copy_string(dev, new_dev, kernel_name); - copy_string(dev, new_dev, exec_program); - copy_string(dev, new_dev, symlink); - return 0; - } - - /* not found, add new structure to the device list */ - tmp_dev = malloc(sizeof(*tmp_dev)); - if (!tmp_dev) - return -ENOMEM; - memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); - list_add_tail(&tmp_dev->node, &config_device_list); - //dump_config_dev(tmp_dev); - return 0; -} - int add_perm_dev(struct perm_device *new_dev) { struct list_head *tmp; @@ -185,17 +152,6 @@ static mode_t get_default_mode(struct sysfs_class_device *class_dev) return mode; } -static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev) -{ - char *dig; - - dig = class_dev->name + strlen(class_dev->name); - while (isdigit(*(dig-1))) - dig--; - strfieldcpy(udev->kernel_number, dig); - dbg("kernel_number='%s'", udev->kernel_number); -} - static void apply_format(struct udevice *udev, unsigned char *string) { char name[NAME_SIZE]; @@ -229,6 +185,12 @@ static void apply_format(struct udevice *udev, unsigned char *string) strcat(pos, udev->bus_id); dbg("substitute bus_id '%s'", udev->bus_id); break; + case 'k': + if (strlen(udev->kernel_name) == 0) + break; + strcat(pos, udev->kernel_name); + dbg("substitute kernel name '%s'", udev->kernel_name); + break; case 'n': if (strlen(udev->kernel_number) == 0) break; @@ -284,6 +246,55 @@ static void apply_format(struct udevice *udev, unsigned char *string) } } +static struct bus_file { + char *bus; + char *file; +} bus_files[] = { + { .bus = "scsi", .file = "vendor" }, + { .bus = "usb", .file = "idVendor" }, + {} +}; + +#define SECONDS_TO_WAIT_FOR_FILE 10 +static void wait_for_device_to_initialize(struct sysfs_device *sysfs_device) +{ + /* sleep until we see the file for this specific bus type show up this + * is needed because we can easily out-run the kernel in looking for + * these files before the paticular subsystem has created them in the + * sysfs tree properly. + * + * And people thought that the /sbin/hotplug event system was going to + * be slow, poo on you for arguing that before even testing it... + */ + struct bus_file *b = &bus_files[0]; + struct sysfs_attribute *tmpattr; + int loop; + + while (1) { + if (b->bus == NULL) + break; + if (strcmp(sysfs_device->bus, b->bus) == 0) { + tmpattr = NULL; + loop = SECONDS_TO_WAIT_FOR_FILE; + while (loop--) { + dbg("looking for file '%s' on bus '%s'", b->file, b->bus); + tmpattr = sysfs_get_device_attr(sysfs_device, b->file); + if (tmpattr) { + /* found it! */ + goto exit; + } + /* sleep to give the kernel a chance to create the file */ + sleep(1); + } + dbg("Timed out waiting for '%s' file, continuing on anyway...", b->file); + goto exit; + } + b++; + } + dbg("Did not find bus type '%s' on list of bus_id_files, contact greg@kroah.com", sysfs_device->bus); +exit: + return; /* here to prevent compiler warning... */ +} static int exec_callout(struct config_device *dev, char *value, int len) { @@ -408,12 +419,50 @@ static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev return -ENODEV; } -static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) +static int match_pair(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair) { struct sysfs_attribute *tmpattr = NULL; + char *c; + + if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0')) + return -ENODEV; + + dbg("look for device attribute '%s'", pair->file); + /* try to find the attribute in the class device directory */ + tmpattr = sysfs_get_classdev_attr(class_dev, pair->file); + if (tmpattr) + goto label_found; + + /* look in the class device directory if present */ + if (sysfs_device) { + tmpattr = sysfs_get_device_attr(sysfs_device, pair->file); + if (tmpattr) + goto label_found; + } + + return -ENODEV; + +label_found: + c = tmpattr->value + strlen(tmpattr->value)-1; + if (*c == '\n') + *c = 0x00; + dbg("compare attribute '%s' value '%s' with '%s'", + pair->file, tmpattr->value, pair->value); + if (strcmp_pattern(pair->value, tmpattr->value) != 0) + return -ENODEV; + + dbg("found matching attribute '%s' with value '%s'", + pair->file, pair->value); + return 0; +} + +static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) +{ + struct sysfs_pair *pair; struct config_device *dev; struct list_head *tmp; - char *c; + int i; + int match; list_for_each(tmp, &config_device_list) { dev = list_entry(tmp, struct config_device, node); @@ -426,34 +475,24 @@ static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, continue; } - dbg("look for device attribute '%s'", dev->sysfs_file); - /* try to find the attribute in the class device directory */ - tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file); - if (tmpattr) - goto label_found; - - /* look in the class device directory if present */ - if (sysfs_device) { - tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file); - if (tmpattr) - goto label_found; + match = 1; + for (i = 0; i < MAX_SYSFS_PAIRS; ++i) { + pair = &dev->sysfs_pair[i]; + if ((pair->file[0] == '\0') || (pair->value[0] == '\0')) + break; + if (match_pair(class_dev, sysfs_device, pair) != 0) { + match = 0; + break; + } } - - continue; - -label_found: - c = tmpattr->value + strlen(tmpattr->value)-1; - if (*c == '\n') - *c = 0x00; - dbg("compare attribute '%s' value '%s' with '%s'", - dev->sysfs_file, tmpattr->value, dev->sysfs_value); - if (strcmp_pattern(dev->sysfs_value, tmpattr->value) != 0) + if (match == 0) continue; + /* found match */ strfieldcpy(udev->name, dev->name); strfieldcpy(udev->symlink, dev->symlink); - dbg("found matching attribute '%s', '%s' becomes '%s' ", - dev->sysfs_file, class_dev->name, udev->name); + dbg("found matching attribute, '%s' becomes '%s' ", + class_dev->name, udev->name); return 0; } @@ -580,19 +619,70 @@ static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice * strfieldcpy(udev->name, class_dev->name); } -int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev) +static struct sysfs_device *get_sysfs_device(struct sysfs_class_device *class_dev) { - struct sysfs_device *sysfs_device = NULL; - struct sysfs_class_device *class_dev_parent = NULL; - int retval = 0; - struct perm_device *perm; + struct sysfs_device *sysfs_device; + struct sysfs_class_device *class_dev_parent; + int loop; + + /* FIXME!!! */ + /* This is needed here as we can easily out-race the placement of the + * device symlink by the kernel. The call to sleep(1); will be removed + * once libsysfs can be queried for sysfs_get_classdev_device() + * multiple times and have it return the proper information when the + * class device really shows up. For now, we live with the time + * delay... + */ + sleep(1); + +#if 0 /* FIXME + Something like this could also work, but for some reason doesn't, + I also tried just stat() on the device symlink, but that still + has nasty races, I'm probably doing something stupid... :( */ + loop = 10; + while (loop--) { + struct stat buf; + int retval; + char filename[SYSFS_PATH_MAX + 6]; + + strcpy(filename, class_dev->path); + strcat(filename, "/device"); + dbg("looking for '%s'", filename); + retval = stat(filename, &buf); + if (!retval) + break; - udev->mode = 0; + /* bah, let's go backwards up a level to see if the device is there, + * as block partitions don't point to the physical device. Need to fix that + * up in the kernel... + */ + if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { + if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { + class_dev_parent = sysfs_get_classdev_parent(class_dev); + if (class_dev_parent == NULL) { + dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); + } else { + strcpy(filename, class_dev_parent->path); + strcat(filename, "/device"); + dbg("looking for '%s'", filename); + retval = stat(filename, &buf); + if (!retval) + break; + } + } + } + /* sleep to give the kernel a chance to create the device file */ + sleep(1); + } +#endif + loop = 1; /* FIXME put a real value in here for when everything is fixed... */ + while (loop--) { + /* find the sysfs_device for this class device */ + /* Wouldn't it really be nice if libsysfs could do this for us? */ + sysfs_device = sysfs_get_classdev_device(class_dev); + if (sysfs_device != NULL) + goto exit; - /* find the sysfs_device for this class device */ - /* Wouldn't it really be nice if libsysfs could do this for us? */ - sysfs_device = sysfs_get_classdev_device(class_dev); - if (sysfs_device == NULL) { /* bah, let's go backwards up a level to see if the device is there, * as block partitions don't point to the physical device. Need to fix that * up in the kernel... @@ -601,28 +691,55 @@ int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *ud dbg("looking at block device"); if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { dbg("really is a partition"); - class_dev_parent = sysfs_get_classdev_parent - (class_dev); + class_dev_parent = sysfs_get_classdev_parent(class_dev); if (class_dev_parent == NULL) { dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); } else { dbg("class_dev_parent->name='%s'", class_dev_parent->name); sysfs_device = sysfs_get_classdev_device(class_dev_parent); + if (sysfs_device != NULL) + goto exit; } } } + /* sleep to give the kernel a chance to create the link */ + sleep(1); + } + dbg("Timed out waiting for device symlink, continuing on anyway..."); +exit: + return sysfs_device; +} + +int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev) +{ + struct sysfs_device *sysfs_device = NULL; + int retval = 0; + struct perm_device *perm; + char *pos; + + udev->mode = 0; + /* find the sysfs_device associated with this class device */ + sysfs_device = get_sysfs_device(class_dev); if (sysfs_device) { dbg("sysfs_device->path='%s'", sysfs_device->path); dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); dbg("sysfs_device->bus='%s'", sysfs_device->bus); strfieldcpy(udev->bus_id, sysfs_device->bus_id); + wait_for_device_to_initialize(sysfs_device); } else { dbg("class_dev->name = '%s'", class_dev->name); } - build_kernel_number(class_dev, udev); + strfieldcpy(udev->kernel_name, class_dev->name); + + /* get kernel number */ + pos = class_dev->name + strlen(class_dev->name); + while (isdigit(*(pos-1))) + pos--; + strfieldcpy(udev->kernel_number, pos); + dbg("kernel_number='%s'", udev->kernel_number); /* rules are looked at in priority order */ retval = do_callout(class_dev, udev, sysfs_device);