]>
Commit | Line | Data |
---|---|---|
ea733a2f GKH |
1 | /* |
2 | * udev-add.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 <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <stdio.h> | |
27 | #include <fcntl.h> | |
28 | #include <unistd.h> | |
29 | #include <errno.h> | |
30 | ||
31 | #include "udev.h" | |
32 | #include "udev_version.h" | |
33 | #include "namedev.h" | |
8e41d35d | 34 | #include "udevdb.h" |
ea733a2f GKH |
35 | #include "libsysfs/libsysfs.h" |
36 | ||
ea733a2f GKH |
37 | /* |
38 | * Right now the major/minor of a device is stored in a file called | |
39 | * "dev" in sysfs. | |
40 | * The number is stored as: | |
727d1ba5 | 41 | * MM:mm |
ea733a2f GKH |
42 | * MM is the major |
43 | * mm is the minor | |
727d1ba5 | 44 | * The value is in decimal. |
ea733a2f GKH |
45 | */ |
46 | static int get_major_minor(struct sysfs_class_device *class_dev, int *major, int *minor) | |
47 | { | |
ea256f90 | 48 | int retval = -ENODEV; |
ea733a2f GKH |
49 | |
50 | char *dev; | |
51 | ||
52 | dev = sysfs_get_value_from_attributes(class_dev->directory->attributes, "dev"); | |
53 | if (dev == NULL) | |
ea256f90 | 54 | goto exit; |
ea733a2f GKH |
55 | |
56 | dbg("dev = %s", dev); | |
57 | ||
ea256f90 GKH |
58 | if (sscanf(dev, "%u:%u", major, minor) != 2) |
59 | goto exit; | |
ea733a2f GKH |
60 | |
61 | dbg("found major = %d, minor = %d", *major, *minor); | |
62 | ||
63 | retval = 0; | |
ea256f90 | 64 | exit: |
ea733a2f GKH |
65 | return retval; |
66 | } | |
67 | ||
68 | /* | |
69 | * We also want to add some permissions here, and possibly some symlinks | |
70 | */ | |
5840bc63 | 71 | static int create_node(struct udevice *dev) |
ea733a2f | 72 | { |
ea733a2f GKH |
73 | char filename[255]; |
74 | int retval = 0; | |
5840bc63 | 75 | |
c056c514 | 76 | strncpy(filename, udev_root, sizeof(filename)); |
5840bc63 GKH |
77 | strncat(filename, dev->name, sizeof(filename)); |
78 | ||
79 | switch (dev->type) { | |
1331c889 | 80 | case 'b': |
5840bc63 | 81 | dev->mode |= S_IFBLK; |
1331c889 GKH |
82 | break; |
83 | case 'c': | |
84 | case 'u': | |
5840bc63 | 85 | dev->mode |= S_IFCHR; |
1331c889 GKH |
86 | break; |
87 | case 'p': | |
5840bc63 | 88 | dev->mode |= S_IFIFO; |
1331c889 GKH |
89 | break; |
90 | default: | |
5840bc63 | 91 | dbg("unknown node type %c\n", dev->type); |
1331c889 GKH |
92 | return -EINVAL; |
93 | } | |
0abf54fc | 94 | |
5840bc63 GKH |
95 | dbg("mknod(%s, %#o, %u, %u)", filename, dev->mode, dev->major, dev->minor); |
96 | retval = mknod(filename, dev->mode, makedev(dev->major, dev->minor)); | |
1331c889 GKH |
97 | if (retval) |
98 | dbg("mknod(%s, %#o, %u, %u) failed with error '%s'", | |
5840bc63 GKH |
99 | filename, dev->mode, dev->major, dev->minor, strerror(errno)); |
100 | ||
101 | // FIXME set the ownership of the node | |
ea733a2f GKH |
102 | return retval; |
103 | } | |
104 | ||
d7e954a4 | 105 | static struct sysfs_class_device *get_class_dev(char *device_name) |
ea733a2f GKH |
106 | { |
107 | char dev_path[SYSFS_PATH_MAX]; | |
63dde9f8 GKH |
108 | struct sysfs_class_device *class_dev = NULL; |
109 | ||
ea733a2f GKH |
110 | strcpy(dev_path, sysfs_path); |
111 | strcat(dev_path, device_name); | |
112 | ||
113 | dbg("looking at %s", dev_path); | |
114 | ||
115 | /* open up the sysfs class device for this thing... */ | |
116 | class_dev = sysfs_open_class_device(dev_path); | |
117 | if (class_dev == NULL) { | |
118 | dbg ("sysfs_open_class_device failed"); | |
63dde9f8 | 119 | goto exit; |
ea733a2f GKH |
120 | } |
121 | dbg("class_dev->name = %s", class_dev->name); | |
122 | ||
63dde9f8 | 123 | exit: |
ea733a2f GKH |
124 | return class_dev; |
125 | } | |
126 | ||
29fd7e67 GKH |
127 | /* wait for the "dev" file to show up in the directory in sysfs. |
128 | * If it doesn't happen in about 10 seconds, give up. | |
129 | */ | |
130 | #define SECONDS_TO_WAIT_FOR_DEV 10 | |
5840bc63 | 131 | int sleep_for_dev(char *path) |
29fd7e67 GKH |
132 | { |
133 | char filename[SYSFS_PATH_MAX + 6]; | |
134 | struct stat buf; | |
135 | int loop = 0; | |
136 | int retval = -ENODEV; | |
137 | ||
138 | strcpy(filename, sysfs_path); | |
5840bc63 | 139 | strcat(filename, path); |
29fd7e67 GKH |
140 | strcat(filename, "/dev"); |
141 | ||
142 | while (loop < SECONDS_TO_WAIT_FOR_DEV) { | |
143 | dbg("looking for %s", filename); | |
144 | retval = stat(filename, &buf); | |
145 | if (retval == 0) { | |
146 | retval = 0; | |
147 | goto exit; | |
148 | } | |
149 | ||
150 | /* sleep for a second or two to give the kernel a chance to | |
151 | * create the dev file */ | |
152 | sleep(1); | |
153 | } | |
154 | retval = -ENODEV; | |
155 | exit: | |
156 | return retval; | |
157 | } | |
158 | ||
5840bc63 | 159 | int udev_add_device(char *path, char *subsystem) |
ea733a2f GKH |
160 | { |
161 | struct sysfs_class_device *class_dev; | |
5840bc63 | 162 | struct udevice dev; |
ea733a2f | 163 | struct device_attr attr; |
ea733a2f GKH |
164 | int retval = -EINVAL; |
165 | ||
ea733a2f GKH |
166 | /* for now, the block layer is the only place where block devices are */ |
167 | if (strcmp(subsystem, "block") == 0) | |
5840bc63 | 168 | dev.type = 'b'; |
ea733a2f | 169 | else |
5840bc63 | 170 | dev.type = 'c'; |
ea733a2f | 171 | |
5840bc63 | 172 | retval = sleep_for_dev(path); |
29fd7e67 GKH |
173 | if (retval) |
174 | goto exit; | |
63dde9f8 | 175 | |
5840bc63 | 176 | class_dev = get_class_dev(path); |
ea733a2f GKH |
177 | if (class_dev == NULL) |
178 | goto exit; | |
179 | ||
180 | retval = namedev_name_device(class_dev, &attr); | |
181 | if (retval) | |
182 | return retval; | |
183 | ||
5840bc63 | 184 | retval = get_major_minor(class_dev, &dev.major, &dev.minor); |
ea733a2f | 185 | if (retval) { |
ca999860 | 186 | dbg("get_major_minor failed"); |
ea733a2f GKH |
187 | goto exit; |
188 | } | |
189 | ||
5840bc63 GKH |
190 | strcpy(dev.name, attr.name); |
191 | strcpy(dev.owner, attr.owner); | |
192 | strcpy(dev.group, attr.group); | |
193 | dev.mode = attr.mode; | |
194 | ||
195 | retval = udevdb_add_dev(path, &dev); | |
8e41d35d | 196 | if (retval != 0) |
5840bc63 | 197 | dbg("udevdb_add_dev failed, but we are going to try to create the node anyway. " |
ca999860 GKH |
198 | "But remove might not work properly for this device."); |
199 | ||
200 | sysfs_close_class_device(class_dev); | |
8e41d35d | 201 | |
5840bc63 GKH |
202 | dbg("name = %s", dev.name); |
203 | retval = create_node(&dev); | |
ea733a2f GKH |
204 | |
205 | exit: | |
206 | return retval; | |
207 | } | |
208 |