6 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation version 2 of the License.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* define this to enable parsing debugging */
25 /* #define DEBUG_PARSER */
39 #include "udev_version.h"
41 #include "libsysfs/libsysfs.h"
42 #include "klibc_fixups.h"
44 #define TYPE_LABEL "LABEL"
45 #define TYPE_NUMBER "NUMBER"
46 #define TYPE_TOPOLOGY "TOPOLOGY"
47 #define TYPE_REPLACE "REPLACE"
48 #define TYPE_CALLOUT "CALLOUT"
49 #define CALLOUT_MAXARG 8
51 static LIST_HEAD(config_device_list
);
53 /* s2 may end with '*' to match everything */
54 static int strncmp_wildcard(char *s1
, char *s2
, int max
)
63 return strncmp(s1
, s2
, len
);
66 static void dump_dev(struct config_device
*dev
)
70 dbg_parse("KERNEL name='%s' ,"
71 "owner='%s', group='%s', mode=%#o",
72 dev
->name
, dev
->owner
, dev
->group
, dev
->mode
);
75 dbg_parse("LABEL name='%s', bus='%s', sysfs_file='%s', sysfs_value='%s', "
76 "owner='%s', group='%s', mode=%#o",
77 dev
->name
, dev
->bus
, dev
->sysfs_file
, dev
->sysfs_value
,
78 dev
->owner
, dev
->group
, dev
->mode
);
81 dbg_parse("NUMBER name='%s', bus='%s', id='%s', "
82 "owner='%s', group='%s', mode=%#o",
83 dev
->name
, dev
->bus
, dev
->id
,
84 dev
->owner
, dev
->group
, dev
->mode
);
87 dbg_parse("TOPOLOGY name='%s', bus='%s', place='%s', "
88 "owner='%s', group='%s', mode=%#o",
89 dev
->name
, dev
->bus
, dev
->place
,
90 dev
->owner
, dev
->group
, dev
->mode
);
93 dbg_parse("REPLACE name=%s, kernel_name=%s, "
94 "owner='%s', group='%s', mode=%#o",
95 dev
->name
, dev
->kernel_name
,
96 dev
->owner
, dev
->group
, dev
->mode
);
99 dbg_parse("CALLOUT name='%s', bus='%s', program='%s', id='%s', "
100 "owner='%s', group='%s', mode=%#o",
101 dev
->name
, dev
->bus
, dev
->exec_program
, dev
->id
,
102 dev
->owner
, dev
->group
, dev
->mode
);
105 dbg_parse("unknown type of method");
109 #define copy_var(a, b, var) \
113 #define copy_string(a, b, var) \
114 if (strlen(b->var)) \
115 strcpy(a->var, b->var);
117 static int add_dev(struct config_device
*new_dev
)
119 struct list_head
*tmp
;
120 struct config_device
*tmp_dev
;
122 /* update the values if we already have the device */
123 list_for_each(tmp
, &config_device_list
) {
124 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
125 if (strncmp_wildcard(dev
->name
, new_dev
->name
, sizeof(dev
->name
)))
127 copy_var(dev
, new_dev
, type
);
128 copy_var(dev
, new_dev
, mode
);
129 copy_string(dev
, new_dev
, bus
);
130 copy_string(dev
, new_dev
, sysfs_file
);
131 copy_string(dev
, new_dev
, sysfs_value
);
132 copy_string(dev
, new_dev
, id
);
133 copy_string(dev
, new_dev
, place
);
134 copy_string(dev
, new_dev
, kernel_name
);
135 copy_string(dev
, new_dev
, owner
);
136 copy_string(dev
, new_dev
, group
);
140 /* not found, add new structure to the device list */
141 tmp_dev
= malloc(sizeof(*tmp_dev
));
144 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
145 list_add(&tmp_dev
->node
, &config_device_list
);
150 static void dump_dev_list(void)
152 struct list_head
*tmp
;
154 list_for_each(tmp
, &config_device_list
) {
155 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
160 static int get_pair(char **orig_string
, char **left
, char **right
)
163 char *string
= *orig_string
;
168 /* eat any whitespace */
169 while (isspace(*string
))
172 /* split based on '=' */
173 temp
= strsep(&string
, "=");
178 /* take the right side and strip off the '"' */
179 while (isspace(*string
))
186 temp
= strsep(&string
, "\"");
187 if (!string
|| *temp
== '\0')
190 *orig_string
= string
;
195 static int get_value(const char *left
, char **orig_string
, char **ret_string
)
200 retval
= get_pair(orig_string
, &left_string
, ret_string
);
203 if (strcasecmp(left_string
, left
) != 0)
208 static int namedev_init_config(void)
217 struct config_device dev
;
219 dbg("opening '%s' to read as config", udev_config_filename
);
220 fd
= fopen(udev_config_filename
, "r");
222 dbg("can't open '%s'", udev_config_filename
);
226 /* loop through the whole file */
230 temp
= fgets(line
, sizeof(line
), fd
);
235 dbg_parse("read '%s'", temp
);
237 /* eat the whitespace at the beginning of the line */
238 while (isspace(*temp
))
245 /* see if this is a comment */
246 if (*temp
== COMMENT_CHARACTER
)
249 memset(&dev
, 0x00, sizeof(struct config_device
));
252 temp2
= strsep(&temp
, ",");
253 if (strcasecmp(temp2
, TYPE_LABEL
) == 0) {
258 retval
= get_value("BUS", &temp
, &temp3
);
261 strfieldcpy(dev
.bus
, temp3
);
264 temp2
= strsep(&temp
, ",");
265 retval
= get_pair(&temp
, &temp2
, &temp3
);
268 strfieldcpy(dev
.sysfs_file
, temp2
);
269 strfieldcpy(dev
.sysfs_value
, temp3
);
271 /* NAME="new_name" */
272 temp2
= strsep(&temp
, ",");
273 retval
= get_value("NAME", &temp
, &temp3
);
276 strfieldcpy(dev
.name
, temp3
);
278 dbg_parse("LABEL name='%s', bus='%s', "
279 "sysfs_file='%s', sysfs_value='%s'",
280 dev
.name
, dev
.bus
, dev
.sysfs_file
,
284 if (strcasecmp(temp2
, TYPE_NUMBER
) == 0) {
289 retval
= get_value("BUS", &temp
, &temp3
);
292 strfieldcpy(dev
.bus
, temp3
);
295 temp2
= strsep(&temp
, ",");
296 retval
= get_value("ID", &temp
, &temp3
);
299 strfieldcpy(dev
.id
, temp3
);
301 /* NAME="new_name" */
302 temp2
= strsep(&temp
, ",");
303 retval
= get_value("NAME", &temp
, &temp3
);
306 strfieldcpy(dev
.name
, temp3
);
308 dbg_parse("NUMBER name='%s', bus='%s', id='%s'",
309 dev
.name
, dev
.bus
, dev
.id
);
312 if (strcasecmp(temp2
, TYPE_TOPOLOGY
) == 0) {
317 retval
= get_value("BUS", &temp
, &temp3
);
320 strfieldcpy(dev
.bus
, temp3
);
323 temp2
= strsep(&temp
, ",");
324 retval
= get_value("PLACE", &temp
, &temp3
);
327 strfieldcpy(dev
.place
, temp3
);
329 /* NAME="new_name" */
330 temp2
= strsep(&temp
, ",");
331 retval
= get_value("NAME", &temp
, &temp3
);
334 strfieldcpy(dev
.name
, temp3
);
336 dbg_parse("TOPOLOGY name='%s', bus='%s', place='%s'",
337 dev
.name
, dev
.bus
, dev
.place
);
340 if (strcasecmp(temp2
, TYPE_REPLACE
) == 0) {
344 /* KERNEL="kernel_name" */
345 retval
= get_value("KERNEL", &temp
, &temp3
);
348 strfieldcpy(dev
.kernel_name
, temp3
);
350 /* NAME="new_name" */
351 temp2
= strsep(&temp
, ",");
352 retval
= get_value("NAME", &temp
, &temp3
);
355 strfieldcpy(dev
.name
, temp3
);
356 dbg_parse("REPLACE name='%s', kernel_name='%s'",
357 dev
.name
, dev
.kernel_name
);
359 if (strcasecmp(temp2
, TYPE_CALLOUT
) == 0) {
364 retval
= get_value("BUS", &temp
, &temp3
);
367 strfieldcpy(dev
.bus
, temp3
);
369 /* PROGRAM="executable" */
370 temp2
= strsep(&temp
, ",");
371 retval
= get_value("PROGRAM", &temp
, &temp3
);
374 strfieldcpy(dev
.exec_program
, temp3
);
377 temp2
= strsep(&temp
, ",");
378 retval
= get_value("ID", &temp
, &temp3
);
381 strfieldcpy(dev
.id
, temp3
);
383 /* NAME="new_name" */
384 temp2
= strsep(&temp
, ",");
385 retval
= get_value("NAME", &temp
, &temp3
);
388 strfieldcpy(dev
.name
, temp3
);
389 dbg_parse("CALLOUT name='%s', program='%s'",
390 dev
.name
, dev
.exec_program
);
393 retval
= add_dev(&dev
);
395 dbg("add_dev returned with error %d", retval
);
399 dbg_parse("%s:%d:%Zd: error parsing '%s'", udev_config_filename
,
400 lineno
, temp
- line
, temp
);
407 static int namedev_init_permissions(void)
414 struct config_device dev
;
416 dbg("opening '%s' to read as permissions config", udev_config_permission_filename
);
417 fd
= fopen(udev_config_permission_filename
, "r");
419 dbg("can't open '%s'", udev_config_permission_filename
);
423 /* loop through the whole file */
425 temp
= fgets(line
, sizeof(line
), fd
);
429 dbg_parse("read '%s'", temp
);
431 /* eat the whitespace at the beginning of the line */
432 while (isspace(*temp
))
439 /* see if this is a comment */
440 if (*temp
== COMMENT_CHARACTER
)
443 memset(&dev
, 0x00, sizeof(dev
));
446 temp2
= strsep(&temp
, ":");
448 dbg("cannot parse line '%s'", line
);
451 strncpy(dev
.name
, temp2
, sizeof(dev
.name
));
453 temp2
= strsep(&temp
, ":");
455 dbg("cannot parse line '%s'", line
);
458 strncpy(dev
.owner
, temp2
, sizeof(dev
.owner
));
460 temp2
= strsep(&temp
, ":");
462 dbg("cannot parse line '%s'", line
);
465 strncpy(dev
.group
, temp2
, sizeof(dev
.owner
));
468 dbg("cannot parse line: %s", line
);
471 dev
.mode
= strtol(temp
, NULL
, 8);
473 dbg_parse("name='%s', owner='%s', group='%s', mode=%#o",
474 dev
.name
, dev
.owner
, dev
.group
,
476 retval
= add_dev(&dev
);
478 dbg("add_dev returned with error %d", retval
);
488 static mode_t
get_default_mode(struct sysfs_class_device
*class_dev
)
490 /* just default everyone to rw for the world! */
494 static void build_kernel_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
498 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
499 dig
= class_dev
->name
+ strlen(class_dev
->name
);
500 while (isdigit(*(dig
-1)))
502 strfieldcpy(udev
->kernel_number
, dig
);
503 dbg("kernel_number='%s'", udev
->kernel_number
);
506 static void apply_format(struct udevice
*udev
, unsigned char *string
)
508 char name
[NAME_SIZE
];
512 pos
= strchr(string
, '%');
515 strfieldcpy(name
, pos
+2);
519 if (strlen(udev
->bus_id
) == 0)
521 strcat(pos
, udev
->bus_id
);
522 dbg("substitute bus_id '%s'", udev
->bus_id
);
525 if (strlen(udev
->kernel_number
) == 0)
527 strcat(pos
, udev
->kernel_number
);
528 dbg("substitute kernel number '%s'", udev
->kernel_number
);
531 sprintf(pos
, "%u", udev
->minor
);
532 dbg("substitute minor number '%u'", udev
->minor
);
535 sprintf(pos
, "%u", udev
->major
);
536 dbg("substitute major number '%u'", udev
->major
);
539 if (strlen(udev
->callout_value
) == 0)
541 strcat(pos
, udev
->callout_value
);
542 dbg("substitute callout output '%s'", udev
->callout_value
);
545 dbg("unknown substitution type '%%%c'", pos
[1]);
548 strcat(string
, name
);
555 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
565 char *args
[CALLOUT_MAXARG
];
568 dbg("callout to '%s'", dev
->exec_program
);
582 close(STDOUT_FILENO
);
583 dup(fds
[1]); /* dup write side of pipe to STDOUT */
584 if (strchr(dev
->exec_program
, ' ')) {
585 /* callout with arguments */
586 arg
= dev
->exec_program
;
587 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
588 args
[i
] = strsep(&arg
, " ");
593 dbg("too many args - %d", i
);
596 retval
= execve(args
[0], args
, main_envp
);
598 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
601 dbg("child execve failed");
604 return -1; /* avoid compiler warning */
606 /* parent reads from fds[0] */
610 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
615 dbg("callout len %d too short", len
);
619 dbg("callout value already set");
623 strncpy(value
, buffer
, len
);
626 dbg("callout returned '%s'", value
);
630 dbg("wait failed result %d", res
);
635 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
636 dbg("callout program status 0x%x", status
);
644 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
646 struct config_device
*dev
;
647 struct list_head
*tmp
;
649 list_for_each(tmp
, &config_device_list
) {
650 dev
= list_entry(tmp
, struct config_device
, node
);
651 if (dev
->type
!= CALLOUT
)
654 /* substitute anything that needs to be in the program name */
655 apply_format(udev
, dev
->exec_program
);
656 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
658 if (strncmp_wildcard(udev
->callout_value
, dev
->id
, NAME_SIZE
) != 0)
660 strfieldcpy(udev
->name
, dev
->name
);
661 if (dev
->mode
!= 0) {
662 udev
->mode
= dev
->mode
;
663 strfieldcpy(udev
->owner
, dev
->owner
);
664 strfieldcpy(udev
->group
, dev
->group
);
666 dbg_parse("callout returned matching value '%s', '%s' becomes '%s'"
667 " - owner='%s', group='%s', mode=%#o",
668 dev
->id
, class_dev
->name
, udev
->name
,
669 dev
->owner
, dev
->group
, dev
->mode
);
675 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
677 struct sysfs_attribute
*tmpattr
= NULL
;
678 struct config_device
*dev
;
679 struct list_head
*tmp
;
681 list_for_each(tmp
, &config_device_list
) {
682 dev
= list_entry(tmp
, struct config_device
, node
);
683 if (dev
->type
!= LABEL
)
686 dbg_parse("look for device attribute '%s'", dev
->sysfs_file
);
687 /* try to find the attribute in the class device directory */
688 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
692 /* look in the class device directory if present */
694 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
702 tmpattr
->value
[strlen(tmpattr
->value
)-1] = 0x00;
703 dbg_parse("compare attribute '%s' value '%s' with '%s'",
704 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
705 if (strcmp(dev
->sysfs_value
, tmpattr
->value
) != 0)
708 strfieldcpy(udev
->name
, dev
->name
);
709 if (dev
->mode
!= 0) {
710 udev
->mode
= dev
->mode
;
711 strfieldcpy(udev
->owner
, dev
->owner
);
712 strfieldcpy(udev
->group
, dev
->group
);
714 dbg_parse("found matching attribute '%s', '%s' becomes '%s' "
715 "- owner='%s', group='%s', mode=%#o",
716 dev
->sysfs_file
, class_dev
->name
, udev
->name
,
717 dev
->owner
, dev
->group
, dev
->mode
);
724 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
726 struct config_device
*dev
;
727 struct list_head
*tmp
;
728 char path
[SYSFS_PATH_MAX
];
732 /* we have to have a sysfs device for NUMBER to work */
736 list_for_each(tmp
, &config_device_list
) {
737 dev
= list_entry(tmp
, struct config_device
, node
);
738 if (dev
->type
!= NUMBER
)
742 strfieldcpy(path
, sysfs_device
->path
);
743 temp
= strrchr(path
, '/');
744 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
745 if (strstr(temp
, dev
->id
) != NULL
) {
749 temp
= strrchr(path
, '/');
750 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
751 if (strstr(temp
, dev
->id
) != NULL
)
756 strfieldcpy(udev
->name
, dev
->name
);
757 if (dev
->mode
!= 0) {
758 udev
->mode
= dev
->mode
;
759 strfieldcpy(udev
->owner
, dev
->owner
);
760 strfieldcpy(udev
->group
, dev
->group
);
762 dbg_parse("found matching id '%s', '%s' becomes '%s'"
763 " - owner='%s', group ='%s', mode=%#o",
764 dev
->id
, class_dev
->name
, udev
->name
,
765 dev
->owner
, dev
->group
, dev
->mode
);
772 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
774 struct config_device
*dev
;
775 struct list_head
*tmp
;
776 char path
[SYSFS_PATH_MAX
];
780 /* we have to have a sysfs device for TOPOLOGY to work */
784 list_for_each(tmp
, &config_device_list
) {
785 dev
= list_entry(tmp
, struct config_device
, node
);
786 if (dev
->type
!= TOPOLOGY
)
790 strfieldcpy(path
, sysfs_device
->path
);
791 temp
= strrchr(path
, '/');
792 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
793 if (strstr(temp
, dev
->place
) != NULL
) {
797 temp
= strrchr(path
, '/');
798 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
799 if (strstr(temp
, dev
->place
) != NULL
)
805 strfieldcpy(udev
->name
, dev
->name
);
806 if (dev
->mode
!= 0) {
807 udev
->mode
= dev
->mode
;
808 strfieldcpy(udev
->owner
, dev
->owner
);
809 strfieldcpy(udev
->group
, dev
->group
);
811 dbg_parse("found matching place '%s', '%s' becomes '%s'"
812 " - owner='%s', group ='%s', mode=%#o",
813 dev
->place
, class_dev
->name
, udev
->name
,
814 dev
->owner
, dev
->group
, dev
->mode
);
820 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
822 struct config_device
*dev
;
823 struct list_head
*tmp
;
825 list_for_each(tmp
, &config_device_list
) {
826 dev
= list_entry(tmp
, struct config_device
, node
);
827 if (dev
->type
!= REPLACE
)
830 dbg_parse("compare name '%s' with '%s'",
831 dev
->kernel_name
, dev
->name
);
832 if (strcmp(dev
->kernel_name
, class_dev
->name
) != 0)
835 strfieldcpy(udev
->name
, dev
->name
);
836 if (dev
->mode
!= 0) {
837 udev
->mode
= dev
->mode
;
838 strfieldcpy(udev
->owner
, dev
->owner
);
839 strfieldcpy(udev
->group
, dev
->group
);
841 dbg_parse("found name, '%s' becomes '%s' - owner='%s', group='%s', mode = %#o",
842 dev
->kernel_name
, udev
->name
,
843 dev
->owner
, dev
->group
, dev
->mode
);
850 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
852 struct config_device
*dev
;
853 struct list_head
*tmp
;
856 strfieldcpy(udev
->name
, class_dev
->name
);
857 /* look for permissions */
858 list_for_each(tmp
, &config_device_list
) {
859 dev
= list_entry(tmp
, struct config_device
, node
);
860 len
= strlen(dev
->name
);
861 if (strncmp_wildcard(class_dev
->name
, dev
->name
, sizeof(dev
->name
)))
863 if (dev
->mode
!= 0) {
864 dbg_parse("found permissions for '%s'", class_dev
->name
);
865 udev
->mode
= dev
->mode
;
866 strfieldcpy(udev
->owner
, dev
->owner
);
867 strfieldcpy(udev
->group
, dev
->group
);
872 static int get_attr(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
874 struct sysfs_device
*sysfs_device
= NULL
;
875 struct sysfs_class_device
*class_dev_parent
= NULL
;
881 /* find the sysfs_device for this class device */
882 /* Wouldn't it really be nice if libsysfs could do this for us? */
883 if (class_dev
->sysdevice
) {
884 sysfs_device
= class_dev
->sysdevice
;
886 /* bah, let's go backwards up a level to see if the device is there,
887 * as block partitions don't point to the physical device. Need to fix that
888 * up in the kernel...
890 if (strstr(class_dev
->path
, "block")) {
891 dbg_parse("looking at block device");
892 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
893 char path
[SYSFS_PATH_MAX
];
895 dbg_parse("really is a partition");
896 strfieldcpy(path
, class_dev
->path
);
897 temp
= strrchr(path
, '/');
899 dbg_parse("looking for a class device at '%s'", path
);
900 class_dev_parent
= sysfs_open_class_device(path
);
901 if (class_dev_parent
== NULL
) {
902 dbg("sysfs_open_class_device at '%s' failed", path
);
904 dbg_parse("class_dev_parent->name='%s'", class_dev_parent
->name
);
905 if (class_dev_parent
->sysdevice
)
906 sysfs_device
= class_dev_parent
->sysdevice
;
913 dbg_parse("sysfs_device->path='%s'", sysfs_device
->path
);
914 dbg_parse("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
915 strfieldcpy(udev
->bus_id
, sysfs_device
->bus_id
);
917 dbg_parse("class_dev->name = '%s'", class_dev
->name
);
920 build_kernel_number(class_dev
, udev
);
922 /* rules are looked at in priority order */
923 retval
= do_callout(class_dev
, udev
);
927 retval
= do_label(class_dev
, udev
, sysfs_device
);
931 retval
= do_number(class_dev
, udev
, sysfs_device
);
935 retval
= do_topology(class_dev
, udev
, sysfs_device
);
939 retval
= do_replace(class_dev
, udev
);
943 do_kernelname(class_dev
, udev
);
947 /* substitute placeholder in NAME */
948 apply_format(udev
, udev
->name
);
951 /* mode was never set above */
953 udev
->mode
= get_default_mode(class_dev
);
954 udev
->owner
[0] = 0x00;
955 udev
->group
[0] = 0x00;
958 if (class_dev_parent
)
959 sysfs_close_class_device(class_dev_parent
);
964 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*dev
)
968 retval
= get_attr(class_dev
, dev
);
970 dbg("get_attr failed");
975 int namedev_init(void)
979 retval
= namedev_init_config();
983 retval
= namedev_init_permissions();