]>
Commit | Line | Data |
---|---|---|
2232cac8 GKH |
1 | /* |
2 | * namedev.c | |
3 | * | |
4 | * Userspace devfs | |
5 | * | |
6 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | |
7 | * | |
8 | * | |
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. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <stddef.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
27 | #include <stdio.h> | |
28 | #include <fcntl.h> | |
29 | #include <ctype.h> | |
30 | #include <unistd.h> | |
31 | #include <errno.h> | |
c27e6911 | 32 | #include <sys/wait.h> |
dac056aa | 33 | #include <sys/stat.h> |
2232cac8 GKH |
34 | |
35 | #include "list.h" | |
36 | #include "udev.h" | |
37 | #include "udev_version.h" | |
38 | #include "namedev.h" | |
185a35a4 | 39 | #include "libsysfs/libsysfs.h" |
2023350e | 40 | #include "klibc_fixups.h" |
2232cac8 | 41 | |
19feb351 | 42 | LIST_HEAD(config_device_list); |
61219c75 | 43 | LIST_HEAD(perm_device_list); |
2232cac8 | 44 | |
9f1da361 KS |
45 | /* compare string with pattern (supports * ? [0-9] [!A-Z]) */ |
46 | static int strcmp_pattern(const char *p, const char *s) | |
c124eafa | 47 | { |
9f1da361 KS |
48 | if (*s == '\0') { |
49 | while (*p == '*') | |
50 | p++; | |
51 | return (*p != '\0'); | |
52 | } | |
53 | switch (*p) { | |
54 | case '[': | |
55 | { | |
56 | int not = 0; | |
57 | p++; | |
58 | if (*p == '!') { | |
59 | not = 1; | |
60 | p++; | |
61 | } | |
62 | while (*p && (*p != ']')) { | |
63 | int match = 0; | |
64 | if (p[1] == '-') { | |
65 | if ((*s >= *p) && (*s <= p[2])) | |
66 | match = 1; | |
67 | p += 3; | |
68 | } else { | |
69 | match = (*p == *s); | |
70 | p++; | |
71 | } | |
72 | if (match ^ not) { | |
73 | while (*p && (*p != ']')) | |
74 | p++; | |
75 | return strcmp_pattern(p+1, s+1); | |
76 | } | |
77 | } | |
78 | } | |
79 | break; | |
80 | case '*': | |
81 | if (strcmp_pattern(p, s+1)) | |
82 | return strcmp_pattern(p+1, s); | |
83 | return 0; | |
84 | case '\0': | |
85 | if (*s == '\0') { | |
86 | return 0; | |
87 | } | |
88 | break; | |
89 | default: | |
90 | if ((*p == *s) || (*p == '?')) | |
91 | return strcmp_pattern(p+1, s+1); | |
92 | break; | |
93 | } | |
94 | return 1; | |
c124eafa KS |
95 | } |
96 | ||
2232cac8 GKH |
97 | #define copy_var(a, b, var) \ |
98 | if (b->var) \ | |
469c7cff | 99 | a->var = b->var; |
2232cac8 GKH |
100 | |
101 | #define copy_string(a, b, var) \ | |
102 | if (strlen(b->var)) \ | |
469c7cff | 103 | strcpy(a->var, b->var); |
2232cac8 | 104 | |
19feb351 | 105 | int add_config_dev(struct config_device *new_dev) |
2232cac8 GKH |
106 | { |
107 | struct list_head *tmp; | |
108 | struct config_device *tmp_dev; | |
109 | ||
09e52d51 | 110 | /* update the values if we already have the device */ |
2232cac8 GKH |
111 | list_for_each(tmp, &config_device_list) { |
112 | struct config_device *dev = list_entry(tmp, struct config_device, node); | |
9f1da361 | 113 | if (strcmp_pattern(new_dev->name, dev->name)) |
c124eafa | 114 | continue; |
137af0cc GKH |
115 | if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name))) |
116 | continue; | |
09e52d51 | 117 | copy_var(dev, new_dev, type); |
09e52d51 KS |
118 | copy_string(dev, new_dev, bus); |
119 | copy_string(dev, new_dev, sysfs_file); | |
120 | copy_string(dev, new_dev, sysfs_value); | |
121 | copy_string(dev, new_dev, id); | |
122 | copy_string(dev, new_dev, place); | |
123 | copy_string(dev, new_dev, kernel_name); | |
137af0cc | 124 | copy_string(dev, new_dev, exec_program); |
3d150dfb | 125 | copy_string(dev, new_dev, symlink); |
09e52d51 | 126 | return 0; |
2232cac8 GKH |
127 | } |
128 | ||
8f43a65e | 129 | /* not found, add new structure to the device list */ |
2232cac8 GKH |
130 | tmp_dev = malloc(sizeof(*tmp_dev)); |
131 | if (!tmp_dev) | |
132 | return -ENOMEM; | |
133 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
8cf7ebe8 | 134 | list_add_tail(&tmp_dev->node, &config_device_list); |
19feb351 | 135 | //dump_config_dev(tmp_dev); |
469c7cff GKH |
136 | return 0; |
137 | } | |
2232cac8 | 138 | |
61219c75 GKH |
139 | int add_perm_dev(struct perm_device *new_dev) |
140 | { | |
141 | struct list_head *tmp; | |
142 | struct perm_device *tmp_dev; | |
143 | ||
144 | /* update the values if we already have the device */ | |
145 | list_for_each(tmp, &perm_device_list) { | |
146 | struct perm_device *dev = list_entry(tmp, struct perm_device, node); | |
147 | if (strcmp_pattern(new_dev->name, dev->name)) | |
148 | continue; | |
149 | copy_var(dev, new_dev, mode); | |
150 | copy_string(dev, new_dev, owner); | |
151 | copy_string(dev, new_dev, group); | |
152 | return 0; | |
153 | } | |
154 | ||
155 | /* not found, add new structure to the perm list */ | |
156 | tmp_dev = malloc(sizeof(*tmp_dev)); | |
157 | if (!tmp_dev) | |
158 | return -ENOMEM; | |
159 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
160 | list_add_tail(&tmp_dev->node, &perm_device_list); | |
161 | //dump_perm_dev(tmp_dev); | |
162 | return 0; | |
163 | } | |
164 | ||
165 | static struct perm_device *find_perm(char *name) | |
166 | { | |
167 | struct list_head *tmp; | |
168 | struct perm_device *perm = NULL; | |
169 | ||
170 | list_for_each(tmp, &perm_device_list) { | |
171 | perm = list_entry(tmp, struct perm_device, node); | |
172 | if (strcmp_pattern(perm->name, name)) | |
173 | continue; | |
174 | return perm; | |
175 | } | |
176 | return NULL; | |
177 | } | |
178 | ||
c2405f50 | 179 | static mode_t get_default_mode(struct sysfs_class_device *class_dev) |
185a35a4 | 180 | { |
89571022 GKH |
181 | mode_t mode = 0600; /* default to owner rw only */ |
182 | ||
183 | if (strlen(default_mode_str) != 0) { | |
184 | mode = strtol(default_mode_str, NULL, 8); | |
185 | } | |
186 | return mode; | |
185a35a4 GKH |
187 | } |
188 | ||
f3b04a2e GKH |
189 | static void apply_format(struct udevice *udev, unsigned char *string) |
190 | { | |
191 | char name[NAME_SIZE]; | |
b1c5e333 KS |
192 | char temp[NAME_SIZE]; |
193 | char *tail; | |
f3b04a2e | 194 | char *pos; |
b1c5e333 KS |
195 | char *pos2; |
196 | char *pos3; | |
197 | int num; | |
f3b04a2e GKH |
198 | |
199 | while (1) { | |
b1c5e333 | 200 | num = 0; |
f3b04a2e GKH |
201 | pos = strchr(string, '%'); |
202 | ||
203 | if (pos) { | |
b1c5e333 KS |
204 | *pos = '\0'; |
205 | tail = pos+1; | |
206 | if (isdigit(tail[0])) { | |
207 | num = (int) strtoul(&pos[1], &tail, 10); | |
208 | if (tail == NULL) { | |
209 | dbg("format parsing error '%s'", pos+1); | |
210 | break; | |
211 | } | |
212 | } | |
213 | strfieldcpy(name, tail+1); | |
214 | ||
215 | switch (tail[0]) { | |
f3b04a2e GKH |
216 | case 'b': |
217 | if (strlen(udev->bus_id) == 0) | |
218 | break; | |
6968d494 | 219 | strcat(pos, udev->bus_id); |
f3b04a2e GKH |
220 | dbg("substitute bus_id '%s'", udev->bus_id); |
221 | break; | |
3e540368 KS |
222 | case 'k': |
223 | if (strlen(udev->kernel_name) == 0) | |
224 | break; | |
225 | strcat(pos, udev->kernel_name); | |
226 | dbg("substitute kernel name '%s'", udev->kernel_name); | |
227 | break; | |
f3b04a2e GKH |
228 | case 'n': |
229 | if (strlen(udev->kernel_number) == 0) | |
230 | break; | |
231 | strcat(pos, udev->kernel_number); | |
232 | dbg("substitute kernel number '%s'", udev->kernel_number); | |
233 | break; | |
5c6f0f14 AB |
234 | case 'D': |
235 | if (strlen(udev->kernel_number) == 0) { | |
525d07e7 | 236 | strcat(pos, "disc"); |
5c6f0f14 AB |
237 | break; |
238 | } | |
239 | strcat(pos, "part"); | |
240 | strcat(pos, udev->kernel_number); | |
241 | dbg("substitute kernel number '%s'", udev->kernel_number); | |
242 | break; | |
f3b04a2e GKH |
243 | case 'm': |
244 | sprintf(pos, "%u", udev->minor); | |
245 | dbg("substitute minor number '%u'", udev->minor); | |
246 | break; | |
247 | case 'M': | |
248 | sprintf(pos, "%u", udev->major); | |
249 | dbg("substitute major number '%u'", udev->major); | |
250 | break; | |
251 | case 'c': | |
252 | if (strlen(udev->callout_value) == 0) | |
253 | break; | |
b1c5e333 KS |
254 | if (num) { |
255 | /* get part of return string */ | |
256 | strncpy(temp, udev->callout_value, sizeof(temp)); | |
257 | pos2 = temp; | |
258 | while (num) { | |
259 | num--; | |
260 | pos3 = strsep(&pos2, " "); | |
261 | if (pos3 == NULL) { | |
262 | dbg("requested part of callout string not found"); | |
263 | break; | |
264 | } | |
265 | } | |
266 | strcat(pos, pos3); | |
267 | dbg("substitute partial callout output '%s'", pos3); | |
268 | } else { | |
269 | strcat(pos, udev->callout_value); | |
270 | dbg("substitute callout output '%s'", udev->callout_value); | |
271 | } | |
f3b04a2e GKH |
272 | break; |
273 | default: | |
274 | dbg("unknown substitution type '%%%c'", pos[1]); | |
275 | break; | |
276 | } | |
277 | strcat(string, name); | |
278 | } else | |
279 | break; | |
280 | } | |
281 | } | |
282 | ||
dac056aa GKH |
283 | static struct bus_file { |
284 | char *bus; | |
285 | char *file; | |
286 | } bus_files[] = { | |
287 | { .bus = "scsi", .file = "vendor" }, | |
288 | { .bus = "usb", .file = "idVendor" }, | |
289 | {} | |
290 | }; | |
291 | ||
292 | #define SECONDS_TO_WAIT_FOR_FILE 10 | |
293 | static void wait_for_device_to_initialize(struct sysfs_device *sysfs_device) | |
294 | { | |
295 | /* sleep until we see the file for this specific bus type show up this | |
296 | * is needed because we can easily out-run the kernel in looking for | |
297 | * these files before the paticular subsystem has created them in the | |
298 | * sysfs tree properly. | |
299 | * | |
300 | * And people thought that the /sbin/hotplug event system was going to | |
301 | * be slow, poo on you for arguing that before even testing it... | |
302 | */ | |
303 | struct bus_file *b = &bus_files[0]; | |
304 | struct sysfs_attribute *tmpattr; | |
305 | int loop; | |
306 | ||
307 | while (1) { | |
308 | if (b->bus == NULL) | |
309 | break; | |
310 | if (strcmp(sysfs_device->bus, b->bus) == 0) { | |
311 | tmpattr = NULL; | |
312 | loop = SECONDS_TO_WAIT_FOR_FILE; | |
313 | while (loop--) { | |
314 | dbg("looking for file '%s' on bus '%s'", b->file, b->bus); | |
315 | tmpattr = sysfs_get_device_attr(sysfs_device, b->file); | |
316 | if (tmpattr) { | |
317 | /* found it! */ | |
318 | goto exit; | |
319 | } | |
320 | /* sleep to give the kernel a chance to create the file */ | |
321 | sleep(1); | |
322 | } | |
323 | dbg("Timed out waiting for '%s' file, continuing on anyway...", b->file); | |
324 | goto exit; | |
325 | } | |
326 | b++; | |
327 | } | |
328 | dbg("Did not find bus type '%s' on list of bus_id_files, contact greg@kroah.com", sysfs_device->bus); | |
329 | exit: | |
330 | return; /* here to prevent compiler warning... */ | |
331 | } | |
19dc5d4c | 332 | |
c27e6911 PM |
333 | static int exec_callout(struct config_device *dev, char *value, int len) |
334 | { | |
335 | int retval; | |
336 | int res; | |
337 | int status; | |
338 | int fds[2]; | |
339 | pid_t pid; | |
340 | int value_set = 0; | |
341 | char buffer[256]; | |
20524642 | 342 | char *pos; |
bc434511 KS |
343 | char *args[CALLOUT_MAXARG]; |
344 | int i; | |
c27e6911 | 345 | |
8f43a65e | 346 | dbg("callout to '%s'", dev->exec_program); |
c27e6911 PM |
347 | retval = pipe(fds); |
348 | if (retval != 0) { | |
349 | dbg("pipe failed"); | |
350 | return -1; | |
351 | } | |
352 | pid = fork(); | |
353 | if (pid == -1) { | |
354 | dbg("fork failed"); | |
355 | return -1; | |
356 | } | |
357 | ||
358 | if (pid == 0) { | |
8f43a65e | 359 | /* child */ |
c27e6911 PM |
360 | close(STDOUT_FILENO); |
361 | dup(fds[1]); /* dup write side of pipe to STDOUT */ | |
bc434511 KS |
362 | if (strchr(dev->exec_program, ' ')) { |
363 | /* callout with arguments */ | |
20524642 | 364 | pos = dev->exec_program; |
bc434511 | 365 | for (i=0; i < CALLOUT_MAXARG-1; i++) { |
20524642 | 366 | args[i] = strsep(&pos, " "); |
bc434511 KS |
367 | if (args[i] == NULL) |
368 | break; | |
369 | } | |
370 | if (args[i]) { | |
53dc383e | 371 | dbg("too many args - %d", i); |
bc434511 KS |
372 | args[i] = NULL; |
373 | } | |
374 | retval = execve(args[0], args, main_envp); | |
375 | } else { | |
376 | retval = execve(dev->exec_program, main_argv, main_envp); | |
377 | } | |
c27e6911 PM |
378 | if (retval != 0) { |
379 | dbg("child execve failed"); | |
380 | exit(1); | |
381 | } | |
382 | return -1; /* avoid compiler warning */ | |
383 | } else { | |
8f43a65e | 384 | /* parent reads from fds[0] */ |
c27e6911 PM |
385 | close(fds[1]); |
386 | retval = 0; | |
387 | while (1) { | |
388 | res = read(fds[0], buffer, sizeof(buffer) - 1); | |
389 | if (res <= 0) | |
390 | break; | |
391 | buffer[res] = '\0'; | |
392 | if (res > len) { | |
f7b4eca4 | 393 | dbg("callout len %d too short", len); |
c27e6911 PM |
394 | retval = -1; |
395 | } | |
396 | if (value_set) { | |
397 | dbg("callout value already set"); | |
398 | retval = -1; | |
399 | } else { | |
400 | value_set = 1; | |
401 | strncpy(value, buffer, len); | |
20524642 KS |
402 | pos = value + strlen(value)-1; |
403 | if (pos[0] == '\n') | |
404 | pos[0] = '\0'; | |
405 | dbg("callout returned '%s'", value); | |
c27e6911 PM |
406 | } |
407 | } | |
408 | close(fds[0]); | |
409 | res = wait(&status); | |
410 | if (res < 0) { | |
411 | dbg("wait failed result %d", res); | |
412 | retval = -1; | |
413 | } | |
414 | ||
1e959a4b | 415 | #ifndef __KLIBC__ |
c27e6911 PM |
416 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { |
417 | dbg("callout program status 0x%x", status); | |
418 | retval = -1; | |
419 | } | |
1e959a4b | 420 | #endif |
c27e6911 PM |
421 | } |
422 | return retval; | |
423 | } | |
424 | ||
137af0cc | 425 | static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
120d45d0 GKH |
426 | { |
427 | struct config_device *dev; | |
428 | struct list_head *tmp; | |
120d45d0 GKH |
429 | |
430 | list_for_each(tmp, &config_device_list) { | |
431 | dev = list_entry(tmp, struct config_device, node); | |
432 | if (dev->type != CALLOUT) | |
433 | continue; | |
f3b04a2e | 434 | |
137af0cc | 435 | if (sysfs_device) { |
19feb351 | 436 | dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); |
137af0cc GKH |
437 | if (strcasecmp(dev->bus, sysfs_device->bus) != 0) |
438 | continue; | |
439 | } | |
440 | ||
f3b04a2e GKH |
441 | /* substitute anything that needs to be in the program name */ |
442 | apply_format(udev, dev->exec_program); | |
443 | if (exec_callout(dev, udev->callout_value, NAME_SIZE)) | |
120d45d0 | 444 | continue; |
9f1da361 | 445 | if (strcmp_pattern(dev->id, udev->callout_value) != 0) |
120d45d0 | 446 | continue; |
70033702 | 447 | strfieldcpy(udev->name, dev->name); |
3d150dfb | 448 | strfieldcpy(udev->symlink, dev->symlink); |
61219c75 GKH |
449 | dbg("callout returned matching value '%s', '%s' becomes '%s'", |
450 | dev->id, class_dev->name, udev->name); | |
120d45d0 GKH |
451 | return 0; |
452 | } | |
453 | return -ENODEV; | |
454 | } | |
455 | ||
ca1cc0fe GKH |
456 | static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
457 | { | |
458 | struct sysfs_attribute *tmpattr = NULL; | |
459 | struct config_device *dev; | |
460 | struct list_head *tmp; | |
28d6536a | 461 | char *c; |
ca1cc0fe GKH |
462 | |
463 | list_for_each(tmp, &config_device_list) { | |
464 | dev = list_entry(tmp, struct config_device, node); | |
465 | if (dev->type != LABEL) | |
466 | continue; | |
467 | ||
137af0cc | 468 | if (sysfs_device) { |
19feb351 | 469 | dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); |
137af0cc GKH |
470 | if (strcasecmp(dev->bus, sysfs_device->bus) != 0) |
471 | continue; | |
472 | } | |
473 | ||
19feb351 | 474 | dbg("look for device attribute '%s'", dev->sysfs_file); |
ca1cc0fe GKH |
475 | /* try to find the attribute in the class device directory */ |
476 | tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file); | |
477 | if (tmpattr) | |
478 | goto label_found; | |
479 | ||
480 | /* look in the class device directory if present */ | |
481 | if (sysfs_device) { | |
482 | tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file); | |
483 | if (tmpattr) | |
484 | goto label_found; | |
485 | } | |
486 | ||
487 | continue; | |
488 | ||
489 | label_found: | |
28d6536a KS |
490 | c = tmpattr->value + strlen(tmpattr->value)-1; |
491 | if (*c == '\n') | |
492 | *c = 0x00; | |
19feb351 | 493 | dbg("compare attribute '%s' value '%s' with '%s'", |
8f43a65e | 494 | dev->sysfs_file, tmpattr->value, dev->sysfs_value); |
83be97ba | 495 | if (strcmp_pattern(dev->sysfs_value, tmpattr->value) != 0) |
ca1cc0fe GKH |
496 | continue; |
497 | ||
70033702 | 498 | strfieldcpy(udev->name, dev->name); |
3d150dfb | 499 | strfieldcpy(udev->symlink, dev->symlink); |
61219c75 GKH |
500 | dbg("found matching attribute '%s', '%s' becomes '%s' ", |
501 | dev->sysfs_file, class_dev->name, udev->name); | |
ca1cc0fe GKH |
502 | |
503 | return 0; | |
504 | } | |
505 | return -ENODEV; | |
506 | } | |
507 | ||
508 | static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) | |
509 | { | |
510 | struct config_device *dev; | |
511 | struct list_head *tmp; | |
512 | char path[SYSFS_PATH_MAX]; | |
513 | int found; | |
514 | char *temp = NULL; | |
515 | ||
516 | /* we have to have a sysfs device for NUMBER to work */ | |
517 | if (!sysfs_device) | |
518 | return -ENODEV; | |
519 | ||
520 | list_for_each(tmp, &config_device_list) { | |
521 | dev = list_entry(tmp, struct config_device, node); | |
522 | if (dev->type != NUMBER) | |
523 | continue; | |
524 | ||
19feb351 | 525 | dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); |
137af0cc GKH |
526 | if (strcasecmp(dev->bus, sysfs_device->bus) != 0) |
527 | continue; | |
528 | ||
ca1cc0fe | 529 | found = 0; |
70033702 | 530 | strfieldcpy(path, sysfs_device->path); |
ca1cc0fe | 531 | temp = strrchr(path, '/'); |
19feb351 | 532 | dbg("search '%s' in '%s', path='%s'", dev->id, temp, path); |
ca1cc0fe GKH |
533 | if (strstr(temp, dev->id) != NULL) { |
534 | found = 1; | |
535 | } else { | |
536 | *temp = 0x00; | |
537 | temp = strrchr(path, '/'); | |
19feb351 | 538 | dbg("search '%s' in '%s', path='%s'", dev->id, temp, path); |
ca1cc0fe GKH |
539 | if (strstr(temp, dev->id) != NULL) |
540 | found = 1; | |
541 | } | |
542 | if (!found) | |
543 | continue; | |
70033702 | 544 | strfieldcpy(udev->name, dev->name); |
3d150dfb | 545 | strfieldcpy(udev->symlink, dev->symlink); |
61219c75 GKH |
546 | dbg("found matching id '%s', '%s' becomes '%s'", |
547 | dev->id, class_dev->name, udev->name); | |
ca1cc0fe GKH |
548 | return 0; |
549 | } | |
550 | return -ENODEV; | |
551 | } | |
552 | ||
8c51bbfe GKH |
553 | static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
554 | { | |
555 | struct config_device *dev; | |
556 | struct list_head *tmp; | |
557 | char path[SYSFS_PATH_MAX]; | |
558 | int found; | |
559 | char *temp = NULL; | |
560 | ||
561 | /* we have to have a sysfs device for TOPOLOGY to work */ | |
562 | if (!sysfs_device) | |
563 | return -ENODEV; | |
564 | ||
565 | list_for_each(tmp, &config_device_list) { | |
566 | dev = list_entry(tmp, struct config_device, node); | |
567 | if (dev->type != TOPOLOGY) | |
568 | continue; | |
569 | ||
19feb351 | 570 | dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); |
137af0cc GKH |
571 | if (strcasecmp(dev->bus, sysfs_device->bus) != 0) |
572 | continue; | |
573 | ||
20ff86bd | 574 | found = 0; |
70033702 | 575 | strfieldcpy(path, sysfs_device->path); |
8c51bbfe | 576 | temp = strrchr(path, '/'); |
19feb351 | 577 | dbg("search '%s' in '%s', path='%s'", dev->place, temp, path); |
8c51bbfe GKH |
578 | if (strstr(temp, dev->place) != NULL) { |
579 | found = 1; | |
580 | } else { | |
581 | *temp = 0x00; | |
582 | temp = strrchr(path, '/'); | |
19feb351 | 583 | dbg("search '%s' in '%s', path='%s'", dev->place, temp, path); |
8c51bbfe GKH |
584 | if (strstr(temp, dev->place) != NULL) |
585 | found = 1; | |
586 | } | |
587 | if (!found) | |
588 | continue; | |
589 | ||
70033702 | 590 | strfieldcpy(udev->name, dev->name); |
3d150dfb | 591 | strfieldcpy(udev->symlink, dev->symlink); |
61219c75 GKH |
592 | dbg("found matching place '%s', '%s' becomes '%s'", |
593 | dev->place, class_dev->name, udev->name); | |
8c51bbfe GKH |
594 | return 0; |
595 | } | |
596 | return -ENODEV; | |
597 | } | |
598 | ||
137af0cc | 599 | static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
120d45d0 GKH |
600 | { |
601 | struct config_device *dev; | |
602 | struct list_head *tmp; | |
603 | ||
604 | list_for_each(tmp, &config_device_list) { | |
605 | dev = list_entry(tmp, struct config_device, node); | |
606 | if (dev->type != REPLACE) | |
607 | continue; | |
608 | ||
19feb351 | 609 | dbg("compare name '%s' with '%s'", dev->kernel_name, class_dev->name); |
9f1da361 | 610 | if (strcmp_pattern(dev->kernel_name, class_dev->name) != 0) |
120d45d0 GKH |
611 | continue; |
612 | ||
70033702 | 613 | strfieldcpy(udev->name, dev->name); |
3d150dfb | 614 | strfieldcpy(udev->symlink, dev->symlink); |
61219c75 | 615 | dbg("found name, '%s' becomes '%s'", dev->kernel_name, udev->name); |
120d45d0 GKH |
616 | |
617 | return 0; | |
618 | } | |
619 | return -ENODEV; | |
620 | } | |
621 | ||
09e52d51 KS |
622 | static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice *udev) |
623 | { | |
61219c75 | 624 | /* heh, this is pretty simple... */ |
09e52d51 | 625 | strfieldcpy(udev->name, class_dev->name); |
09e52d51 KS |
626 | } |
627 | ||
dac056aa | 628 | static struct sysfs_device *get_sysfs_device(struct sysfs_class_device *class_dev) |
185a35a4 | 629 | { |
dac056aa GKH |
630 | struct sysfs_device *sysfs_device; |
631 | struct sysfs_class_device *class_dev_parent; | |
632 | int loop; | |
dac056aa GKH |
633 | |
634 | /* FIXME!!! */ | |
635 | /* This is needed here as we can easily out-race the placement of the | |
636 | * device symlink by the kernel. The call to sleep(1); will be removed | |
637 | * once libsysfs can be queried for sysfs_get_classdev_device() | |
638 | * multiple times and have it return the proper information when the | |
639 | * class device really shows up. For now, we live with the time | |
640 | * delay... | |
641 | */ | |
1edbb8d3 GKH |
642 | sleep(1); |
643 | ||
644 | #if 0 /* FIXME | |
645 | Something like this could also work, but for some reason doesn't, | |
646 | I also tried just stat() on the device symlink, but that still | |
647 | has nasty races, I'm probably doing something stupid... :( */ | |
dac056aa GKH |
648 | loop = 10; |
649 | while (loop--) { | |
650 | struct stat buf; | |
1edbb8d3 GKH |
651 | int retval; |
652 | char filename[SYSFS_PATH_MAX + 6]; | |
dac056aa GKH |
653 | |
654 | strcpy(filename, class_dev->path); | |
655 | strcat(filename, "/device"); | |
656 | dbg("looking for '%s'", filename); | |
657 | retval = stat(filename, &buf); | |
658 | if (!retval) | |
659 | break; | |
1edbb8d3 | 660 | |
dac056aa GKH |
661 | /* bah, let's go backwards up a level to see if the device is there, |
662 | * as block partitions don't point to the physical device. Need to fix that | |
663 | * up in the kernel... | |
664 | */ | |
665 | if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { | |
666 | if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { | |
1edbb8d3 GKH |
667 | class_dev_parent = sysfs_get_classdev_parent(class_dev); |
668 | if (class_dev_parent == NULL) { | |
669 | dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); | |
670 | } else { | |
671 | strcpy(filename, class_dev_parent->path); | |
672 | strcat(filename, "/device"); | |
673 | dbg("looking for '%s'", filename); | |
674 | retval = stat(filename, &buf); | |
675 | if (!retval) | |
676 | break; | |
dac056aa GKH |
677 | } |
678 | } | |
679 | } | |
dac056aa GKH |
680 | /* sleep to give the kernel a chance to create the device file */ |
681 | sleep(1); | |
682 | } | |
1edbb8d3 | 683 | #endif |
dac056aa GKH |
684 | loop = 1; /* FIXME put a real value in here for when everything is fixed... */ |
685 | while (loop--) { | |
686 | /* find the sysfs_device for this class device */ | |
687 | /* Wouldn't it really be nice if libsysfs could do this for us? */ | |
688 | sysfs_device = sysfs_get_classdev_device(class_dev); | |
689 | if (sysfs_device != NULL) | |
690 | goto exit; | |
7bd22a78 | 691 | |
7bd22a78 GKH |
692 | /* bah, let's go backwards up a level to see if the device is there, |
693 | * as block partitions don't point to the physical device. Need to fix that | |
694 | * up in the kernel... | |
695 | */ | |
5d4754f1 | 696 | if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { |
19feb351 | 697 | dbg("looking at block device"); |
7bd22a78 | 698 | if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { |
19feb351 | 699 | dbg("really is a partition"); |
dac056aa | 700 | class_dev_parent = sysfs_get_classdev_parent(class_dev); |
7bd22a78 | 701 | if (class_dev_parent == NULL) { |
5d4754f1 | 702 | dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); |
7bd22a78 | 703 | } else { |
19feb351 | 704 | dbg("class_dev_parent->name='%s'", class_dev_parent->name); |
5d4754f1 | 705 | sysfs_device = sysfs_get_classdev_device(class_dev_parent); |
dac056aa GKH |
706 | if (sysfs_device != NULL) |
707 | goto exit; | |
7bd22a78 GKH |
708 | } |
709 | } | |
710 | } | |
dac056aa | 711 | /* sleep to give the kernel a chance to create the link */ |
1edbb8d3 | 712 | sleep(1); |
dac056aa | 713 | |
7bd22a78 | 714 | } |
1edbb8d3 | 715 | dbg("Timed out waiting for device symlink, continuing on anyway..."); |
dac056aa GKH |
716 | exit: |
717 | return sysfs_device; | |
718 | } | |
719 | ||
720 | int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev) | |
721 | { | |
722 | struct sysfs_device *sysfs_device = NULL; | |
723 | struct sysfs_class_device *class_dev_parent = NULL; | |
724 | int retval = 0; | |
725 | struct perm_device *perm; | |
3e540368 | 726 | char *pos; |
dac056aa GKH |
727 | |
728 | udev->mode = 0; | |
28d6536a | 729 | |
dac056aa GKH |
730 | /* find the sysfs_device associated with this class device */ |
731 | sysfs_device = get_sysfs_device(class_dev); | |
7bd22a78 | 732 | if (sysfs_device) { |
19feb351 GKH |
733 | dbg("sysfs_device->path='%s'", sysfs_device->path); |
734 | dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); | |
735 | dbg("sysfs_device->bus='%s'", sysfs_device->bus); | |
f3b04a2e | 736 | strfieldcpy(udev->bus_id, sysfs_device->bus_id); |
dac056aa | 737 | wait_for_device_to_initialize(sysfs_device); |
03e64c8f | 738 | } else { |
19feb351 | 739 | dbg("class_dev->name = '%s'", class_dev->name); |
03e64c8f | 740 | } |
120d45d0 | 741 | |
3e540368 KS |
742 | strfieldcpy(udev->kernel_name, class_dev->name); |
743 | ||
744 | /* get kernel number */ | |
745 | pos = class_dev->name + strlen(class_dev->name); | |
746 | while (isdigit(*(pos-1))) | |
747 | pos--; | |
748 | strfieldcpy(udev->kernel_number, pos); | |
749 | dbg("kernel_number='%s'", udev->kernel_number); | |
f3b04a2e | 750 | |
120d45d0 | 751 | /* rules are looked at in priority order */ |
137af0cc | 752 | retval = do_callout(class_dev, udev, sysfs_device); |
120d45d0 | 753 | if (retval == 0) |
09e52d51 | 754 | goto found; |
120d45d0 | 755 | |
ca1cc0fe GKH |
756 | retval = do_label(class_dev, udev, sysfs_device); |
757 | if (retval == 0) | |
09e52d51 | 758 | goto found; |
ca1cc0fe GKH |
759 | |
760 | retval = do_number(class_dev, udev, sysfs_device); | |
761 | if (retval == 0) | |
09e52d51 | 762 | goto found; |
ca1cc0fe | 763 | |
8c51bbfe GKH |
764 | retval = do_topology(class_dev, udev, sysfs_device); |
765 | if (retval == 0) | |
09e52d51 | 766 | goto found; |
8c51bbfe | 767 | |
137af0cc | 768 | retval = do_replace(class_dev, udev, sysfs_device); |
120d45d0 | 769 | if (retval == 0) |
09e52d51 | 770 | goto found; |
120d45d0 | 771 | |
09e52d51 KS |
772 | do_kernelname(class_dev, udev); |
773 | goto done; | |
c2405f50 | 774 | |
09e52d51 | 775 | found: |
3d150dfb | 776 | /* substitute placeholder */ |
f3b04a2e | 777 | apply_format(udev, udev->name); |
3d150dfb | 778 | apply_format(udev, udev->symlink); |
98b88dbf | 779 | |
09e52d51 | 780 | done: |
61219c75 GKH |
781 | perm = find_perm(udev->name); |
782 | if (perm) { | |
783 | udev->mode = perm->mode; | |
784 | strfieldcpy(udev->owner, perm->owner); | |
785 | strfieldcpy(udev->group, perm->group); | |
786 | } else { | |
787 | /* no matching perms found :( */ | |
9d496c74 GKH |
788 | udev->mode = get_default_mode(class_dev); |
789 | udev->owner[0] = 0x00; | |
790 | udev->group[0] = 0x00; | |
615e05f8 | 791 | } |
61219c75 GKH |
792 | dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o", |
793 | udev->name, udev->owner, udev->group, udev->mode); | |
7bd22a78 | 794 | |
120d45d0 | 795 | return 0; |
185a35a4 GKH |
796 | } |
797 | ||
2232cac8 GKH |
798 | int namedev_init(void) |
799 | { | |
800 | int retval; | |
28d6536a | 801 | |
e8baccca | 802 | retval = namedev_init_rules(); |
2232cac8 GKH |
803 | if (retval) |
804 | return retval; | |
805 | ||
806 | retval = namedev_init_permissions(); | |
807 | if (retval) | |
808 | return retval; | |
809 | ||
19feb351 | 810 | dump_config_dev_list(); |
61219c75 | 811 | dump_perm_dev_list(); |
2232cac8 GKH |
812 | return retval; |
813 | } |