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
));
467 dev
.mode
= strtol(temp
, NULL
, 8);
469 dbg_parse("name='%s', owner='%s', group='%s', mode=%#o",
470 dev
.name
, dev
.owner
, dev
.group
,
472 retval
= add_dev(&dev
);
474 dbg("add_dev returned with error %d", retval
);
484 static mode_t
get_default_mode(struct sysfs_class_device
*class_dev
)
486 /* just default everyone to rw for the world! */
490 static void build_kernel_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
494 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
495 dig
= class_dev
->name
+ strlen(class_dev
->name
);
496 while (isdigit(*(dig
-1)))
498 strfieldcpy(udev
->kernel_number
, dig
);
499 dbg("kernel_number='%s'", udev
->kernel_number
);
502 static void apply_format(struct udevice
*udev
, unsigned char *string
)
504 char name
[NAME_SIZE
];
508 pos
= strchr(string
, '%');
511 strfieldcpy(name
, pos
+2);
515 if (strlen(udev
->bus_id
) == 0)
517 strcat(pos
, udev
->bus_id
);
518 dbg("substitute bus_id '%s'", udev
->bus_id
);
521 if (strlen(udev
->kernel_number
) == 0)
523 strcat(pos
, udev
->kernel_number
);
524 dbg("substitute kernel number '%s'", udev
->kernel_number
);
527 sprintf(pos
, "%u", udev
->minor
);
528 dbg("substitute minor number '%u'", udev
->minor
);
531 sprintf(pos
, "%u", udev
->major
);
532 dbg("substitute major number '%u'", udev
->major
);
535 if (strlen(udev
->callout_value
) == 0)
537 strcat(pos
, udev
->callout_value
);
538 dbg("substitute callout output '%s'", udev
->callout_value
);
541 dbg("unknown substitution type '%%%c'", pos
[1]);
544 strcat(string
, name
);
551 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
561 char *args
[CALLOUT_MAXARG
];
564 dbg("callout to '%s'", dev
->exec_program
);
578 close(STDOUT_FILENO
);
579 dup(fds
[1]); /* dup write side of pipe to STDOUT */
580 if (strchr(dev
->exec_program
, ' ')) {
581 /* callout with arguments */
582 arg
= dev
->exec_program
;
583 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
584 args
[i
] = strsep(&arg
, " ");
589 dbg("too many args - %d", i
);
592 retval
= execve(args
[0], args
, main_envp
);
594 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
597 dbg("child execve failed");
600 return -1; /* avoid compiler warning */
602 /* parent reads from fds[0] */
606 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
611 dbg("callout len %d too short", len
);
615 dbg("callout value already set");
619 strncpy(value
, buffer
, len
);
622 dbg("callout returned '%s'", value
);
626 dbg("wait failed result %d", res
);
631 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
632 dbg("callout program status 0x%x", status
);
640 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
642 struct config_device
*dev
;
643 struct list_head
*tmp
;
645 list_for_each(tmp
, &config_device_list
) {
646 dev
= list_entry(tmp
, struct config_device
, node
);
647 if (dev
->type
!= CALLOUT
)
650 /* substitute anything that needs to be in the program name */
651 apply_format(udev
, dev
->exec_program
);
652 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
654 if (strncmp_wildcard(udev
->callout_value
, dev
->id
, NAME_SIZE
) != 0)
656 strfieldcpy(udev
->name
, dev
->name
);
657 if (dev
->mode
!= 0) {
658 udev
->mode
= dev
->mode
;
659 strfieldcpy(udev
->owner
, dev
->owner
);
660 strfieldcpy(udev
->group
, dev
->group
);
662 dbg_parse("callout returned matching value '%s', '%s' becomes '%s'"
663 " - owner='%s', group='%s', mode=%#o",
664 dev
->id
, class_dev
->name
, udev
->name
,
665 dev
->owner
, dev
->group
, dev
->mode
);
671 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
673 struct sysfs_attribute
*tmpattr
= NULL
;
674 struct config_device
*dev
;
675 struct list_head
*tmp
;
677 list_for_each(tmp
, &config_device_list
) {
678 dev
= list_entry(tmp
, struct config_device
, node
);
679 if (dev
->type
!= LABEL
)
682 dbg_parse("look for device attribute '%s'", dev
->sysfs_file
);
683 /* try to find the attribute in the class device directory */
684 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
688 /* look in the class device directory if present */
690 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
698 tmpattr
->value
[strlen(tmpattr
->value
)-1] = 0x00;
699 dbg_parse("compare attribute '%s' value '%s' with '%s'",
700 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
701 if (strcmp(dev
->sysfs_value
, tmpattr
->value
) != 0)
704 strfieldcpy(udev
->name
, dev
->name
);
705 if (dev
->mode
!= 0) {
706 udev
->mode
= dev
->mode
;
707 strfieldcpy(udev
->owner
, dev
->owner
);
708 strfieldcpy(udev
->group
, dev
->group
);
710 dbg_parse("found matching attribute '%s', '%s' becomes '%s' "
711 "- owner='%s', group='%s', mode=%#o",
712 dev
->sysfs_file
, class_dev
->name
, udev
->name
,
713 dev
->owner
, dev
->group
, dev
->mode
);
720 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
722 struct config_device
*dev
;
723 struct list_head
*tmp
;
724 char path
[SYSFS_PATH_MAX
];
728 /* we have to have a sysfs device for NUMBER to work */
732 list_for_each(tmp
, &config_device_list
) {
733 dev
= list_entry(tmp
, struct config_device
, node
);
734 if (dev
->type
!= NUMBER
)
738 strfieldcpy(path
, sysfs_device
->path
);
739 temp
= strrchr(path
, '/');
740 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
741 if (strstr(temp
, dev
->id
) != NULL
) {
745 temp
= strrchr(path
, '/');
746 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
747 if (strstr(temp
, dev
->id
) != NULL
)
752 strfieldcpy(udev
->name
, dev
->name
);
753 if (dev
->mode
!= 0) {
754 udev
->mode
= dev
->mode
;
755 strfieldcpy(udev
->owner
, dev
->owner
);
756 strfieldcpy(udev
->group
, dev
->group
);
758 dbg_parse("found matching id '%s', '%s' becomes '%s'"
759 " - owner='%s', group ='%s', mode=%#o",
760 dev
->id
, class_dev
->name
, udev
->name
,
761 dev
->owner
, dev
->group
, dev
->mode
);
768 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
770 struct config_device
*dev
;
771 struct list_head
*tmp
;
772 char path
[SYSFS_PATH_MAX
];
776 /* we have to have a sysfs device for TOPOLOGY to work */
780 list_for_each(tmp
, &config_device_list
) {
781 dev
= list_entry(tmp
, struct config_device
, node
);
782 if (dev
->type
!= TOPOLOGY
)
786 strfieldcpy(path
, sysfs_device
->path
);
787 temp
= strrchr(path
, '/');
788 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
789 if (strstr(temp
, dev
->place
) != NULL
) {
793 temp
= strrchr(path
, '/');
794 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
795 if (strstr(temp
, dev
->place
) != NULL
)
801 strfieldcpy(udev
->name
, dev
->name
);
802 if (dev
->mode
!= 0) {
803 udev
->mode
= dev
->mode
;
804 strfieldcpy(udev
->owner
, dev
->owner
);
805 strfieldcpy(udev
->group
, dev
->group
);
807 dbg_parse("found matching place '%s', '%s' becomes '%s'"
808 " - owner='%s', group ='%s', mode=%#o",
809 dev
->place
, class_dev
->name
, udev
->name
,
810 dev
->owner
, dev
->group
, dev
->mode
);
816 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
818 struct config_device
*dev
;
819 struct list_head
*tmp
;
821 list_for_each(tmp
, &config_device_list
) {
822 dev
= list_entry(tmp
, struct config_device
, node
);
823 if (dev
->type
!= REPLACE
)
826 dbg_parse("compare name '%s' with '%s'",
827 dev
->kernel_name
, dev
->name
);
828 if (strcmp(dev
->kernel_name
, class_dev
->name
) != 0)
831 strfieldcpy(udev
->name
, dev
->name
);
832 if (dev
->mode
!= 0) {
833 udev
->mode
= dev
->mode
;
834 strfieldcpy(udev
->owner
, dev
->owner
);
835 strfieldcpy(udev
->group
, dev
->group
);
837 dbg_parse("found name, '%s' becomes '%s' - owner='%s', group='%s', mode = %#o",
838 dev
->kernel_name
, udev
->name
,
839 dev
->owner
, dev
->group
, dev
->mode
);
846 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
848 struct config_device
*dev
;
849 struct list_head
*tmp
;
852 strfieldcpy(udev
->name
, class_dev
->name
);
853 /* look for permissions */
854 list_for_each(tmp
, &config_device_list
) {
855 dev
= list_entry(tmp
, struct config_device
, node
);
856 len
= strlen(dev
->name
);
857 if (strncmp_wildcard(class_dev
->name
, dev
->name
, sizeof(dev
->name
)))
859 if (dev
->mode
!= 0) {
860 dbg_parse("found permissions for '%s'", class_dev
->name
);
861 udev
->mode
= dev
->mode
;
862 strfieldcpy(udev
->owner
, dev
->owner
);
863 strfieldcpy(udev
->group
, dev
->group
);
868 static int get_attr(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
870 struct sysfs_device
*sysfs_device
= NULL
;
871 struct sysfs_class_device
*class_dev_parent
= NULL
;
877 /* find the sysfs_device for this class device */
878 /* Wouldn't it really be nice if libsysfs could do this for us? */
879 if (class_dev
->sysdevice
) {
880 sysfs_device
= class_dev
->sysdevice
;
882 /* bah, let's go backwards up a level to see if the device is there,
883 * as block partitions don't point to the physical device. Need to fix that
884 * up in the kernel...
886 if (strstr(class_dev
->path
, "block")) {
887 dbg_parse("looking at block device");
888 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
889 char path
[SYSFS_PATH_MAX
];
891 dbg_parse("really is a partition");
892 strfieldcpy(path
, class_dev
->path
);
893 temp
= strrchr(path
, '/');
895 dbg_parse("looking for a class device at '%s'", path
);
896 class_dev_parent
= sysfs_open_class_device(path
);
897 if (class_dev_parent
== NULL
) {
898 dbg("sysfs_open_class_device at '%s' failed", path
);
900 dbg_parse("class_dev_parent->name='%s'", class_dev_parent
->name
);
901 if (class_dev_parent
->sysdevice
)
902 sysfs_device
= class_dev_parent
->sysdevice
;
909 dbg_parse("sysfs_device->path='%s'", sysfs_device
->path
);
910 dbg_parse("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
911 strfieldcpy(udev
->bus_id
, sysfs_device
->bus_id
);
913 dbg_parse("class_dev->name = '%s'", class_dev
->name
);
916 build_kernel_number(class_dev
, udev
);
918 /* rules are looked at in priority order */
919 retval
= do_callout(class_dev
, udev
);
923 retval
= do_label(class_dev
, udev
, sysfs_device
);
927 retval
= do_number(class_dev
, udev
, sysfs_device
);
931 retval
= do_topology(class_dev
, udev
, sysfs_device
);
935 retval
= do_replace(class_dev
, udev
);
939 do_kernelname(class_dev
, udev
);
943 /* substitute placeholder in NAME */
944 apply_format(udev
, udev
->name
);
947 /* mode was never set above */
949 udev
->mode
= get_default_mode(class_dev
);
950 udev
->owner
[0] = 0x00;
951 udev
->group
[0] = 0x00;
954 if (class_dev_parent
)
955 sysfs_close_class_device(class_dev_parent
);
960 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*dev
)
964 retval
= get_attr(class_dev
, dev
);
966 dbg("get_attr failed");
971 int namedev_init(void)
975 retval
= namedev_init_config();
979 retval
= namedev_init_permissions();