4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
23 #include <sys/mount.h>
24 #include <sys/statvfs.h>
29 #include <sys/statfs.h>
36 #include "core/device-handler.h"
37 #include "core/device-notifier.h"
38 #include "core/common.h"
39 #include "core/devices.h"
41 #include "mmc-handler.h"
43 #include "core/edbus-handler.h"
44 #include "core/list.h"
45 #include "core/config-parser.h"
47 #define VCONFKEY_INTERNAL_PRIVATE_MMC_ID "db/private/sysman/mmc_device_id"
49 #define MMC_PARENT_PATH "/opt/storage"
50 #define MMC_DEV "/dev/mmcblk"
52 #define SMACKFS_MAGIC 0x43415d53
53 #define SMACKFS_MNT "/smack"
56 #define ST_RDONLY 0x0001
59 #define MMC_32GB_SIZE 61315072
60 #define FORMAT_RETRY 3
61 #define UNMOUNT_RETRY 5
63 #define ODE_MOUNT_STATE 1
65 #define COMM_PKG_MGR_DBUS_SERVICE "com.samsung.slp.pkgmgr"
66 #define COMM_PKG_MGR_DBUS_PATH "/com/samsung/slp/pkgmgr"
67 #define COMM_PKG_MGR_DBUS_INTERFACE COMM_PKG_MGR_DBUS_SERVICE
68 #define COMM_PKG_MGR_METHOD "CreateExternalDirectory"
70 #define SMACK_LABELING_TIME (0.5)
72 enum unmount_operation {
84 static void *mount_start(void *arg);
85 static void *unmount_start(void *arg);
86 static void *format_start(void *arg);
88 static const struct mmc_thread_func {
89 enum mmc_operation type;
90 void *(*func) (void*);
91 } mmc_func[MMC_END] = {
92 [MMC_MOUNT] = {.type = MMC_MOUNT, .func = mount_start},
93 [MMC_UNMOUNT] = {.type = MMC_UNMOUNT, .func = unmount_start},
94 [MMC_FORMAT] = {.type = MMC_FORMAT, .func = format_start},
108 static dd_list *fs_head;
109 static char *mmc_curpath;
110 static bool smack = false;
111 static bool mmc_disabled = false;
112 static Ecore_Timer *smack_timer = NULL;
114 int __WEAK__ app2ext_unmount(void);
116 static void __CONSTRUCTOR__ smack_check(void)
122 ret = statfs(SMACKFS_MNT, &sfs);
123 } while (ret < 0 && errno == EINTR);
125 if (ret == 0 && sfs.f_type == SMACKFS_MAGIC)
127 _I("smackfs check %d", smack);
130 void add_fs(const struct mmc_fs_ops *fs)
132 DD_LIST_APPEND(fs_head, (void*)fs);
135 void remove_fs(const struct mmc_fs_ops *fs)
137 DD_LIST_REMOVE(fs_head, (void*)fs);
140 const struct mmc_fs_ops *find_fs(enum mmc_fs_type type)
142 struct mmc_fs_ops *fs;
145 DD_LIST_FOREACH(fs_head, elem, fs) {
146 if (fs->type == type)
152 bool mmc_check_mounted(const char *mount_point)
154 struct stat parent_stat, mount_stat;
155 char parent_path[PATH_MAX];
157 snprintf(parent_path, sizeof(parent_path), "%s", MMC_PARENT_PATH);
159 if (stat(mount_point, &mount_stat) != 0 || stat(parent_path, &parent_stat) != 0)
162 if (mount_stat.st_dev == parent_stat.st_dev)
168 static void launch_syspopup(char *str)
170 struct popup_data *params;
171 static const struct device_ops *apps = NULL;
173 FIND_DEVICE_VOID(apps, "apps");
175 params = malloc(sizeof(struct popup_data));
176 if (params == NULL) {
180 params->name = MMC_POPUP_NAME;
181 params->key = POPUP_KEY_CONTENT;
182 params->value = strdup(str);
183 apps->init((void *)params);
187 static int get_partition(const char *devpath, char *subpath)
192 for (i = 1; i < 5; ++i) {
193 snprintf(path, sizeof(path), "%sp%d", devpath, i);
194 if (!access(path, R_OK)) {
195 strncpy(subpath, path, strlen(path));
202 static int create_partition(const char *devpath)
207 snprintf(data, sizeof(data), "\"n\\n\\n\\n\\n\\nw\" | fdisk %s", devpath);
209 r = launch_evenif_exist("/usr/bin/printf", data);
210 if (WIFSIGNALED(r) && (WTERMSIG(r) == SIGINT || WTERMSIG(r) == SIGQUIT || WEXITSTATUS(r)))
216 static int mmc_check_and_unmount(const char *path)
218 int ret = 0, retry = 0;
219 while (mount_check(path)) {
223 if (retry > UNMOUNT_RETRY)
230 int get_block_number(void)
239 char *pre_mmc_device_id = NULL;
240 int mmc_dev_changed = 0;
242 if ((dp = opendir("/sys/block")) == NULL) {
243 _E("Can not open directory..");
247 r = chdir("/sys/block");
249 _E("Fail to change the directory..");
254 while ((dir = readdir(dp)) != NULL) {
255 memset(&stat, 0, sizeof(struct stat));
256 if(lstat(dir->d_name, &stat) < 0) {continue;}
257 if (S_ISDIR(stat.st_mode) || S_ISLNK(stat.st_mode)) {
258 if (strncmp(".", dir->d_name, 1) == 0
259 || strncmp("..", dir->d_name, 2) == 0)
261 if (strncmp("mmcblk", dir->d_name, 6) == 0) {
262 snprintf(buf, 255, "/sys/block/%s/device/type",
265 fd = open(buf, O_RDONLY);
269 r = read(fd, buf, 10);
270 if ((r >= 0) && (r < 10))
273 _E("%s read error: %s", buf,
276 if (strncmp("SD", buf, 2) == 0) {
277 char *str_mmcblk_num = strndup((dir->d_name) + 6, 1);
278 if (str_mmcblk_num == NULL) {
279 _E("Memory Allocation Failed");
284 atoi(str_mmcblk_num);
286 free(str_mmcblk_num);
288 _I("%d", mmcblk_num);
290 snprintf(buf, 255, "/sys/block/%s/device/cid", dir->d_name);
292 fd = open(buf, O_RDONLY);
294 _E("%s open error", buf, strerror(errno));
297 r = read(fd, buf, 255);
298 if ((r >=0) && (r < 255)) {
301 _E("%s read error: %s", buf,strerror(errno));
304 pre_mmc_device_id = vconf_get_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID);
305 if (pre_mmc_device_id) {
306 if (strcmp(pre_mmc_device_id, "") == 0) {
307 vconf_set_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID, buf);
308 } else if (strncmp(pre_mmc_device_id,buf,33) == 0) {
309 if ( vconf_get_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED,&mmc_dev_changed) == 0
310 && mmc_dev_changed != VCONFKEY_SYSMAN_MMC_NOT_CHANGED) {
311 vconf_set_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED, VCONFKEY_SYSMAN_MMC_NOT_CHANGED);
313 } else if (strncmp(pre_mmc_device_id,buf,32) != 0) {
314 vconf_set_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID, buf);
315 vconf_set_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED, VCONFKEY_SYSMAN_MMC_CHANGED);
317 free(pre_mmc_device_id);
319 _E("failed to get pre_mmc_device_id");
328 _E("failed to find mmc block number");
332 static int find_mmc_node(char devpath[])
336 num = get_block_number();
340 snprintf(devpath, NAME_MAX, "%s%d", MMC_DEV, num);
344 static int get_mmc_size(const char *devpath)
347 unsigned long long ullbytes;
350 fd = open(devpath, O_RDONLY);
356 r = ioctl(fd, BLKGETSIZE64, &ullbytes);
360 _E("ioctl BLKGETSIZE64 error");
364 nbytes = ullbytes/512;
365 _I("block size(64) : %d", nbytes);
369 static int rw_mount(const char *szPath)
371 struct statvfs mount_stat;
372 if (!statvfs(szPath, &mount_stat)) {
373 if ((mount_stat.f_flag & ST_RDONLY) == ST_RDONLY)
379 static void request_smack_broadcast(void)
383 ret = dbus_method_sync_timeout(COMM_PKG_MGR_DBUS_SERVICE,
384 COMM_PKG_MGR_DBUS_PATH,
385 COMM_PKG_MGR_DBUS_INTERFACE,
387 NULL, NULL, SMACK_LABELING_TIME*1000);
389 _E("Failed to call dbus method (err: %d)", ret);
392 static Eina_Bool smack_timer_cb(void *data)
395 ecore_timer_del(smack_timer);
398 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_MOUNTED);
399 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED);
403 void mmc_mount_done(void)
405 request_smack_broadcast();
406 smack_timer = ecore_timer_add(SMACK_LABELING_TIME,
407 smack_timer_cb, NULL);
412 _E("Fail to add abnormal check timer");
413 smack_timer_cb(NULL);
416 static int mmc_mount(const char *devpath, const char *mount_point)
418 struct mmc_fs_ops *fs;
420 char path[NAME_MAX] = {0,};
423 /* mmc_disabled set by start/stop func. */
430 /* check partition */
431 r = get_partition(devpath, path);
435 DD_LIST_FOREACH(fs_head, elem, fs) {
436 if (fs->match(devpath))
443 _I("devpath : %s", devpath);
444 r = fs->check(devpath);
446 _E("failt to check devpath : %s", devpath);
448 r = fs->mount(smack, devpath, mount_point);
452 r = rw_mount(mount_point);
459 static void *mount_start(void *arg)
461 struct mmc_data *data = (struct mmc_data*)arg;
465 devpath = data->devpath;
471 /* clear previous filesystem */
473 mmc_check_and_unmount(MMC_MOUNT_POINT);
475 /* check mount point */
476 if (access(MMC_MOUNT_POINT, R_OK) != 0) {
477 if (mkdir(MMC_MOUNT_POINT, 0755) < 0) {
483 /* mount operation */
484 r = mmc_mount(devpath, MMC_MOUNT_POINT);
486 launch_syspopup("mountrdonly");
487 /* Do not need to show error popup, if mmc is disabled */
488 else if (r == -EWOULDBLOCK)
489 goto error_without_popup;
493 mmc_set_config(MAX_RATIO);
494 r = ode_mmc_inserted();
501 /* give a transmutable attribute to mount_point */
502 r = setxattr(MMC_MOUNT_POINT, "security.SMACK64TRANSMUTE", "TRUE", strlen("TRUE"), 0);
504 _E("setxattr error : %s", strerror(errno));
506 request_smack_broadcast();
507 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_MOUNTED);
508 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED);
512 launch_syspopup("mounterr");
515 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, VCONFKEY_SYSMAN_MMC_MOUNT_FAILED);
516 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
520 _E("failed to mount device : %s", strerror(-r));
524 /* to ignore previous mount callbck func,
525 set a specific value(-1) to MMC_MOUNT key */
526 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, -1);
527 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, -1);
532 _I("you should mount with encryption");
536 static int mmc_unmount(int option, const char *mount_point)
541 /* try to unmount app2ext */
542 r = app2ext_unmount();
544 _I("Faild to unmount app2ext : %s", strerror(-r));
545 /* it must called before unmounting mmc */
547 r = mmc_check_and_unmount(mount_point);
550 if (option == UNMOUNT_NORMAL) {
551 _I("Failed to unmount with normal option : %s", strerror(-r));
555 _I("Execute force unmount!");
556 /* Force Unmount Scenario */
560 /* At first, notify to other app who already access sdcard */
561 _I("Notify to other app who already access sdcard");
562 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
565 /* Second, kill app with SIGTERM */
566 _I("Kill app with SIGTERM");
567 terminate_process(MMC_MOUNT_POINT, false);
570 /* Last time, kill app with SIGKILL */
571 _I("Kill app with SIGKILL");
572 terminate_process(MMC_MOUNT_POINT, true);
575 if (umount2(mount_point, MNT_DETACH) != 0) {
576 _I("Failed to unmount with lazy option : %s", strerror(errno));
582 /* it takes some seconds til other app completely clean up */
585 /* try to unmount app2ext */
586 r = app2ext_unmount();
588 _I("Faild to unmount app2ext : %s", strerror(-r));
591 r = mmc_check_and_unmount(mount_point);
599 static void *unmount_start(void *arg)
601 struct mmc_data *data = (struct mmc_data*)arg;
604 option = data->option;
606 assert(option == UNMOUNT_NORMAL || option == UNMOUNT_FORCE);
608 r = mmc_unmount(option, MMC_MOUNT_POINT);
613 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
618 _E("Failed to unmount device : %s", strerror(-r));
619 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_MOUNTED);
623 static int format(const char *devpath)
625 const struct mmc_fs_ops *fs = NULL;
627 char path[NAME_MAX] = {0,};
633 /* check partition */
634 r = get_partition(devpath, path);
636 /* if there is partition, find partition file system */
637 DD_LIST_FOREACH(fs_head, elem, fs) {
642 /* if there isn't partition, create partition */
643 create_partition(devpath);
644 r = get_partition(devpath, path);
646 memcpy(path, devpath, strlen(devpath));
649 _I("format partition : %s", path);
652 /* find root file system */
653 DD_LIST_FOREACH(fs_head, elem, fs) {
654 if (fs->match(devpath))
660 /* cannot find root and partition file system,
661 find suitable file system */
662 size = get_mmc_size(path);
663 if (size <= MMC_32GB_SIZE)
664 fs = find_fs(FS_TYPE_VFAT);
666 fs = find_fs(FS_TYPE_EXFAT);
672 for (retry = FORMAT_RETRY; retry > 0; --retry) {
674 _I("format path : %s", path);
675 r = fs->format(path);
682 static void *format_start(void *arg)
684 struct mmc_data *data = (struct mmc_data*)arg;
686 int option, r, key = VCONFKEY_SYSMAN_MMC_MOUNTED;
687 bool format_ret = true;
689 option = data->option;
690 devpath = data->devpath;
693 assert(option == UNMOUNT_NORMAL || option == UNMOUNT_FORCE);
695 _I("Format Start (option:%d)", option);
696 r = mmc_unmount(option, MMC_MOUNT_POINT);
700 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS, VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS_NOW);
702 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS, VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS_NONE);
709 _I("Format Successful");
710 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT, VCONFKEY_SYSMAN_MMC_FORMAT_COMPLETED);
717 _E("Format Failed : %s", strerror(-r));
718 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT, VCONFKEY_SYSMAN_MMC_FORMAT_FAILED);
722 static int mmc_make_thread(int type, int option, const char *devpath)
725 struct mmc_data *pdata;
728 if (type < 0 || type >= MMC_END)
731 pdata = malloc(sizeof(struct mmc_data));
738 pdata->option = option;
740 pdata->devpath = strdup(devpath);
741 r = pthread_create(&th, NULL, mmc_func[type].func, pdata);
743 _E("pthread create failed");
744 free(pdata->devpath);
753 static int mmc_inserted(const char *devpath)
756 _I("MMC inserted : %s", devpath);
757 mmc_curpath = strdup(devpath);
758 r = mmc_make_thread(MMC_MOUNT, -1, devpath);
764 static int mmc_removed(void)
767 /* first, try to unmount app2ext */
771 mmc_check_and_unmount((const char *)MMC_MOUNT_POINT);
772 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_REMOVED);
778 static int mmc_changed_cb(void *data)
780 char *devpath = (char*)data;
782 /* if MMC is inserted */
784 return mmc_inserted(devpath);
786 return mmc_removed();
789 static int mmc_booting_done(void* data)
791 char devpath[NAME_MAX] = {0,};
794 /* check mmc exists */
795 r = find_mmc_node(devpath);
800 return mmc_inserted(devpath);
803 static DBusMessage *edbus_request_secure_mount(E_DBus_Object *obj, DBusMessage *msg)
805 DBusMessageIter iter;
810 ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
822 /* check mount point */
823 if (access(path, R_OK) != 0) {
824 if (mkdir(path, 0755) < 0) {
830 _I("mount path : %s", path);
831 ret = mmc_mount(mmc_curpath, path);
834 reply = dbus_message_new_method_return(msg);
835 dbus_message_iter_init_append(reply, &iter);
836 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
840 static DBusMessage *edbus_request_secure_unmount(E_DBus_Object *obj, DBusMessage *msg)
842 DBusMessageIter iter;
847 ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
854 _I("unmount path : %s", path);
855 ret = mmc_unmount(UNMOUNT_NORMAL, path);
858 reply = dbus_message_new_method_return(msg);
859 dbus_message_iter_init_append(reply, &iter);
860 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
864 static DBusMessage *edbus_request_mount(E_DBus_Object *obj, DBusMessage *msg)
866 DBusMessageIter iter;
868 struct mmc_data *pdata;
876 pdata = malloc(sizeof(struct mmc_data));
883 pdata->devpath = strdup(mmc_curpath);
884 if (!pdata->devpath) {
890 ret = (int)mount_start(pdata);
893 reply = dbus_message_new_method_return(msg);
894 dbus_message_iter_init_append(reply, &iter);
895 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
899 static DBusMessage *edbus_request_unmount(E_DBus_Object *obj, DBusMessage *msg)
901 DBusMessageIter iter;
904 char params[NAME_MAX];
905 struct mmc_data *pdata;
907 ret = dbus_message_get_args(msg, NULL,
908 DBUS_TYPE_INT32, &opt, DBUS_TYPE_INVALID);
910 _I("there is no message");
915 pdata = malloc(sizeof(struct mmc_data));
923 ret = (int)unmount_start(pdata);
926 reply = dbus_message_new_method_return(msg);
927 dbus_message_iter_init_append(reply, &iter);
928 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
932 static DBusMessage *edbus_request_format(E_DBus_Object *obj, DBusMessage *msg)
934 DBusMessageIter iter;
937 struct mmc_data *pdata;
939 ret = dbus_message_get_args(msg, NULL,
940 DBUS_TYPE_INT32, &opt, DBUS_TYPE_INVALID);
942 _I("there is no message");
952 pdata = malloc(sizeof(struct mmc_data));
960 pdata->devpath = strdup(mmc_curpath);
961 if (!pdata->devpath) {
967 ret = (int)format_start(pdata);
970 reply = dbus_message_new_method_return(msg);
971 dbus_message_iter_init_append(reply, &iter);
972 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
976 static DBusMessage *edbus_request_insert(E_DBus_Object *obj, DBusMessage *msg)
978 DBusMessageIter iter;
983 ret = dbus_message_get_args(msg, NULL,
984 DBUS_TYPE_STRING, &devpath, DBUS_TYPE_INVALID);
986 _I("there is no message");
991 ret = mmc_inserted(devpath);
994 reply = dbus_message_new_method_return(msg);
995 dbus_message_iter_init_append(reply, &iter);
996 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
1000 static DBusMessage *edbus_request_remove(E_DBus_Object *obj, DBusMessage *msg)
1002 DBusMessageIter iter;
1006 ret = mmc_removed();
1008 reply = dbus_message_new_method_return(msg);
1009 dbus_message_iter_init_append(reply, &iter);
1010 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
1014 static DBusMessage *edbus_change_status(E_DBus_Object *obj, DBusMessage *msg)
1016 DBusMessageIter iter;
1019 char params[NAME_MAX];
1020 struct mmc_data *pdata;
1022 ret = dbus_message_get_args(msg, NULL,
1023 DBUS_TYPE_INT32, &opt, DBUS_TYPE_INVALID);
1025 _I("there is no message");
1029 if (opt == VCONFKEY_SYSMAN_MMC_MOUNTED)
1032 reply = dbus_message_new_method_return(msg);
1033 dbus_message_iter_init_append(reply, &iter);
1034 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
1038 int get_mmc_devpath(char devpath[])
1041 return -EWOULDBLOCK;
1044 snprintf(devpath, NAME_MAX, "%s", mmc_curpath);
1048 static const struct edbus_method edbus_methods[] = {
1049 { "RequestSecureMount", "s", "i", edbus_request_secure_mount },
1050 { "RequestSecureUnmount", "s", "i", edbus_request_secure_unmount },
1051 { "RequestMount", NULL, "i", edbus_request_mount },
1052 { "RequestUnmount", "i", "i", edbus_request_unmount },
1053 { "RequestFormat", "i", "i", edbus_request_format },
1054 { "RequestInsert", "s", "i", edbus_request_insert },
1055 { "RequestRemove", NULL, "i", edbus_request_remove },
1056 { "ChangeStatus", "i", "i", edbus_change_status },
1059 static int mmc_poweroff(void *data)
1065 static void mmc_init(void *data)
1070 ret = register_edbus_method(DEVICED_PATH_MMC, edbus_methods, ARRAY_SIZE(edbus_methods));
1072 _E("fail to init edbus method(%d)", ret);
1074 /* register mmc uevent control routine */
1075 ret = mmc_uevent_start();
1077 _E("fail to mmc uevent start");
1079 /* register notifier if mmc exist or not */
1080 register_notifier(DEVICE_NOTIFIER_POWEROFF, mmc_poweroff);
1081 register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, mmc_booting_done);
1082 register_notifier(DEVICE_NOTIFIER_MMC, mmc_changed_cb);
1085 static void mmc_exit(void *data)
1087 /* unregister notifier */
1088 unregister_notifier(DEVICE_NOTIFIER_POWEROFF, mmc_poweroff);
1089 unregister_notifier(DEVICE_NOTIFIER_MMC, mmc_changed_cb);
1090 unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, mmc_booting_done);
1092 /* unregister mmc uevent control routine */
1096 static int mmc_start(enum device_flags flags)
1098 mmc_disabled = false;
1103 static int mmc_stop(enum device_flags flags)
1105 mmc_disabled = true;
1106 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_REMOVED);
1111 const struct device_ops mmc_device_ops = {
1112 .priority = DEVICE_PRIORITY_NORMAL,
1120 DEVICE_OPS_REGISTER(&mmc_device_ops)