blob: e19506679f9a1471077106549ffdf5b111d668a2 [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;
75static boolean door_open = false;
76
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) {
90 LOGE("Error receiving uevent (%s)\n", strerror(errno));
91 return -errno;
92 }
93
94 if (!(event = malloc(sizeof(struct uevent)))) {
95 LOGE("Error allocating memory (%s)\n", 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 }
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)))) {
142 LOGE("Error allocating memory (%s)\n", 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'\n", 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
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
187 LOG_VOL("No uevent handlers registered for '%s' subsystem\n", 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\n",
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\n", 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\n", param_name);
230 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;
251LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d\n", low_batt, door_open);
252 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;
275LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d\n", low_batt, door_open);
276 volmgr_safe_mode(low_batt || door_open);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800277 } else
278 LOG_VOL("handle_switch_event(): Ignoring switch '%s'\n", name);
279
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 */
299 if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
300 n = 2;
301 else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
302 n = 3;
303 else {
304 LOGE("Bad blockdev type '%s'\n", get_uevent_param(event, "DEVTYPE"));
305 return -EINVAL;
306 }
307
308 truncate_sysfs_path(event->path, n, mediapath);
309
310 if (!(media = media_lookup_by_path(mediapath, false))) {
311#if DEBUG_UEVENT
312 LOG_VOL("No backend media found @ device path '%s'\n", mediapath);
313#endif
314 return 0;
315 }
316
317 maj = atoi(get_uevent_param(event, "MAJOR"));
318 min = atoi(get_uevent_param(event, "MINOR"));
319
320 if (event->action == action_add) {
321 blkdev_t *disk;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800322
323 /*
324 * If there isn't a disk already its because *we*
325 * are the disk
326 */
327 disk = blkdev_lookup_by_devno(maj, 0);
328
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800329 if (!(blkdev = blkdev_create(disk,
330 event->path,
331 maj,
332 min,
333 media,
334 get_uevent_param(event, "DEVTYPE")))) {
335 LOGE("Unable to allocate new blkdev (%m)\n");
336 return -1;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800337 }
338
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800339 blkdev_refresh(blkdev);
340
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800341 /*
342 * Add the blkdev to media
343 */
344 int rc;
345 if ((rc = media_add_blkdev(media, blkdev)) < 0) {
346 LOGE("Unable to add blkdev to card (%d)\n", rc);
347 return rc;
348 }
349
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800350 LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d\n",
351 blkdev->major, blkdev->minor, media->name, mediapath,
352 blkdev_get_num_pending_partitions(blkdev->disk));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800353
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800354 if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
355 if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
356 LOGE("Volmgr failed to handle device (%d)\n", rc);
357 return rc;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800358 }
359 }
360 } else if (event->action == action_remove) {
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800361 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800362 return 0;
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800363
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800364 LOG_VOL("Destroying blkdev %d.%d @ %s on media %s\n", blkdev->major,
365 blkdev->minor, blkdev->devpath, media->name);
366 volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
367
368 } else if (event->action == action_change) {
369 if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
370 return 0;
371
372 LOG_VOL("Modified blkdev %d.%d @ %s on media %s\n", blkdev->major,
373 blkdev->minor, blkdev->devpath, media->name);
374
375 blkdev_refresh(blkdev);
376 } else {
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800377#if DEBUG_UEVENT
378 LOG_VOL("No handler implemented for action %d\n", event->action);
379#endif
380 }
381 return 0;
382}
383
384static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
385{
386 media_t *media = media_lookup_by_dev(dev);
387 if (media)
388 media_remove_blkdev(media, dev);
389 blkdev_destroy(dev);
390}
391
392static int handle_bdi_event(struct uevent *event)
393{
394 return 0;
395}
396
397static int handle_mmc_event(struct uevent *event)
398{
399 if (event->action == action_add) {
400 media_t *media;
401 char serial[80];
402 char *type;
403
404 /*
405 * Pull card information from sysfs
406 */
407 type = get_uevent_param(event, "MMC_TYPE");
408 if (strcmp(type, "SD") && strcmp(type, "MMC"))
409 return 0;
410
411 read_sysfs_var(serial, sizeof(serial), event->path, "serial");
412 if (!(media = media_create(event->path,
413 get_uevent_param(event, "MMC_NAME"),
414 serial,
415 media_mmc))) {
416 LOGE("Unable to allocate new media (%m)\n");
417 return -1;
418 }
419 LOG_VOL("New MMC card '%s' (serial %u) added @ %s\n", media->name,
420 media->serial, media->devpath);
421 } else if (event->action == action_remove) {
422 media_t *media;
423
424 if (!(media = media_lookup_by_path(event->path, false))) {
425 LOGE("Unable to lookup media '%s'\n", event->path);
426 return -1;
427 }
428
429 LOG_VOL("MMC card '%s' (serial %u) @ %s removed\n", media->name,
430 media->serial, media->devpath);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800431 media_destroy(media);
432 } else {
433#if DEBUG_UEVENT
434 LOG_VOL("No handler implemented for action %d\n", event->action);
435#endif
436 }
437
438 return 0;
439}