blob: b1a69444ecf96ed32cbbe52eda238b906a0ee85e [file] [log] [blame]
The Android Open Source Project8ac3a132009-01-20 14:04:01 -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
The Android Open Source Project13f797d2009-02-10 15:44:07 -080074static boolean low_batt = false;
The Android Open Source Project261ed752009-02-19 10:57:36 -080075static boolean door_open = true;
The Android Open Source Project13f797d2009-02-10 15:44:07 -080076
The Android Open Source Project8ac3a132009-01-20 14:04:01 -080077int 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) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -080090 LOGE("Error receiving uevent (%s)", strerror(errno));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -080091 return -errno;
92 }
93
94 if (!(event = malloc(sizeof(struct uevent)))) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -080095 LOGE("Error allocating memory (%s)", strerror(errno));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -080096 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 }
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800126 s+= strlen(s) + 1;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800127 }
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)))) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800142 LOGE("Error allocating memory (%s)", strerror(errno));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800143 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 {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800157 LOGE("Invalid action '%s'", action);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800158 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
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800178#if DEBUG_UEVENT
179 dump_uevent(event);
180#endif
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800181 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
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800187 LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800188#endif
189 return 0;
190}
191
192static void dump_uevent(struct uevent *event)
193{
194 int i;
195
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800196 LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s",
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800197 event->seqnum, event->subsystem, event->action, event->path);
198 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
199 if (!event->param[i])
200 break;
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800201 LOG_VOL("%s", event->param[i]);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800202 }
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
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800229 LOGE("get_uevent_param(): No parameter '%s' found", param_name);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800230 return NULL;
231}
232
233/*
234 * ---------------
235 * Uevent Handlers
236 * ---------------
237 */
238
239static int handle_powersupply_event(struct uevent *event)
240{
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800241 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 Project1b8e5a62009-02-13 12:57:54 -0800251LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800252 volmgr_safe_mode(low_batt || door_open);
253 }
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800254 return 0;
255}
256
257static int handle_switch_event(struct uevent *event)
258{
259 char *name = get_uevent_param(event, "SWITCH_NAME");
260 char *state = get_uevent_param(event, "SWITCH_STATE");
261
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800262
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800263 if (!strcmp(name, "usb_mass_storage")) {
264 if (!strcmp(state, "online")) {
265 ums_hostconnected_set(true);
266 } else {
267 ums_hostconnected_set(false);
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800268 volmgr_enable_ums(false);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800269 }
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800270 } else if (!strcmp(name, "sd-door")) {
271 if (!strcmp(state, "open"))
272 door_open = true;
273 else
274 door_open = false;
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800275LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800276 volmgr_safe_mode(low_batt || door_open);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800277 } else
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800278 LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800279
280 return 0;
281}
282
283static int handle_battery_event(struct uevent *event)
284{
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800285 return 0;
286}
287
288static int handle_block_event(struct uevent *event)
289{
290 char mediapath[255];
291 media_t *media;
292 int n;
293 int maj, min;
294 blkdev_t *blkdev;
295
296 /*
297 * Look for backing media for this block device
298 */
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800299 if (!strncmp(get_uevent_param(event, "DEVPATH"),
300 "/devices/virtual/",
301 strlen("/devices/virtual/"))) {
302 n = 0;
303 } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800304 n = 2;
305 else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
306 n = 3;
307 else {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800308 LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800309 return -EINVAL;
310 }
311
312 truncate_sysfs_path(event->path, n, mediapath);
313
314 if (!(media = media_lookup_by_path(mediapath, false))) {
315#if DEBUG_UEVENT
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800316 LOG_VOL("No backend media found @ device path '%s'", mediapath);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800317#endif
318 return 0;
319 }
320
321 maj = atoi(get_uevent_param(event, "MAJOR"));
322 min = atoi(get_uevent_param(event, "MINOR"));
323
324 if (event->action == action_add) {
325 blkdev_t *disk;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800326
327 /*
328 * If there isn't a disk already its because *we*
329 * are the disk
330 */
331 disk = blkdev_lookup_by_devno(maj, 0);
332
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800333 if (!(blkdev = blkdev_create(disk,
334 event->path,
335 maj,
336 min,
337 media,
338 get_uevent_param(event, "DEVTYPE")))) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800339 LOGE("Unable to allocate new blkdev (%m)");
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800340 return -1;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800341 }
342
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800343 blkdev_refresh(blkdev);
344
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800345 /*
346 * Add the blkdev to media
347 */
348 int rc;
349 if ((rc = media_add_blkdev(media, blkdev)) < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800350 LOGE("Unable to add blkdev to card (%d)", rc);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800351 return rc;
352 }
353
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800354 LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d",
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800355 blkdev->major, blkdev->minor, media->name, mediapath,
356 blkdev_get_num_pending_partitions(blkdev->disk));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800357
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800358 if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
359 if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800360 LOGE("Volmgr failed to handle device (%d)", rc);
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800361 return rc;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800362 }
363 }
364 } else if (event->action == action_remove) {
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800365 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800366 return 0;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800367
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800368 LOG_VOL("Destroying blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800369 blkdev->minor, blkdev->devpath, media->name);
370 volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
371
372 } else if (event->action == action_change) {
373 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
374 return 0;
375
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800376 LOG_VOL("Modified blkdev %d.%d @ %s on media %s", blkdev->major,
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800377 blkdev->minor, blkdev->devpath, media->name);
378
379 blkdev_refresh(blkdev);
380 } else {
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800381#if DEBUG_UEVENT
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800382 LOG_VOL("No handler implemented for action %d", event->action);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800383#endif
384 }
385 return 0;
386}
387
388static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
389{
390 media_t *media = media_lookup_by_dev(dev);
391 if (media)
392 media_remove_blkdev(media, dev);
393 blkdev_destroy(dev);
394}
395
396static int handle_bdi_event(struct uevent *event)
397{
398 return 0;
399}
400
401static int handle_mmc_event(struct uevent *event)
402{
403 if (event->action == action_add) {
404 media_t *media;
405 char serial[80];
406 char *type;
407
408 /*
409 * Pull card information from sysfs
410 */
411 type = get_uevent_param(event, "MMC_TYPE");
412 if (strcmp(type, "SD") && strcmp(type, "MMC"))
413 return 0;
414
415 read_sysfs_var(serial, sizeof(serial), event->path, "serial");
416 if (!(media = media_create(event->path,
417 get_uevent_param(event, "MMC_NAME"),
418 serial,
419 media_mmc))) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800420 LOGE("Unable to allocate new media (%m)");
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800421 return -1;
422 }
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800423 LOG_VOL("New MMC card '%s' (serial %u) added @ %s", media->name,
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800424 media->serial, media->devpath);
425 } else if (event->action == action_remove) {
426 media_t *media;
427
428 if (!(media = media_lookup_by_path(event->path, false))) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800429 LOGE("Unable to lookup media '%s'", event->path);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800430 return -1;
431 }
432
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800433 LOG_VOL("MMC card '%s' (serial %u) @ %s removed", media->name,
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800434 media->serial, media->devpath);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800435 media_destroy(media);
436 } else {
437#if DEBUG_UEVENT
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800438 LOG_VOL("No handler implemented for action %d", event->action);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800439#endif
440 }
441
442 return 0;
443}