blob: d16f3157f3527b3f6bad420c17b691e917424255 [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");
242 char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY");
243
244 if (!strcasecmp(ps_type, "battery")) {
245 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);
275 } else
276 LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name);
277
278 return 0;
279}
280
281static int handle_battery_event(struct uevent *event)
282{
283 return 0;
284}
285
286static int handle_block_event(struct uevent *event)
287{
288 char mediapath[255];
289 media_t *media;
290 int n;
291 int maj, min;
292 blkdev_t *blkdev;
293
294 /*
295 * Look for backing media for this block device
296 */
297 if (!strncmp(get_uevent_param(event, "DEVPATH"),
298 "/devices/virtual/",
299 strlen("/devices/virtual/"))) {
300 n = 0;
301 } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
302 n = 2;
303 else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
304 n = 3;
305 else {
306 LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
307 return -EINVAL;
308 }
309
310 truncate_sysfs_path(event->path, n, mediapath);
311
312 if (!(media = media_lookup_by_path(mediapath, false))) {
313#if DEBUG_UEVENT
314 LOG_VOL("No backend media found @ device path '%s'", mediapath);
315#endif
316 return 0;
317 }
318
319 maj = atoi(get_uevent_param(event, "MAJOR"));
320 min = atoi(get_uevent_param(event, "MINOR"));
321
322 if (event->action == action_add) {
323 blkdev_t *disk;
324
325 /*
326 * If there isn't a disk already its because *we*
327 * are the disk
328 */
329 disk = blkdev_lookup_by_devno(maj, 0);
330
331 if (!(blkdev = blkdev_create(disk,
332 event->path,
333 maj,
334 min,
335 media,
336 get_uevent_param(event, "DEVTYPE")))) {
337 LOGE("Unable to allocate new blkdev (%m)");
338 return -1;
339 }
340
341 blkdev_refresh(blkdev);
342
343 /*
344 * Add the blkdev to media
345 */
346 int rc;
347 if ((rc = media_add_blkdev(media, blkdev)) < 0) {
348 LOGE("Unable to add blkdev to card (%d)", rc);
349 return rc;
350 }
351
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700352 LOGI("New blkdev %d.%d on media %s, media path %s, Dpp %d",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800353 blkdev->major, blkdev->minor, media->name, mediapath,
354 blkdev_get_num_pending_partitions(blkdev->disk));
355
356 if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
357 if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
358 LOGE("Volmgr failed to handle device (%d)", rc);
359 return rc;
360 }
361 }
362 } else if (event->action == action_remove) {
363 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
364 return 0;
365
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700366 LOGI("Destroying blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800367 blkdev->minor, blkdev->devpath, media->name);
368 volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
369
370 } else if (event->action == action_change) {
371 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
372 return 0;
373
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700374 LOGI("Modified blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800375 blkdev->minor, blkdev->devpath, media->name);
376
377 blkdev_refresh(blkdev);
378 } else {
379#if DEBUG_UEVENT
380 LOG_VOL("No handler implemented for action %d", event->action);
381#endif
382 }
383 return 0;
384}
385
386static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
387{
388 media_t *media = media_lookup_by_dev(dev);
389 if (media)
390 media_remove_blkdev(media, dev);
391 blkdev_destroy(dev);
392}
393
394static int handle_bdi_event(struct uevent *event)
395{
396 return 0;
397}
398
399static int handle_mmc_event(struct uevent *event)
400{
401 if (event->action == action_add) {
402 media_t *media;
403 char serial[80];
404 char *type;
405
406 /*
407 * Pull card information from sysfs
408 */
409 type = get_uevent_param(event, "MMC_TYPE");
410 if (strcmp(type, "SD") && strcmp(type, "MMC"))
411 return 0;
412
413 read_sysfs_var(serial, sizeof(serial), event->path, "serial");
414 if (!(media = media_create(event->path,
415 get_uevent_param(event, "MMC_NAME"),
416 serial,
417 media_mmc))) {
418 LOGE("Unable to allocate new media (%m)");
419 return -1;
420 }
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700421 LOGI("New MMC card '%s' (serial %u) added @ %s", media->name,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800422 media->serial, media->devpath);
423 } else if (event->action == action_remove) {
424 media_t *media;
425
426 if (!(media = media_lookup_by_path(event->path, false))) {
427 LOGE("Unable to lookup media '%s'", event->path);
428 return -1;
429 }
430
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700431 LOGI("MMC card '%s' (serial %u) @ %s removed", media->name,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800432 media->serial, media->devpath);
433 media_destroy(media);
434 } else {
435#if DEBUG_UEVENT
436 LOG_VOL("No handler implemented for action %d", event->action);
437#endif
438 }
439
440 return 0;
441}