add files
[idlebox.git] / device.c
blob8f2efc1f3ea8d23b8a07b620274c2a26acb18db3
1 #include <string.h>
2 #include <sys/socket.h>
3 #include <linux/netlink.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <stdarg.h>
8 #include "device.h"
9 #include "log.h"
11 int device_init(void)
13 int fd;
15 fd = open_uevent_socket();
16 if(fd < 0)
17 return -1;
19 fcntl(fd, F_SETFD, FD_CLOEXEC);
20 fcntl(fd, F_SETFL, O_NONBLOCK);
22 coldboot(fd, "/sys/class");
23 coldboot(fd, "/sys/block");
24 coldboot(fd, "/sys/devices");
26 return fd;
29 int open_uevent_socket(void)
31 struct sockaddr_nl addr;
32 int sz = 64*1024;
33 int s;
35 memset(&addr, 0, sizeof(addr));
36 addr.nl_family = AF_NETLINK;
37 addr.nl_pid = getpid();
38 addr.nl_groups = 0xffffffff;
40 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
41 if(s < 0)
42 return -1;
44 setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
46 if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
47 close(s);
48 return -1;
51 return s;
54 void coldboot(int event_fd, const char *path)
56 DIR *d = opendir(path);
57 if(d) {
58 do_coldboot(event_fd, d);
59 closedir(d);
63 void do_coldboot(int event_fd, DIR *d)
65 struct dirent *de;
66 int dfd, fd;
68 dfd = dirfd(d);
70 fd = openat(dfd, "uevent", O_WRONLY);
71 if(fd >= 0) {
72 write(fd, "add\n", 4);
73 close(fd);
74 handle_device_fd(event_fd);
77 while((de = readdir(d))) {
78 DIR *d2;
80 if(de->d_type != DT_DIR || de->d_name[0] == '.')
81 continue;
83 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
84 if(fd < 0)
85 continue;
87 d2 = fdopendir(fd);
88 if(d2 == 0)
89 close(fd);
90 else {
91 do_coldboot(event_fd, d2);
92 closedir(d2);
97 #define UEVENT_MSG_LEN 1024
98 void handle_device_fd(int fd)
100 char msg[UEVENT_MSG_LEN+2];
101 int n;
103 while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) {
104 struct uevent uevent;
106 if(n == UEVENT_MSG_LEN) /* overflow -- discard */
107 continue;
109 msg[n] = '\0';
110 msg[n+1] = '\0';
112 parse_event(msg, &uevent);
114 handle_device_event(&uevent);
115 //handle_firmware_event(&uevent);
119 void parse_event(const char *msg, struct uevent *uevent)
121 uevent->action = "";
122 uevent->path = "";
123 uevent->subsystem = "";
124 uevent->firmware = "";
125 uevent->major = -1;
126 uevent->minor = -1;
128 /* currently ignoring SEQNUM */
129 while(*msg) {
130 if(!strncmp(msg, "ACTION=", 7)) {
131 msg += 7;
132 uevent->action = msg;
133 } else if(!strncmp(msg, "DEVPATH=", 8)) {
134 msg += 8;
135 uevent->path = msg;
136 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
137 msg += 10;
138 uevent->subsystem = msg;
139 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
140 msg += 9;
141 uevent->firmware = msg;
142 } else if(!strncmp(msg, "MAJOR=", 6)) {
143 msg += 6;
144 uevent->major = atoi(msg);
145 } else if(!strncmp(msg, "MINOR=", 6)) {
146 msg += 6;
147 uevent->minor = atoi(msg);
150 /* advance to after the next \0 */
151 while(*msg++)
155 /*INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
156 uevent->action, uevent->path, uevent->subsystem,
157 uevent->firmware, uevent->major, uevent->minor);*/
160 void handle_device_event(struct uevent *uevent)
162 char devpath[96];
163 char *base, *name;
164 int block;
166 /* if it's not a /dev device, nothing to do */
167 if((uevent->major < 0) || (uevent->minor < 0))
168 return;
170 /* do we have a name? */
171 name = strrchr(uevent->path, '/');
172 if(!name)
173 return;
174 name++;
176 /* too-long names would overrun our buffer */
177 if(strlen(name) > 64)
178 return;
180 /* are we block or char? where should we live? */
181 if(!strncmp(uevent->subsystem, "block", 5)) {
182 block = 1;
183 base = "/dev/block/";
184 mkdir(base, 0755);
185 } else {
186 block = 0;
187 /* this should probably be configurable somehow */
188 if(!strncmp(uevent->subsystem, "graphics", 8)) {
189 base = "/dev/graphics/";
190 mkdir(base, 0755);
191 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
192 base = "/dev/oncrpc/";
193 mkdir(base, 0755);
194 } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
195 base = "/dev/adsp/";
196 mkdir(base, 0755);
197 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
198 base = "/dev/msm_camera/";
199 mkdir(base, 0755);
200 } else if(!strncmp(uevent->subsystem, "input", 5)) {
201 base = "/dev/input/";
202 mkdir(base, 0755);
203 } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
204 base = "/dev/mtd/";
205 mkdir(base, 0755);
206 } else if(!strncmp(uevent->subsystem, "sound", 5)) {
207 base = "/dev/snd/";
208 mkdir(base, 0755);
209 } else if(!strncmp(uevent->subsystem, "misc", 4) &&
210 !strncmp(name, "log_", 4)) {
211 base = "/dev/log/";
212 mkdir(base, 0755);
213 name += 4;
214 } else
215 base = "/dev/";
218 snprintf(devpath, sizeof(devpath), "%s%s", base, name);
220 if(!strcmp(uevent->action, "add")) {
221 make_device(devpath, block, uevent->major, uevent->minor);
222 return;
225 if(!strcmp(uevent->action, "remove")) {
226 unlink(devpath);
227 return;
231 void make_device(const char *path, int block, int major, int minor)
233 unsigned uid;
234 unsigned gid;
235 mode_t mode;
236 dev_t dev;
238 if(major > 255 || minor > 255)
239 return;
241 mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
242 dev = (major << 8) | minor;
243 mknod(path, mode, dev);
244 chown(path, uid, gid);
247 mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
249 return 0777;