blob: dffe817073e029f42de1560add6c14dae5e4f6c9 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001
2/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <string.h>
19#include <stdlib.h>
20#include <errno.h>
21
22#include <sys/types.h>
23#include <sys/socket.h>
24
25#include "vold.h"
26#include "uevent.h"
27#include "mmc.h"
28#include "blkdev.h"
29#include "volmgr.h"
30#include "media.h"
31
32#define DEBUG_UEVENT 0
33
34#define UEVENT_PARAMS_MAX 32
35
36enum uevent_action { action_add, action_remove, action_change };
37
38struct uevent {
39 char *path;
40 enum uevent_action action;
41 char *subsystem;
42 char *param[UEVENT_PARAMS_MAX];
43 unsigned int seqnum;
44};
45
46struct uevent_dispatch {
47 char *subsystem;
48 int (* dispatch) (struct uevent *);
49};
50
51static void dump_uevent(struct uevent *);
52static int dispatch_uevent(struct uevent *event);
53static void free_uevent(struct uevent *event);
54static char *get_uevent_param(struct uevent *event, char *param_name);
55
56static int handle_powersupply_event(struct uevent *event);
57static int handle_switch_event(struct uevent *);
58static int handle_battery_event(struct uevent *);
59static int handle_mmc_event(struct uevent *);
60static int handle_block_event(struct uevent *);
61static int handle_bdi_event(struct uevent *);
62static void _cb_blkdev_ok_to_destroy(blkdev_t *dev);
63
64static struct uevent_dispatch dispatch_table[] = {
65 { "switch", handle_switch_event },
66 { "battery", handle_battery_event },
67 { "mmc", handle_mmc_event },
68 { "block", handle_block_event },
69 { "bdi", handle_bdi_event },
70 { "power_supply", handle_powersupply_event },
71 { NULL, NULL }
72};
73
74static boolean low_batt = false;
75static boolean door_open = true;
76
77int process_uevent_message(int socket)
78{
79 char buffer[64 * 1024]; // Thank god we're not in the kernel :)
80 int count;
81 char *s = buffer;
82 char *end;
83 struct uevent *event;
84 int param_idx = 0;
85 int i;
86 int first = 1;
87 int rc = 0;
88
89 if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) {
90 LOGE("Error receiving uevent (%s)", strerror(errno));
91 return -errno;
92 }
93
94 if (!(event = malloc(sizeof(struct uevent)))) {
95 LOGE("Error allocating memory (%s)", strerror(errno));
96 return -errno;
97 }
98
99 memset(event, 0, sizeof(struct uevent));
100
101 end = s + count;
102 while (s < end) {
103 if (first) {
104 char *p;
105 for (p = s; *p != '@'; p++);
106 p++;
107 event->path = strdup(p);
108 first = 0;
109 } else {
110 if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
111 char *a = s + strlen("ACTION=");
112
113 if (!strcmp(a, "add"))
114 event->action = action_add;
115 else if (!strcmp(a, "change"))
116 event->action = action_change;
117 else if (!strcmp(a, "remove"))
118 event->action = action_remove;
119 } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
120 event->seqnum = atoi(s + strlen("SEQNUM="));
121 else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
122 event->subsystem = strdup(s + strlen("SUBSYSTEM="));
123 else
124 event->param[param_idx++] = strdup(s);
125 }
126 s+= strlen(s) + 1;
127 }
128
129 rc = dispatch_uevent(event);
130
131 free_uevent(event);
132 return rc;
133}
134
135int simulate_uevent(char *subsys, char *path, char *action, char **params)
136{
137 struct uevent *event;
138 char tmp[255];
139 int i, rc;
140
141 if (!(event = malloc(sizeof(struct uevent)))) {
142 LOGE("Error allocating memory (%s)", strerror(errno));
143 return -errno;
144 }
145
146 memset(event, 0, sizeof(struct uevent));
147
148 event->subsystem = strdup(subsys);
149
150 if (!strcmp(action, "add"))
151 event->action = action_add;
152 else if (!strcmp(action, "change"))
153 event->action = action_change;
154 else if (!strcmp(action, "remove"))
155 event->action = action_remove;
156 else {
157 LOGE("Invalid action '%s'", action);
158 return -1;
159 }
160
161 event->path = strdup(path);
162
163 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
164 if (!params[i])
165 break;
166 event->param[i] = strdup(params[i]);
167 }
168
169 rc = dispatch_uevent(event);
170 free_uevent(event);
171 return rc;
172}
173
174static int dispatch_uevent(struct uevent *event)
175{
176 int i;
177
178#if DEBUG_UEVENT
179 dump_uevent(event);
180#endif
181 for (i = 0; dispatch_table[i].subsystem != NULL; i++) {
182 if (!strcmp(dispatch_table[i].subsystem, event->subsystem))
183 return dispatch_table[i].dispatch(event);
184 }
185
186#if DEBUG_UEVENT
187 LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem);
188#endif
189 return 0;
190}
191
192static void dump_uevent(struct uevent *event)
193{
194 int i;
195
196 LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s",
197 event->seqnum, event->subsystem, event->action, event->path);
198 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
199 if (!event->param[i])
200 break;
201 LOG_VOL("%s", event->param[i]);
202 }
203}
204
205static void free_uevent(struct uevent *event)
206{
207 int i;
208 free(event->path);
209 free(event->subsystem);
210 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
211 if (!event->param[i])
212 break;
213 free(event->param[i]);
214 }
215 free(event);
216}
217
218static char *get_uevent_param(struct uevent *event, char *param_name)
219{
220 int i;
221
222 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
223 if (!event->param[i])
224 break;
225 if (!strncmp(event->param[i], param_name, strlen(param_name)))
226 return &event->param[i][strlen(param_name) + 1];
227 }
228
229 LOGE("get_uevent_param(): No parameter '%s' found", param_name);
230 return NULL;
231}
232
233/*
234 * ---------------
235 * Uevent Handlers
236 * ---------------
237 */
238
239static int handle_powersupply_event(struct uevent *event)
240{
241 char *ps_type = get_uevent_param(event, "POWER_SUPPLY_TYPE");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800242
243 if (!strcasecmp(ps_type, "battery")) {
The Android Open Source Projectf614d642009-03-18 17:39:49 -0700244 char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800245 int capacity = atoi(ps_cap);
246
247 if (capacity < 5)
248 low_batt = true;
249 else
250 low_batt = false;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800251 volmgr_safe_mode(low_batt || door_open);
252 }
253 return 0;
254}
255
256static int handle_switch_event(struct uevent *event)
257{
258 char *name = get_uevent_param(event, "SWITCH_NAME");
259 char *state = get_uevent_param(event, "SWITCH_STATE");
260
261
262 if (!strcmp(name, "usb_mass_storage")) {
263 if (!strcmp(state, "online")) {
264 ums_hostconnected_set(true);
265 } else {
266 ums_hostconnected_set(false);
267 volmgr_enable_ums(false);
268 }
269 } else if (!strcmp(name, "sd-door")) {
270 if (!strcmp(state, "open"))
271 door_open = true;
272 else
273 door_open = false;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800274 volmgr_safe_mode(low_batt || door_open);
San Mehat9ad18f02009-06-30 18:15:09 -0700275 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800276
277 return 0;
278}
279
280static int handle_battery_event(struct uevent *event)
281{
282 return 0;
283}
284
285static int handle_block_event(struct uevent *event)
286{
287 char mediapath[255];
288 media_t *media;
289 int n;
290 int maj, min;
291 blkdev_t *blkdev;
292
293 /*
294 * Look for backing media for this block device
295 */
296 if (!strncmp(get_uevent_param(event, "DEVPATH"),
297 "/devices/virtual/",
298 strlen("/devices/virtual/"))) {
299 n = 0;
300 } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
301 n = 2;
302 else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
303 n = 3;
304 else {
305 LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
306 return -EINVAL;
307 }
308
The Android Open Source Projectf614d642009-03-18 17:39:49 -0700309 truncate_sysfs_path(event->path, n, mediapath, sizeof(mediapath));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800310
311 if (!(media = media_lookup_by_path(mediapath, false))) {
312#if DEBUG_UEVENT
313 LOG_VOL("No backend media found @ device path '%s'", mediapath);
314#endif
315 return 0;
316 }
317
318 maj = atoi(get_uevent_param(event, "MAJOR"));
319 min = atoi(get_uevent_param(event, "MINOR"));
320
321 if (event->action == action_add) {
322 blkdev_t *disk;
323
324 /*
325 * If there isn't a disk already its because *we*
326 * are the disk
327 */
328 disk = blkdev_lookup_by_devno(maj, 0);
329
330 if (!(blkdev = blkdev_create(disk,
331 event->path,
332 maj,
333 min,
334 media,
335 get_uevent_param(event, "DEVTYPE")))) {
San Mehat1f278212009-07-16 10:44:15 -0700336 LOGE("Unable to allocate new blkdev (%s)", strerror(errno));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800337 return -1;
338 }
339
340 blkdev_refresh(blkdev);
341
342 /*
343 * Add the blkdev to media
344 */
345 int rc;
346 if ((rc = media_add_blkdev(media, blkdev)) < 0) {
347 LOGE("Unable to add blkdev to card (%d)", rc);
348 return rc;
349 }
350
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700351 LOGI("New blkdev %d.%d on media %s, media path %s, Dpp %d",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800352 blkdev->major, blkdev->minor, media->name, mediapath,
353 blkdev_get_num_pending_partitions(blkdev->disk));
354
355 if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
356 if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
San Mehat1f278212009-07-16 10:44:15 -0700357 if (rc == -EBUSY) {
358 LOGI("Volmgr not ready to handle device");
359 } else {
360 LOGE("Volmgr failed to handle device (%d)", rc);
361 return rc;
362 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800363 }
364 }
365 } else if (event->action == action_remove) {
366 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
367 return 0;
368
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700369 LOGI("Destroying blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800370 blkdev->minor, blkdev->devpath, media->name);
371 volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
372
373 } else if (event->action == action_change) {
374 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
375 return 0;
376
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700377 LOGI("Modified blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800378 blkdev->minor, blkdev->devpath, media->name);
379
380 blkdev_refresh(blkdev);
381 } else {
382#if DEBUG_UEVENT
383 LOG_VOL("No handler implemented for action %d", event->action);
384#endif
385 }
386 return 0;
387}
388
389static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
390{
391 media_t *media = media_lookup_by_dev(dev);
392 if (media)
393 media_remove_blkdev(media, dev);
394 blkdev_destroy(dev);
395}
396
397static int handle_bdi_event(struct uevent *event)
398{
399 return 0;
400}
401
402static int handle_mmc_event(struct uevent *event)
403{
404 if (event->action == action_add) {
405 media_t *media;
406 char serial[80];
407 char *type;
408
409 /*
410 * Pull card information from sysfs
411 */
412 type = get_uevent_param(event, "MMC_TYPE");
413 if (strcmp(type, "SD") && strcmp(type, "MMC"))
414 return 0;
415
416 read_sysfs_var(serial, sizeof(serial), event->path, "serial");
417 if (!(media = media_create(event->path,
418 get_uevent_param(event, "MMC_NAME"),
419 serial,
420 media_mmc))) {
San Mehat1f278212009-07-16 10:44:15 -0700421 LOGE("Unable to allocate new media (%s)", strerror(errno));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800422 return -1;
423 }
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700424 LOGI("New MMC card '%s' (serial %u) added @ %s", media->name,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800425 media->serial, media->devpath);
426 } else if (event->action == action_remove) {
427 media_t *media;
428
429 if (!(media = media_lookup_by_path(event->path, false))) {
430 LOGE("Unable to lookup media '%s'", event->path);
431 return -1;
432 }
433
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700434 LOGI("MMC card '%s' (serial %u) @ %s removed", media->name,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800435 media->serial, media->devpath);
436 media_destroy(media);
437 } else {
438#if DEBUG_UEVENT
439 LOG_VOL("No handler implemented for action %d", event->action);
440#endif
441 }
442
443 return 0;
444}